Les Courants du Destin
GitOps, ArgoCD, Flux et infrastructure as code
Au sommet de la plus haute tour de la Citadelle, il existe une salle secrète que peu d'archivistes connaissent : la Chambre des Courants. C'est ici que les Maîtres des Courants observent les forces invisibles qui gouvernent l'infrastructure du royaume - chaque pont, chaque route, chaque forteresse est façonné par ces courants.
Le Maître des Courants t'invite à observer la grande carte animée qui occupe tout un mur. Des lignes de lumière parcourent le royaume, connectant les serveurs de la Citadelle aux avant-postes les plus reculés. « Chaque modification que tu vois est un courant - un changement décrit dans un parchemin, qui façonne automatiquement la réalité. C'est ce qu'on appelle GitOps. Et c'est la manière la plus puissante de gouverner une infrastructure. »
La philosophie GitOps
GitOps est une philosophie où Git est la source de vérité unique pour toute l'infrastructure et les déploiements applicatifs. L'idée est simple mais révolutionnaire :
- Toute l'infrastructure est décrite dans des fichiers versionnés dans Git
- Modifier l'infrastructure = faire un commit
- Déployer = merger une branche
- Annuler un déploiement = git revert
Le terme a été inventé par Weaveworks en 2017. Depuis, il est devenu un standard de facto dans le monde Kubernetes et au-delà.
Les quatre principes fondamentaux
- Déclaratif : l'état désiré du système est décrit dans des fichiers (YAML, JSON, HCL), pas dans des scripts impératifs
- Versionné : tout est dans Git - l'historique complet de chaque changement d'infrastructure
- Automatique : un agent applique automatiquement les changements quand Git est modifié
- Auto-réconciliant : si quelqu'un modifie l'infrastructure manuellement, l'agent la remet dans l'état décrit par Git
Pull vs Push deployment
Il existe deux modèles pour appliquer les changements d'infrastructure. Comprendre la différence est fondamental.
Le modèle Push (traditionnel)
Dans le modèle push, un système externe (CI/CD) pousse les changements vers le cluster :
# Modèle PUSH - le pipeline CI/CD déploie directement
# .github/workflows/deploy.yml
# 1. Le développeur push du code
# 2. Le CI/CD build l'image Docker
# 3. Le CI/CD applique les manifestes Kubernetes
# kubectl apply -f k8s/deployment.yaml
# 4. Le CI/CD a besoin d'accès direct au cluster (credentials) Problèmes du modèle push :
- Le pipeline CI/CD a besoin de credentials d'accès au cluster - surface d'attaque importante
- Si quelqu'un modifie le cluster manuellement, le CI/CD ne le sait pas (drift)
- Pas de réconciliation automatique
Le modèle Pull (GitOps)
Dans le modèle pull, un agent installé dans le cluster surveille Git et applique les changements :
# Modèle PULL - un agent dans le cluster surveille Git
# 1. Le développeur push du code et des manifestes dans Git
# 2. L'agent GitOps (ArgoCD, Flux) détecte le changement
# 3. L'agent PULL les manifestes depuis Git
# 4. L'agent applique les changements dans le cluster
# 5. Si quelqu'un modifie manuellement, l'agent corrige (auto-healing) Avantages du modèle pull :
- Pas de credentials externes : l'agent est déjà dans le cluster
- Auto-healing : toute modification manuelle est automatiquement corrigée
- Audit complet : chaque changement est un commit Git
- Rollback simple : git revert annule un déploiement
| Aspect | Push (CI/CD classique) | Pull (GitOps) |
|---|---|---|
| Qui déploie | Le pipeline CI/CD | Un agent dans le cluster |
| Credentials | Le CI/CD a accès au cluster | L'agent a accès à Git (lecture seule) |
| Drift | Non détecté | Automatiquement corrigé |
| Rollback | Re-déployer une ancienne version | git revert |
| Audit | Logs du CI/CD | git log (complet, permanent) |
ArgoCD - le gardien du cluster
ArgoCD est l'outil GitOps le plus populaire pour Kubernetes. Il s'installe dans ton cluster et surveille un ou plusieurs dépôts Git. Dès qu'un changement est détecté, il l'applique.
Concepts clés
- Application : une ressource ArgoCD qui lie un dépôt Git à un namespace Kubernetes
- Sync : le processus d'alignement entre l'état Git et l'état du cluster
- Health : l'état de santé des ressources déployées
- Diff : la différence entre l'état désiré (Git) et l'état actuel (cluster)
L'Application CRD
On déclare une Application ArgoCD en YAML. C'est la ressource centrale qui dit : "surveille ce dépôt Git et déploie dans ce namespace" :
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mon-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/mon-org/mon-app-gitops.git
targetRevision: main
path: overlays/production # Dossier contenant les manifestes
destination:
server: https://kubernetes.default.svc
namespace: mon-app
syncPolicy:
automated: # Sync automatique activé
prune: true # Supprime les ressources absentes de Git
selfHeal: true # Corrige les modifications manuelles
syncOptions:
- CreateNamespace=true # Crée le namespace si nécessaire Sync policies
- Manual sync : tu déclenches le déploiement manuellement depuis l'UI ou le CLI
- Automated sync : ArgoCD déploie automatiquement dès qu'il détecte un changement dans Git
- Self-heal : si quelqu'un fait un
kubectl editmanuel, ArgoCD restaure l'état Git - Prune : si tu supprimes un fichier de Git, ArgoCD supprime la ressource correspondante du cluster
# CLI ArgoCD - commandes courantes
# Se connecter
argocd login argocd.mon-domaine.com
# Lister les applications
argocd app list
# Voir l'état d'une application
argocd app get mon-app
# Synchroniser manuellement
argocd app sync mon-app
# Voir l'historique des déploiements
argocd app history mon-app
# Rollback vers une version précédente
argocd app rollback mon-app 3 Astuce : ArgoCD offre une interface web superbe qui affiche l'état de chaque ressource Kubernetes sous forme d'arbre visuel. C'est souvent par l'UI que les équipes surveillent leurs déploiements au quotidien.
Flux - l'alternative CNCF
Flux (anciennement Flux v2) est l'autre grand outil GitOps, maintenu par la CNCF (Cloud Native Computing Foundation). Contrairement à ArgoCD, Flux n'a pas d'interface web par défaut - il est entièrement piloté par des ressources Kubernetes.
Concepts clés de Flux
- GitRepository : pointe vers un dépôt Git à surveiller
- Kustomization : décrit quoi déployer depuis le dépôt et comment
- HelmRelease : déploie des charts Helm depuis Git ou un registry
- ImageUpdateAutomation : met à jour automatiquement les tags d'images dans Git
# Installation de Flux dans un cluster
flux bootstrap github \
--owner=mon-org \
--repository=flux-config \
--path=clusters/production \
--personal
# Cela crée un dépôt Git et installe Flux dans le cluster
# Flux se gère lui-même via GitOps ! Exemple de configuration Flux
# source.yaml - Définir la source Git
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: mon-app
namespace: flux-system
spec:
interval: 1m # Vérifier Git toutes les minutes
url: https://github.com/mon-org/mon-app-gitops
ref:
branch: main
---
# kustomization.yaml - Définir quoi déployer
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: mon-app
namespace: flux-system
spec:
interval: 5m
path: ./overlays/production # Chemin dans le dépôt
prune: true # Supprimer les ressources absentes de Git
sourceRef:
kind: GitRepository
name: mon-app
healthChecks: # Vérifier la santé après déploiement
- apiVersion: apps/v1
kind: Deployment
name: mon-app
namespace: mon-app ArgoCD vs Flux - comment choisir ?
| Aspect | ArgoCD | Flux |
|---|---|---|
| Interface web | Oui, riche et intuitive | Non (CLI + extensions tierces) |
| Configuration | Application CRD + UI | 100% ressources Kubernetes |
| Multi-cluster | Natif (un ArgoCD gère N clusters) | Un Flux par cluster |
| Helm | Support intégré | HelmRelease CRD |
| Philosophie | Outil centralisé avec UI | Composants distribués, natif K8s |
| Courbe d'apprentissage | Plus facile (UI aide) | Plus raide (tout en YAML) |
Structure d'un repo GitOps
L'organisation du dépôt GitOps est cruciale. La structure la plus répandue utilise Kustomize avec des overlays par environnement :
mon-app-gitops/
├── base/ # Configuration commune à tous les envs
│ ├── kustomization.yaml # Liste des ressources de base
│ ├── deployment.yaml # Déploiement de l'application
│ ├── service.yaml # Service Kubernetes
│ ├── configmap.yaml # Configuration non-sensible
│ └── hpa.yaml # Auto-scaling
├── overlays/ # Personnalisations par environnement
│ ├── dev/
│ │ ├── kustomization.yaml # Hérite de base/ + patches dev
│ │ ├── replicas-patch.yaml # 1 réplica en dev
│ │ └── resources-patch.yaml # Moins de CPU/RAM
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ ├── replicas-patch.yaml # 2 réplicas en staging
│ │ └── ingress-patch.yaml # Domaine staging
│ └── production/
│ ├── kustomization.yaml
│ ├── replicas-patch.yaml # 5 réplicas en prod
│ ├── resources-patch.yaml # Plus de CPU/RAM
│ └── ingress-patch.yaml # Domaine production
└── apps/ # Autres applications déployées
├── monitoring/
│ ├── kustomization.yaml
│ └── prometheus.yaml
└── ingress/
├── kustomization.yaml
└── nginx-ingress.yaml Kustomize en deux mots
Kustomize est un outil intégré à kubectl qui permet de personnaliser des manifestes YAML sans les dupliquer. Le principe : une base commune et des overlays qui la modifient.
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
---
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base # Hérite de la base
patches:
- path: replicas-patch.yaml # Applique les patches prod
- path: resources-patch.yaml
namespace: mon-app-prod # Namespace spécifique
---
# overlays/production/replicas-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mon-app
spec:
replicas: 5 # 5 réplicas en production # Prévisualiser ce que Kustomize génère pour la prod
kubectl kustomize overlays/production/
# Appliquer directement (hors GitOps, pour tester)
kubectl apply -k overlays/production/ Gestion des secrets dans GitOps
Le plus grand défi de GitOps : comment versionner des secrets (mots de passe, clés API, certificats) dans Git sans les exposer en clair ?
Sealed Secrets (Bitnami)
Sealed Secrets chiffre tes secrets avec une clé publique. Seul le contrôleur installé dans le cluster peut les déchiffrer :
# Créer un Secret Kubernetes classique (NE PAS commiter !)
kubectl create secret generic mon-secret \
--from-literal=db-password=SuperSecretMotDePasse \
--dry-run=client -o yaml > secret.yaml
# Chiffrer avec kubeseal (clé publique du cluster)
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# Le SealedSecret est safe pour Git - il ne peut être déchiffré
# que par le contrôleur du cluster
cat sealed-secret.yaml
# apiVersion: bitnami.com/v1alpha1
# kind: SealedSecret
# spec:
# encryptedData:
# db-password: AgBy3i4OJSWK+... (chiffré, illisible)
# Commiter le SealedSecret (chiffré) dans Git
git add sealed-secret.yaml
git commit -m "Ajout du secret de base de données (chiffré)" SOPS + age
SOPS (de Mozilla) chiffre les valeurs dans les fichiers YAML/JSON tout en gardant les clés lisibles. Combiné avec age (chiffrement moderne), c'est une solution élégante :
# Installer SOPS et age
# (varie selon l'OS)
# Générer une clé age
age-keygen -o key.txt
# Public key: age1ql3z7hjy...
# Configurer SOPS pour le projet
cat > .sops.yaml << 'EOF'
creation_rules:
- path_regex: .*secrets.*\.yaml$
age: age1ql3z7hjy... # Clé publique
EOF
# Chiffrer un fichier
sops --encrypt secrets.yaml > secrets.enc.yaml
# Le fichier chiffré garde les clés lisibles :
# db:
# password: ENC[AES256_GCM,data:abc123...,type:str]
# host: ENC[AES256_GCM,data:def456...,type:str]
# Déchiffrer (nécessite la clé privée)
sops --decrypt secrets.enc.yaml External Secrets Operator
External Secrets Operator (ESO) ne stocke pas les secrets dans Git du tout. Il les récupère depuis un gestionnaire de secrets externe (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) :
# external-secret.yaml - Référence vers un secret externe
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: mon-secret
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: mon-secret # Nom du Secret K8s créé
data:
- secretKey: db-password
remoteRef:
key: production/database # Chemin dans Vault
property: password | Solution | Principe | Idéal pour |
|---|---|---|
| Sealed Secrets | Chiffrement asymétrique, déchiffrement dans le cluster | Petites équipes, setup simple |
| SOPS + age | Chiffrement des valeurs dans les fichiers YAML | Flexibilité, multi-cloud |
| External Secrets | Référence vers un gestionnaire de secrets externe | Entreprises avec Vault/AWS SM existant |
La puissance du modèle - rollback et audit
Voici où GitOps brille vraiment : les opérations qui sont complexes dans un déploiement traditionnel deviennent triviales.
Rollback = git revert
# Un déploiement a causé un problème en production ?
# Pas de panique. C'est un simple git revert.
# Voir l'historique des déploiements
git log --oneline
# a1b2c3d Mise à jour image v2.3.0
# d4e5f6g Ajout variable d'environnement LOG_LEVEL
# h7i8j9k Mise à jour image v2.2.0
# Annuler le dernier déploiement
git revert HEAD
# [main k1l2m3n] Revert "Mise à jour image v2.3.0"
# Push - l'agent GitOps détecte le changement et rollback automatiquement
git push
# En quelques secondes, la prod est revenue à v2.2.0
# Pas besoin de trouver la bonne commande kubectl
# Pas besoin de chercher la bonne image Docker
# Git a TOUT l'historique Audit trail = git log
# Qui a modifié quoi, quand, et pourquoi ?
git log --format="%h %an %ad %s" --date=short
# a1b2c3d Alice 2026-03-09 Mise à jour image v2.3.0
# d4e5f6g Bob 2026-03-08 Ajout variable LOG_LEVEL=debug
# h7i8j9k Alice 2026-03-07 Mise à jour image v2.2.0
# l4m5n6o Carol 2026-03-05 Augmentation replicas 3 -> 5
# Qui a touché à la config de production ?
git log --oneline -- overlays/production/
# Qu'est-ce qui a changé exactement dans ce déploiement ?
git diff d4e5f6g..a1b2c3d Multi-environnement - branches vs dossiers
Comment gérer dev, staging et production dans un repo GitOps ? Deux approches existent :
Approche par branches (non recommandée)
# Branche dev -> déploie en dev
# Branche staging -> déploie en staging
# Branche main -> déploie en production
# Problème : les promotions se font par merge/cherry-pick
# entre branches. C'est fragile et source d'erreurs.
# Les branches divergent facilement. Approche par dossiers (recommandée)
# Une seule branche (main), des dossiers par environnement
# overlays/dev/ -> déploie en dev
# overlays/staging/ -> déploie en staging
# overlays/production/ -> déploie en production
# Promotion : modifier le tag d'image dans le dossier suivant
# 1. Tester en dev : modifier overlays/dev/kustomization.yaml
# 2. Promouvoir : copier le même tag dans overlays/staging/
# 3. Mettre en prod : copier le tag dans overlays/production/
# Avantage : un seul git log montre tout l'historique
# de tous les environnements. Pas de divergence de branches. Astuce : La communauté GitOps recommande fortement l'approche par dossiers. Les branches par environnement semblent intuitives au début, mais deviennent rapidement un cauchemar de maintenance quand les environnements divergent.
Exercice pratique - Structurer un repo GitOps
Crée un dépôt GitOps pour une application web fictive appelée "taverne-api" (l'API de la Taverne du royaume) avec Kustomize et trois environnements :
- Crée la structure base/ + overlays/ (dev, staging, production)
- Écris les manifestes de base (deployment, service)
- Personnalise chaque environnement (réplicas, ressources)
- Simule une promotion de dev vers production
- Simule un rollback avec git revert
Étape 1 - Créer la structure
# Créer le dépôt
mkdir taverne-gitops
cd taverne-gitops
git init -b main
# Créer l'arborescence
mkdir -p base
mkdir -p overlays/dev
mkdir -p overlays/staging
mkdir -p overlays/production Étape 2 - Les manifestes de base
# base/deployment.yaml
cat > base/deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: taverne-api
spec:
replicas: 1
selector:
matchLabels:
app: taverne-api
template:
metadata:
labels:
app: taverne-api
spec:
containers:
- name: taverne-api
image: taverne-api:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
EOF
# base/service.yaml
cat > base/service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: taverne-api
spec:
selector:
app: taverne-api
ports:
- port: 80
targetPort: 8080
EOF
# base/kustomization.yaml
cat > base/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
EOF
git add base/
git commit -m "Ajout des manifestes de base taverne-api" Étape 3 - Les overlays par environnement
# overlays/dev/kustomization.yaml
cat > overlays/dev/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: taverne-dev
images:
- name: taverne-api
newTag: v1.0.0
EOF
# overlays/staging/kustomization.yaml
cat > overlays/staging/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: taverne-staging
images:
- name: taverne-api
newTag: v1.0.0
patches:
- path: replicas-patch.yaml
EOF
cat > overlays/staging/replicas-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: taverne-api
spec:
replicas: 2
EOF
# overlays/production/kustomization.yaml
cat > overlays/production/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: taverne-prod
images:
- name: taverne-api
newTag: v1.0.0
patches:
- path: replicas-patch.yaml
- path: resources-patch.yaml
EOF
cat > overlays/production/replicas-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: taverne-api
spec:
replicas: 5
EOF
cat > overlays/production/resources-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: taverne-api
spec:
template:
spec:
containers:
- name: taverne-api
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
EOF
git add overlays/
git commit -m "Ajout des overlays dev, staging et production" Étape 4 - Simuler une promotion
# Nouvelle version testée en dev
sed -i 's/newTag: v1.0.0/newTag: v1.1.0/' overlays/dev/kustomization.yaml
git add overlays/dev/
git commit -m "Deploy taverne-api v1.1.0 en dev"
# Promotion en staging
sed -i 's/newTag: v1.0.0/newTag: v1.1.0/' overlays/staging/kustomization.yaml
git add overlays/staging/
git commit -m "Promotion taverne-api v1.1.0 en staging"
# Promotion en production
sed -i 's/newTag: v1.0.0/newTag: v1.1.0/' overlays/production/kustomization.yaml
git add overlays/production/
git commit -m "Promotion taverne-api v1.1.0 en production" Étape 5 - Simuler un rollback
# Bug en prod ! On annule le dernier déploiement
git revert HEAD --no-edit
# [main abc1234] Revert "Promotion taverne-api v1.1.0 en production"
# Vérifier que la prod est revenue à v1.0.0
grep newTag overlays/production/kustomization.yaml
# newTag: v1.0.0
# L'historique complet montre tout
git log --oneline
# abc1234 Revert "Promotion taverne-api v1.1.0 en production"
# def5678 Promotion taverne-api v1.1.0 en production
# ghi9012 Promotion taverne-api v1.1.0 en staging
# jkl3456 Deploy taverne-api v1.1.0 en dev
# mno7890 Ajout des overlays dev, staging et production
# pqr1234 Ajout des manifestes de base taverne-api Récapitulatif des concepts
| Concept | Description |
|---|---|
| GitOps | Philosophie où Git est la source de vérité pour l'infrastructure |
| Modèle Pull | Un agent dans le cluster surveille Git et applique les changements |
| Modèle Push | Le CI/CD pousse les changements vers le cluster (traditionnel) |
| ArgoCD | Outil GitOps populaire avec interface web riche |
| Flux | Outil GitOps CNCF, 100% natif Kubernetes |
| Kustomize | Outil de personnalisation de manifestes YAML (base + overlays) |
| Sealed Secrets | Chiffrement asymétrique de secrets pour Git |
| SOPS + age | Chiffrement des valeurs dans les fichiers YAML |
| External Secrets | Récupération de secrets depuis un gestionnaire externe |
| Self-healing | Correction automatique des modifications manuelles |
| Dossiers vs branches | Les dossiers par environnement sont préférés aux branches |
Le Maître des Courants éteint la grande carte animée d'un geste de la main. La salle retrouve son calme. « Tu comprends maintenant pourquoi on les appelle les Courants du Destin. Chaque commit dans ton dépôt GitOps est un courant qui façonne l'infrastructure du royaume. Pas de magie obscure, pas de commandes tapées en urgence sur un serveur en feu. Juste des parchemins versionnés qui deviennent réalité. »
« Le plus beau, c'est que tout est dans l'historique. Qui a changé quoi, quand, pourquoi. Si quelque chose se brise, tu remontes le courant. Un simple git revert, et le royaume retrouve sa stabilité. C'est la paix de l'esprit que seul un Maître des Courants peut offrir. »
Il te tend un médaillon gravé d'un arbre dont les branches se transforment en flux lumineux. « Tu as appris à gouverner l'infrastructure par les Courants. Utilise ce pouvoir avec sagesse. »