Aller au contenu
Culture DevOps high
🚧 Section en cours de réécriture — Le contenu est en cours de restructuration et peut évoluer.

CI/CD : automatiser le chemin du code à la production

21 min de lecture

Netflix déploie des milliers de fois par jour. Amazon pousse du code en production toutes les 11,7 secondes. Pendant ce temps, certaines entreprises attendent des semaines, voire des mois, pour mettre à jour leurs applications. La différence ? L’automatisation CI/CD.

L’intégration continue et le déploiement continu ne sont pas des gadgets pour les géants de la tech — ce sont des pratiques fondamentales qui permettent à n’importe quelle équipe de livrer plus vite, avec moins de bugs et moins de stress.

Ce guide vous explique ces concepts de A à Z, même si vous n’avez jamais configuré de pipeline.

Qu’est-ce que CI/CD ? L’analogie de la chaîne de montage

Section intitulée « Qu’est-ce que CI/CD ? L’analogie de la chaîne de montage »

Imaginez une usine automobile. Avant l’industrialisation, chaque voiture était assemblée à la main, du début à la fin, par des artisans. Le processus prenait des semaines et chaque véhicule était différent.

Puis est arrivée la chaîne de montage : chaque étape est standardisée, automatisée, et vérifie la qualité avant de passer à la suivante. Résultat : plus de voitures, de meilleure qualité, en moins de temps.

CI/CD, c’est la chaîne de montage du logiciel.

  • CI (Continuous Integration) : Chaque fois qu’un développeur pousse du code, il est automatiquement compilé, testé et vérifié. Comme un contrôle qualité après chaque étape de la chaîne.

  • CD (Continuous Delivery/Deployment) : Le code validé est automatiquement préparé (ou déployé) pour la production. Comme une voiture qui sort de la chaîne prête à être livrée.

Vue d'ensemble d'un pipeline CI/CD avec les étapes de build, test, sécurité et déploiement

Le terme “CD” est souvent source de confusion car il désigne deux choses différentes. Clarifions une fois pour toutes.

Comparaison entre Continuous Integration, Continuous Delivery et Continuous Deployment

Continuous Integration (CI) — Intégration Continue

Section intitulée « Continuous Integration (CI) — Intégration Continue »

Ce que c’est : La pratique de fusionner fréquemment le code de tous les développeurs dans une branche principale, avec vérification automatique.

Analogie : Imaginez une équipe qui écrit un livre ensemble. Au lieu que chacun écrive son chapitre dans son coin pendant des mois (et découvre ensuite que les chapitres ne s’accordent pas), tout le monde ajoute régulièrement ses paragraphes au manuscrit commun. Un correcteur automatique vérifie à chaque ajout que le texte reste cohérent.

Ce qui se passe concrètement :

  1. Un développeur pousse son code sur Git
  2. Un serveur CI (Jenkins, GitLab CI, GitHub Actions…) détecte le changement
  3. Le code est compilé automatiquement
  4. Les tests unitaires et d’intégration s’exécutent
  5. Des analyses de qualité et sécurité sont lancées
  6. Si tout passe : le code est validé ✅
  7. Si quelque chose échoue : l’équipe est alertée ❌

Ce que c’est : L’extension de la CI où le code est automatiquement préparé pour être déployé en production, mais un humain appuie sur le bouton pour le déploiement final.

Analogie : La voiture sort de la chaîne de montage, passe tous les contrôles qualité, et attend dans le parking. Le concessionnaire décide quand la livrer au client.

Quand l’utiliser :

  • Applications critiques nécessitant une validation métier
  • Secteurs réglementés (finance, santé)
  • Équipes en transition vers le DevOps
  • Changements impactant les utilisateurs (UI, fonctionnalités majeures)

Ce que c’est : La version ultime où tout est automatique : dès que le code passe les tests, il est déployé en production sans intervention humaine.

Analogie : La voiture sort de la chaîne de montage et est immédiatement livrée au client, sans passer par le parking.

Quand l’utiliser :

  • Applications avec excellente couverture de tests
  • Équipes matures avec forte culture qualité
  • Systèmes avec rollback automatique
  • Microservices indépendants

Un pipeline est une séquence d’étapes automatisées qui transforme le code source en application déployée. Voyons chaque étape en détail.

Le pipeline démarre quand un événement se produit :

ÉvénementQuand l’utiliser
Push sur une brancheTests à chaque modification
Pull Request / Merge RequestValidation avant fusion
Tag / ReleaseDéploiement d’une version
Cron (planifié)Tests nocturnes, scans de sécurité
ManuelDéploiements contrôlés
# Exemple GitHub Actions
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Tous les jours à 2h

Le code source est transformé en artefact exécutable :

  • Application compilée (Java, Go, Rust…)
  • Image Docker (conteneurs)
  • Bundle optimisé (JavaScript, TypeScript)
  • Package (pip, npm, nuget…)
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/

Trois niveaux de tests, du plus rapide au plus lent :

  1. Tests unitaires : Vérifient les fonctions isolément (millisecondes)

  2. Tests d’intégration : Vérifient les interactions entre composants (secondes)

  3. Tests End-to-End : Vérifient les parcours utilisateurs complets (minutes)

test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Run integration tests
run: npm run test:integration
- name: Upload coverage
uses: codecov/codecov-action@v4

Le code est scanné pour détecter :

Type d’analyseCe qu’il détecteOutils
LintingStyle, conventionsESLint, Ruff, Pylint
SASTVulnérabilités dans le codeSonarQube, Semgrep, CodeQL
SecretsCredentials exposésGitLeaks, TruffleHog
DépendancesCVE dans les packagesDependabot, Snyk, Trivy
SBOMInventaire des composantsSyft, CycloneDX
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Check for secrets
uses: gitleaks/gitleaks-action@v2

L’artefact validé est publié dans un registre :

  • Docker Registry (Docker Hub, GitHub Container Registry, Harbor)
  • Package Registry (npm, PyPI, Maven Central)
  • Artifact Storage (S3, GCS, Artifactory)
publish:
needs: [test, security]
runs-on: ubuntu-latest
steps:
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

L’artefact est déployé sur l’environnement cible :

Déploiement automatique après chaque merge sur main :

deploy-staging:
needs: publish
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
kubectl set image deployment/myapp \
myapp=ghcr.io/${{ github.repository }}:${{ github.sha }}

Comment mettre à jour une application en production sans interrompre le service ? Plusieurs stratégies existent, chacune avec ses avantages.

Comparaison des stratégies de déploiement : Rolling Update, Blue/Green, Canary et Feature Flags

Principe : Remplacer les instances une par une.

Comment ça marche :

  1. Vous avez 4 serveurs avec la version 1.0
  2. Le déploiement arrête le serveur 1, le met à jour en 2.0, le redémarre
  3. Puis le serveur 2, puis 3, puis 4
  4. À chaque instant, au moins 3 serveurs sont disponibles

Avantages :

  • Zero downtime
  • Utilise l’infrastructure existante
  • Rollback possible (mais lent)

Inconvénients :

  • Période avec versions mixtes (1.0 et 2.0 en parallèle)
  • Problème si l’ancienne et la nouvelle version sont incompatibles (API, base de données)
# Kubernetes Rolling Update
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 1 pod en plus pendant la mise à jour
maxUnavailable: 0 # Aucun pod indisponible

Principe : Maintenir deux environnements identiques et basculer le trafic.

Comment ça marche :

  1. Blue = production actuelle (version 1.0)
  2. Green = nouvelle version (2.0) déployée en parallèle
  3. Tests sur Green sans impact utilisateur
  4. Changement DNS : le trafic bascule de Blue vers Green
  5. Blue devient l’environnement de backup

Avantages :

  • Rollback instantané (rebascule vers Blue)
  • Test en conditions réelles avant mise en prod
  • Pas de versions mixtes

Inconvénients :

  • Coût infrastructure x2
  • Complexité pour les migrations de base de données
# Exemple avec labels Kubernetes
# 1. Déployer la nouvelle version avec label version: green
# 2. Mettre à jour le Service pour pointer vers green
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: green # Bascule ici

Principe : Déployer la nouvelle version pour un petit pourcentage d’utilisateurs, puis augmenter progressivement.

Pourquoi “Canary” ? Le nom vient des canaris utilisés dans les mines de charbon. Si le canari mourait, les mineurs savaient que l’air était toxique. Ici, si le “canary” (petit groupe d’utilisateurs) rencontre des problèmes, on stoppe le déploiement.

Comment ça marche :

  1. 5% du trafic → nouvelle version
  2. Monitoring des métriques (erreurs, latence, satisfaction)
  3. Si tout va bien : 25% → 50% → 100%
  4. Si problème : rollback immédiat (seuls 5% impactés)

Avantages :

  • Risque limité (seul un petit % d’utilisateurs impacté)
  • Feedback réel en production
  • Détection précoce des problèmes

Inconvénients :

  • Nécessite un monitoring sophistiqué
  • Plus complexe à configurer
# Istio Canary avec VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
http:
- route:
- destination:
host: myapp
subset: stable
weight: 95
- destination:
host: myapp
subset: canary
weight: 5

Principe : Déployer le code en production mais garder les fonctionnalités désactivées, puis les activer progressivement.

Comment ça marche :

  1. Le code de la nouvelle fonctionnalité est déployé mais “caché”
  2. Un système de configuration (LaunchDarkly, Unleash, maison) contrôle son activation
  3. Activation progressive : d’abord les employés, puis 10% des utilisateurs, puis tous

Avantages :

  • Découplage déploiement / activation
  • A/B testing facile
  • Rollback instantané (désactiver le flag)
  • Pas besoin de re-déployer

Inconvénients :

  • Code plus complexe (conditions if/else)
  • Risque de dette technique (flags oubliés)
  • Nécessite une gestion rigoureuse
# Exemple avec feature flag
from feature_flags import is_enabled
def get_recommendations(user):
if is_enabled("new_recommendation_algorithm", user):
return new_algorithm(user) # Nouvelle version
else:
return legacy_algorithm(user) # Version actuelle

Voici un pipeline GitHub Actions complet intégrant toutes les bonnes pratiques :

name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ===== CONTINUOUS INTEGRATION =====
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run test -- --coverage
- uses: codecov/codecov-action@v4
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
- name: Check secrets
uses: gitleaks/gitleaks-action@v2
build:
needs: [lint, test, security]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ===== CONTINUOUS DELIVERY =====
deploy-staging:
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
echo "Déploiement sur staging..."
# kubectl, ArgoCD, ou autre outil de déploiement
integration-tests:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run E2E tests on staging
run: npm run test:e2e
env:
BASE_URL: https://staging.example.com
deploy-production:
needs: integration-tests
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- name: Deploy to production
run: |
echo "Déploiement en production..."
# Déploiement avec stratégie Blue/Green ou Canary
  1. Un commit = un build : Chaque push déclenche le pipeline. Pas d’exceptions.

  2. Build rapide : Visez moins de 10 minutes. Au-delà, les développeurs n’attendent pas le feedback.

  3. Fail fast : Les étapes les plus rapides en premier (lint avant tests, tests unitaires avant E2E).

  4. Cache agressif : Dépendances, images Docker, résultats de build.

  5. Feedback visible : Status checks sur les PR, notifications Slack/Teams.

  1. Infrastructure as Code : Toute configuration d’environnement est versionnée.

  2. Environnements identiques : Staging doit être le plus proche possible de la production.

  3. Rollback automatisable : Pouvoir revenir en arrière en moins de 5 minutes.

  4. Feature flags : Découpler déploiement et activation des fonctionnalités.

  5. Monitoring proactif : Alertes sur les métriques clés (erreurs, latence, saturation).

MétriqueObjectifPourquoi
Temps de build< 10 minFeedback rapide
Fréquence de déploiementQuotidienne minimumPetits changements = moins de risque
Lead time< 1 jourDu commit à la prod
Change Failure Rate< 15%Qualité des déploiements
MTTR< 1 heureTemps de récupération après incident
SymptômeCause probableSolution
Build aléatoirement casséTests flakyIdentifier et corriger les tests non déterministes
Pipeline lentPas de cacheConfigurer le cache pour dépendances et builds
Échec au déploiementSecrets manquantsVérifier les variables d’environnement
Tests passent localement, échouent en CIEnvironnement différentUtiliser des conteneurs pour reproduire l’environnement CI
Déploiement bloquéApprobation en attenteVérifier les reviewers et les environnements protégés
  1. CI = intégrer fréquemment : Chaque commit est automatiquement buildé, testé et validé.

  2. CD = déployer continuellement : Delivery (bouton manuel) ou Deployment (100% auto).

  3. Le pipeline est votre gardien : S’il passe, le code est déployable. S’il échoue, on corrige avant tout.

  4. Commencez simple : Rolling Update d’abord, puis évoluez vers Blue/Green ou Canary selon vos besoins.

  5. Mesurez pour améliorer : Les métriques DORA guident vos efforts d’optimisation.

  6. L’automatisation libère : Moins de tâches manuelles = moins d’erreurs humaines = plus de temps pour créer de la valeur.