Aller au contenu

Fondamentaux du CI/CD

Anatomie d'un pipeline

Un pipeline CI/CD est une sequence d'étapes automatisees declenchee par un événement (push, tag, merge request). Chaque étape produit un résultat qui alimente la suivante.

graph LR
    Trigger --> Build --> Test --> Scan --> Package --> Deploy
    Deploy --> Production
    Package --> Registre["Registre (Harbor)"]
    Scan --> Rapport_sec["Rapport securite (Trivy)"]
    Test --> Rapport_test["Rapport tests + couverture"]
    Build --> Artefact["Artefact compile"]
    Trigger --> Evenement["Push / Tag / Merge<br/>Cron / Webhook"]

Étapes détaillées

Étape Objectif Outils typiques Artefact produit
Trigger Déclencher le pipeline Webhook Git, cron, dispatch manuel Événement
Build Compiler le code, construire l'image go build, npm, Containerfile Binaire, image OCI
Test Valider le comportement (unit, integration) pytest, go test, Jest Rapport de tests
Scan Détecter vulnerabilites et defauts de qualité Trivy, SonarQube, hadolint Rapport de sécurité
Package Pousser l'artefact dans un registre podman push, helm package Image taguee dans Harbor
Deploy Déployer sur l'environnement cible Flux v2 sync, kubectl apply Application en production

Pipeline-as-Code

Le pipeline est defini dans un fichier YAML versionne dans le dépôt. Chaque modification du pipeline suit le même workflow que le code : review, merge, historique.

Fichier de workflow Gitea Actions

# .gitea/workflows/ci.yaml
name: CI Pipeline
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: make build
      - name: Test
        run: make test
      - name: Scan
        run: trivy fs --exit-code 1 .

Avantages du pipeline-as-code

Avantage Description
Versionnement Le pipeline évolue avec le code, chaque changement est trace
Reproductibilite Le même commit produit toujours le même pipeline
Review Les modifications du pipeline passent par le même processus de review
Self-service Les développeurs modifient leur pipeline sans dépendre d'un admin CI
Disaster recovery Le pipeline est restaure avec le dépôt — pas de configuration orpheline

Runners, agents et workers

Le runner est le composant qui execute les étapes du pipeline. Il reçoit les jobs du serveur CI, execute les commandes dans un environnement isole et renvoie les résultats.

Architecture runner

graph LR
    CI["Serveur CI<br/>(Gitea)<br/><br/>File de jobs<br/>Resultats<br/>Artefacts"] <-->|gRPC| R1["Runner #1<br/>(act runner)<br/>Conteneur ephemere"]
    CI <-->|gRPC| R2["Runner #2<br/>(act runner)<br/>VM persistante"]

Ephemere vs persistant

Caractéristique Runner ephemere Runner persistant
Cycle de vie Cree pour un job, detruit apres Demarre une fois, traite plusieurs jobs
Isolation Forte (chaque job = environnement neuf) Faible (risque de pollution entre jobs)
Performance Temps de demarrage à chaque job Pas de demarrage, cache reutilise
Sécurité Pas de residus de secrets entre jobs Risque de fuite de secrets entre jobs
Cas d'usage Production, pipelines sensibles Dev, builds lourds avec cache

Recommandation

Privilegier les runners ephemeres en production. Le coût de demarrage (~10s pour un conteneur) est negligeable face au gain en isolation et en sécurité.


Passage d'artefacts entre étapes

Les étapes d'un pipeline doivent partager des artefacts (binaires compiles, rapports de tests, images). Plusieurs mécanismes existent :

Mécanisme Description Exemple
Cache Répertoires persistants entre runs (dependencies) ~/.cache/go-build, node_modules
Artifacts Fichiers uploades par un job, telecharges par un autre Binaire compile, rapport HTML
Registre OCI Image poussee dans Harbor, tiree par le déploiement harbor.example.com/app:v1.2.3
Volume partage Volume monte dans tous les conteneurs du même job Workspace du job
# Exemple : partage d'artefacts entre jobs
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: make build
      - uses: actions/upload-artifact@v4
        with:
          name: binary
          path: ./bin/app

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: binary
      - run: ./deploy.sh

Injection de secrets

Les secrets (tokens, credentials, cles) ne doivent jamais apparaître dans le code du pipeline. Ils sont injectes a l'execution.

Méthodes d'injection

Méthode Source Sécurité Exemple
Variables CI UI du serveur CI Moyenne ${{ secrets.REGISTRY_PASSWORD }}
Vault injection HashiCorp Vault Forte vault kv get secret/ci/registry
Fichier monte Secret Kubernetes Forte /run/secrets/registry_password
OIDC token exchange IdP (Keycloak) Forte Token ephemere sans secret statique

Regles absolues

  • Ne jamais logger un secret (désactiver set -x autour des commandes sensibles)
  • Ne jamais passer un secret en argument de commande (visible dans /proc)
  • Utiliser des tokens ephemeres plutôt que des credentials statiques
  • Rotationner les secrets régulièrement (90 jours maximum)

Stratégies de déploiement

Le déploiement est l'étape la plus critique. Plusieurs stratégies permettent de limiter l'impact d'une régression.

Comparaison des stratégies

Stratégie Principe Risque Rollback Coût infra
Rolling Remplacement progressif des instances Moyen Redeploy ancien x1
Blue/Green Deux environnements, bascule instantanee Faible Rebascule x2
Canary Petit pourcentage du trafic vers la nouvelle version Tres faible Retrait du canary x1 + epsilon
Recreate Arrêt total, redeploy Eleve (downtime) Redeploy ancien x1

Rolling update

graph TD
    T0["Temps 0 : v1 v1 v1 v1"]
    T1["Temps 1 : <b>v2</b> v1 v1 v1 — une instance remplacee"]
    T2["Temps 2 : <b>v2 v2</b> v1 v1"]
    T3["Temps 3 : <b>v2 v2 v2</b> v1"]
    T4["Temps 4 : <b>v2 v2 v2 v2</b> — deploiement termine"]
    T0 --> T1 --> T2 --> T3 --> T4

Blue/Green

graph LR
    subgraph Avant bascule
        T1["Traffic"] -->|100%| Blue1["Blue (v1) — actif"]
        Green1["Green (v2) — pret, pas de trafic"]
    end
    subgraph Apres bascule
        T2["Traffic"] -->|100%| Green2["Green (v2) — actif"]
        Blue2["Blue (v1) — standby"]
    end

Canary

graph LR
    Traffic["Traffic"]
    Traffic -->|95%| Stable["Stable v1"]
    Traffic -->|5%| Canary["Canary v2<br/>observation des metriques"]

Modèle GitOps

GitOps est un paradigme de déploiement ou l'état desire de l'infrastructure est declare dans un dépôt Git. Un opérateur (Flux v2) reconcilie en permanence l'état reel du cluster avec l'état declare.

Principes fondamentaux

Principe Description
Declaratif L'état desire est décrit dans des manifestes (YAML, Helm, Kustomize)
Versionne Git est la source de vérité unique
Automatise Les changements sont appliques automatiquement par l'opérateur
Observable L'opérateur détecte et signale les derivations (drift)

Boucle de reconciliation

graph LR
    Git["Depot Git
    etat desire

    manifests/
    app/deployment.yaml"] --> FluxV2["Flux v2
    operateur

    Compare desired
    vs actual
    Sync si diff"]
    FluxV2 --> K8s["Cluster K8s
    etat reel

    Pods, Services,
    ConfigMaps..."]
    FluxV2 --> Drift{"Drift detecte ?"}
    Drift --> Sync["Sync automatique
    ou alerte"]

Pull vs Push deployment

Modèle Sens du flux Sécurité Exemple
Push Le CI pousse vers le cluster Le CI a des credentials du cluster kubectl apply
Pull L'opérateur tire depuis Git Le cluster tire, pas de credentials CI Flux v2 sync

Recommandation GitOps

Le modèle pull est plus securise : le cluster n'expose pas d'API au CI, et les credentials du cluster ne sortent jamais du cluster. C'est le modèle natif de Flux v2.


Recapitulatif

Concept Point cle
Pipeline-as-code Le pipeline vit dans le dépôt, versionne et reviewable
Runners ephemeres Un conteneur par job pour l'isolation maximale
Artefacts Upload/download entre jobs, images OCI pour le déploiement
Secrets Injection depuis Vault, jamais en clair dans le pipeline
Déploiement Rolling pour la simplicité, Blue/Green ou Canary pour la sécurité
GitOps Git = source de vérité, Flux v2 = reconciliation automatique