Réduire le lead time de déploiement avec le GitOps
Des déploiements lents ne sont presque jamais un problème d'outillage. C'est un problème de confiance : les équipes ajoutent des validations manuelles parce qu'elles ne font pas confiance au pipeline pour détecter ce qu'un relecteur humain détecterait.
Le GitOps répond à ce problème en faisant du pipeline la piste d'audit. Chaque changement en production est un commit dans un dépôt Git, rattaché à une pull request relue, et le cluster est réconcilié automatiquement sur ce commit.
Où part le lead time
Le lead time, c'est le temps entre le merge d'un changement et sa mise en production. Avec un comité de changement hebdomadaire, le code est généralement prêt en quelques minutes, et l'essentiel du lead time se passe à attendre, d'abord le prochain créneau du comité, puis quelqu'un pour lancer le déploiement pendant la fenêtre de mise en production. Un changement mergé le lendemain de la réunion peut facilement attendre presque une semaine. Regrouper les changements rend aussi chaque release plus grosse et plus risquée, et les grosses releases finissent souvent par avoir des validations plus strictes, ce qui pousse les équipes à regrouper encore davantage.
Le pipeline ci-dessous supprime cette attente. Chaque merge part en production seul, et la question à laquelle répondait un approbateur humain (« est-ce que ça a l'air sain en production ? ») devient une vérification de métriques exécutée à chaque release.
Le pattern
- Source de vérité : les manifestes applicatifs sont rangés dans un dépôt Git de configuration dédié, et non dans les scripts CI.
- Réconciliation : Argo CD surveille le dépôt et aligne le cluster sur son contenu, donc personne ne lance de
kubectl applydepuis son laptop. Flux fonctionne de la même manière ; j'utilise Argo CD ici. - Livraison progressive : les paliers du canary et leurs critères de réussite sont écrits sous forme de configuration dans le dépôt.
- Rollback : le rollout s'interrompt automatiquement, et un
git revertremet Git en phase avec ce qui tourne.
Étape 1 : la CI build l'image et met à jour le tag
Le workflow de release du dépôt applicatif s'exécute à chaque merge sur main. La protection de branche a déjà exigé que le job de tests passe sur la PR, donc ce workflow se contente de builder et pousser une image taguée avec le SHA du commit, puis d'écrire ce tag dans le dépôt de configuration. La revue a eu lieu sur la PR applicative, et le commit de mise à jour y renvoie via le SHA, si bien que chaque déploiement reste rattaché à une revue.
# dépôt applicatif : .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
permissions:
contents: read
packages: write
concurrency:
group: release-payments-api
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ghcr.io/acme/payments-api:${{ github.sha }}
cache-from: type=registry,ref=ghcr.io/acme/payments-api:buildcache
cache-to: type=registry,ref=ghcr.io/acme/payments-api:buildcache,mode=max
- name: Check out config repo
uses: actions/checkout@v7
with:
repository: acme/gitops-config
ssh-key: ${{ secrets.CONFIG_REPO_DEPLOY_KEY }}
path: gitops-config
- name: Bump image tag
working-directory: gitops-config/apps/payments-api/overlays/production
run: |
kustomize edit set image ghcr.io/acme/payments-api=ghcr.io/acme/payments-api:${{ github.sha }}
git config user.name "release-bot"
git config user.email "release-bot@users.noreply.github.com"
if git diff --quiet; then echo "Tag already deployed"; exit 0; fi
git commit -am "payments-api: deploy ${{ github.sha }}"
git pull --rebase
git pushLa deploy key n'a d'accès en écriture que sur le dépôt de configuration. Rien dans ce workflow ne peut non plus atteindre le cluster. La CI ne détient jamais d'identifiants du cluster, puisque c'est Argo CD qui récupère les changements depuis Git. concurrency empêche deux merges de ce service de se marcher dessus, et git pull --rebase gère le cas où le bot d'un autre service a poussé entre-temps sur le même dépôt de configuration. Kustomize est préinstallé sur les runners Ubuntu de GitHub. Si votre contrôle des changements exige une seconde approbation pour la production, ouvrez une PR sur le dépôt de configuration au lieu de pousser directement. Le pipeline reste le même, avec une étape de revue en plus. Cette étape coûte du lead time, donc je la réserve aux services qui en ont besoin.
Étape 2 : le dépôt de configuration et l'Application Argo CD
Le dépôt de configuration contient une base et un overlay de production. Après une mise à jour, le kustomization.yaml de l'overlay ressemble à ceci :
# gitops-config : apps/payments-api/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: payments
resources:
- ../../base
images:
- name: ghcr.io/acme/payments-api
newTag: 3f9c2e1a7b4d5c6e8f0a1b2c3d4e5f6a7b8c9d0eEt une Application Argo CD pointe sur cet overlay avec une synchronisation automatique :
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payments-api
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/gitops-config.git
targetRevision: main
path: apps/payments-api/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: payments
syncPolicy:
automated:
prune: true
selfHeal: truePar défaut, Argo CD interroge Git toutes les quelques minutes. Avec un webhook depuis le dépôt de configuration, la synchronisation démarre quelques secondes après le commit de mise à jour. selfHeal annule les modifications faites à la main dans le cluster, ce qui le garde aligné sur Git.
Étape 3 : un canary validé par Prometheus
Au lieu d'un Deployment, la base définit un Rollout Argo Rollouts. Il déplace le trafic par paliers pendant qu'une analyse tourne en arrière-plan. Si l'analyse échoue, le rollout est interrompu et le trafic revient de lui-même sur la version stable.
# gitops-config : apps/payments-api/base/rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: payments-api
spec:
replicas: 5
revisionHistoryLimit: 3
selector:
matchLabels:
app: payments-api
template:
metadata:
labels:
app: payments-api
spec:
containers:
- name: payments-api
image: ghcr.io/acme/payments-api # tag défini par l'overlay
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: http
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 256Mi
strategy:
canary:
canaryService: payments-api-canary
stableService: payments-api-stable
analysis:
templates:
- templateName: http-success-rate
startingStep: 1
args:
- name: service-name
value: payments-api-canary
steps:
- setWeight: 20
- pause: { duration: 5m }
- setWeight: 50
- pause: { duration: 10m }payments-api-canary et payments-api-stable sont deux objets Service ordinaires avec le sélecteur app: payments-api. Le contrôleur ajoute un pod-template-hash au sélecteur de chacun, si bien que l'un ne sélectionne que les pods canary et l'autre que les pods stables, ce qui permet à Prometheus de mesurer le canary séparément. Sans traffic router (Istio, NGINX, Gateway API), setWeight est approximé par le nombre de réplicas, donc 20 % de 5 réplicas donnent un pod canary. Si vous avez besoin de pourcentages précis, ajoutez un traffic router.
Voici le template d'analyse :
# gitops-config : apps/payments-api/base/analysis-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: http-success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 1m
failureLimit: 1
successCondition: result[0] >= 0.99
provider:
prometheus:
address: http://prometheus-operated.monitoring.svc:9090
query: |
sum(rate(http_requests_total{service="{{args.service-name}}", code!~"5.."}[2m]))
/
sum(rate(http_requests_total{service="{{args.service-name}}"}[2m]))Adaptez les noms de métrique et de labels à votre instrumentation. Sans count, une analyse en arrière-plan tourne jusqu'à la fin du rollout, et deux mesures en échec (une de plus que failureLimit) l'interrompent. Si le canary ne reçoit aucun trafic, la requête ne renvoie aucune valeur exploitable et l'analyse passe en erreur au lieu de réussir, ce qui est l'issue la plus sûre.
Comment chaque brique réduit le lead time
- Des lots plus petits : chaque release ne contient qu'un seul merge, ce qui la garde petite et peu coûteuse à annuler. Ce sont ces petites releases qui permettent de se passer du comité de changement.
- Une promotion automatisée : la question à laquelle répondait un approbateur humain est désormais une requête Prometheus exécutée à chaque release. En additionnant les étapes, on obtient la CI (quelques minutes si vous avez accéléré le build), une synchronisation Argo CD (quelques secondes avec un webhook) et 15 minutes de pauses canary. Entre le merge et 100 % du trafic, on reste généralement bien en dessous d'une heure, là où il fallait auparavant attendre le prochain créneau d'approbation.
- Un rollback rapide : quand l'analyse échoue, le rollout s'interrompt de lui-même et les pods stables tournent toujours. Faites ensuite un
git revertdu commit de mise à jour pour que Git corresponde de nouveau à ce qui tourne ; Argo CD affiche alors l'application comme saine sur le tag précédent.
Les équipes qui appliquent cette approche avec constance passent généralement de déploiements hebdomadaires validés par un comité de changement à plusieurs déploiements par jour. La traçabilité s'améliore aussi, puisque chaque changement en production garde un commit, un diff, une PR liée et une exécution d'analyse qui lui sont rattachés.
Envie de faire tourner ça en production ?
Ce tutoriel couvre les concepts et l'architecture. Si vous voulez l'implémenter dans votre propre infrastructure, ou monter en compétence pour posséder ce sujet durablement, je propose du mentorat individuel construit autour de votre environnement réel, pas une formation générique.
Ce tutoriel
- Concepts clés et architecture principale
- Extraits de code illustratifs
- Le raisonnement derrière chaque décision
Mentorat individuel
- Des sessions de travail sur votre propre environnement
- Des réponses directes aux cas particuliers que vous rencontrez
- Un retour sur votre implémentation réelle
- Un accompagnement continu pendant que vous la construisez
Tutoriels similaires
Pipelines GitOps avec ArgoCD & Helm
Une configuration GitOps multi-environnements avec Argo CD et Helm : app-of-apps, surcouches par environnement, auto-réparation.
Kubernetes : Deployments, Services et Ingress
Les trois objets de presque toute application Kubernetes, et les réglages de rollout qui évitent qu'une release tourne mal.