Pipelines CI/CD avancés
Pipelines CI/CD avancés
Principes d'un pipeline mature
Un pipeline CI/CD avancé ne se limite pas à builder et déployer. Il intègre la qualité, la sécurité, les tests et le déploiement progressif.
graph LR
A[Commit] --> B[Lint & Format]
B --> C[Tests unitaires]
C --> D[Build & Scan sécurité]
D --> E[Tests d'intégration]
E --> F[Deploy Staging]
F --> G[Tests E2E]
G --> H[Deploy Canary 5%]
H --> I{Métriques OK ?}
I -->|Oui| J[Rollout 100%]
I -->|Non| K[Rollback automatique]
GitHub Actions : pipelines complets
Pipeline de déploiement multi-environnement
name: Deploy Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
lint-and-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 lint
- run: npm run test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
security-scan:
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Audit des dépendances
run: npm audit --audit-level=high
- name: Scan SAST avec Semgrep
uses: semgrep/semgrep-action@v1
with:
config: p/typescript
- name: Scan des secrets
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
build-and-push:
runs-on: ubuntu-latest
needs: [lint-and-test, security-scan]
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- name: Login au registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Metadata Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=ref,event=branch
- name: Build et push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan de vulnérabilités de l'image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
exit-code: 1
severity: CRITICAL,HIGH
deploy-staging:
runs-on: ubuntu-latest
needs: build-and-push
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- uses: actions/checkout@v4
- name: Deploy sur staging
run: |
kubectl set image deployment/api \
api=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n staging
kubectl rollout status deployment/api -n staging --timeout=300s
deploy-production:
runs-on: ubuntu-latest
needs: build-and-push
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Canary deployment (5%)
run: |
kubectl apply -f k8s/canary.yaml
kubectl set image deployment/api-canary \
api=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n production
- name: Vérification des métriques (5 min)
run: |
sleep 300
ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[5m])" | jq '.data.result[0].value[1]')
if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
echo "Taux d'erreur trop élevé : $ERROR_RATE"
kubectl rollout undo deployment/api-canary -n production
exit 1
fi
- name: Rollout complet
run: |
kubectl set image deployment/api \
api=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n production
kubectl rollout status deployment/api -n production --timeout=600s
kubectl delete -f k8s/canary.yaml
GitLab CI/CD : pipeline avancé
stages:
- validate
- test
- build
- deploy
- verify
variables:
DOCKER_TLS_CERTDIR: "/certs"
.node-cache: &node-cache
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
lint:
stage: validate
<<: *node-cache
script:
- npm ci
- npm run lint
- npm run type-check
test:
stage: test
<<: *node-cache
services:
- postgres:16-alpine
variables:
POSTGRES_DB: test
POSTGRES_PASSWORD: test
DATABASE_URL: postgres://postgres:test@postgres:5432/test
script:
- npm ci
- npm run test:ci
- npm run test:e2e
coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/'
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
deploy_production:
stage: deploy
environment:
name: production
url: https://api.example.com
when: manual
only:
- main
script:
- helm upgrade --install api ./charts/api
--set image.tag=$CI_COMMIT_SHA
--namespace production
--wait --timeout 10m
smoke_test:
stage: verify
needs: [deploy_production]
script:
- curl -f https://api.example.com/health || exit 1
- npm run test:smoke
Stratégies de déploiement
Blue/Green
Deux environnements identiques : blue (actuel) et green (nouveau). On bascule le trafic d'un coup.
# Service qui pointe vers l'environnement actif
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
version: green # Basculer entre blue et green
ports:
- port: 3000
Canary
Déploiement progressif : 5% → 25% → 50% → 100%.
Feature Flags
Découpler le déploiement de l'activation des fonctionnalités :
// Le code est déployé mais la feature est désactivée
if (await featureFlags.isEnabled('new-checkout', { userId })) {
return newCheckoutFlow(order);
}
return legacyCheckoutFlow(order);
Bonnes pratiques CI/CD
- Fail fast : les étapes les plus rapides en premier (lint, types)
- Paralléliser les jobs indépendants
- Cache les dépendances entre les runs
- Artefacts immuables : builder une fois, déployer partout
- Environnements éphémères pour les PR (preview deployments)
- Rollback automatique si les métriques dégradent
- Secrets gérés par le CI, jamais dans le code