Multi-environnements GitOps : AppProjects, ApplicationSet & promotion
Objectifs pédagogiques
- Choisir une organisation des environnements : namespaces ou clusters, dossiers plutôt que branches
- Faire de l'AppProject ArgoCD une frontière de confiance entre staging et production
- Générer les applications de chaque environnement avec un ApplicationSet
- Encadrer les mises en production avec des fenêtres de synchronisation
- Promouvoir exactement l'artefact validé en staging, de façon tracée
Pourquoi plusieurs environnements
Le rôle de staging
- Un environnement proche de la production, où l'on peut tout casser sans conséquence
- C'est là que tournent le DAST authentifié, l'IAST, les tests de charge et les expériences de chaos
- La production ne reçoit que ce qui a passé staging : le même artefact, pas une reconstruction
- Staging n'apporte rien s'il partage secrets, droits ou réseau avec la production : un attaquant qui le compromet doit rester enfermé
Namespaces ou clusters
| Namespaces d'un même cluster | Clusters distincts |
| Coût | faible | plus élevé |
| Isolation | logique : RBAC, NetworkPolicies, AppProjects, quotas | forte : plan de contrôle et nœuds séparés |
| Rayon d'impact | une faille du cluster touche les deux | limité à un environnement |
| Mises à jour du cluster | testées directement en production | testées d'abord sur staging |
| Adapté à | lab, petites équipes | production exigeante, conformité |
- Dans les deux cas, l'étanchéité se prouve par des tests négatifs
Des dossiers, pas des branches
- Une branche par environnement semble naturelle, mais les branches divergent, les correctifs se cherry-pick et la production finit par contenir ce que staging n'a jamais vu
- On préfère une seule branche (
main) et un dossier ou un fichier de valeurs par environnement - Ce qui est commun reste unique (le chart), ce qui diffère est explicite (les valeurs)
- Une MR de promotion montre exactement ce qui change en production
Organisation du dépôt
securepay-platform/
├── charts/securepay-api/ # Un seul chart pour tous les environnements
├── environments/
│ ├── staging/values.yaml # Réplicas, ressources, digest validé en CI
│ └── production/values.yaml # Digest promu, seuils stricts
├── apps/
│ ├── projects/ # AppProjects staging et production
│ └── appsets/securepay-api.yaml # ApplicationSet des environnements
└── policies/ # Politiques communes, action par environnement
AppProject, la frontière de confiance
Ce que borne un AppProject
- sourceRepos : les dépôts dont les applications du projet peuvent tirer leurs manifests
- destinations : les clusters et namespaces où elles peuvent déployer
- clusterResourceWhitelist : les ressources de niveau cluster autorisées (aucune si la liste est vide)
- roles : les droits des personnes et des automates sur les applications du projet
- syncWindows : quand une synchronisation peut avoir lieu
- Le projet
default, ouvert à tout, ne doit héberger aucune application réelle
L'AppProject de production
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Environnement de production SecurePay
sourceRepos:
- https://gitlab.com/<groupe>/securepay-platform.git
destinations:
- server: https://kubernetes.default.svc
namespace: securepay-prod
clusterResourceWhitelist: []
- Constaté en test : une application du projet
staging qui vise securepay-prod est refusée, avec le motif application destination … do not match any of the allowed destinations in project 'staging'
Les fenêtres de synchronisation
spec:
syncWindows:
- kind: deny # ou allow
schedule: "0 18 24 11 *" # format cron : début de la fenêtre
duration: 72h
timeZone: Europe/Paris
applications: ["*"]
manualSync: false # true : une synchronisation manuelle reste possible
- Traduction outillée d'une règle de gestion des changements : gel avant un pic d'activité, couverture d'astreinte
- Constaté en test : la synchronisation automatique est empêchée (Sync prevented by sync window), une synchronisation manuelle reste bloquée, puis tout reprend à la levée de la fenêtre
ApplicationSet
Générer plutôt que copier
- Un ApplicationSet produit des Applications à partir d'un modèle et d'un générateur
- Générateurs :
list : une liste explicite, ici les environnements ; git : un dossier ou un fichier par environnement ; clusters : un élément par cluster déclaré ; matrix et merge : combinaisons des précédents - Les environnements restent identiques par construction, seules les variables changent
L'ApplicationSet SecurePay
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata: {name: securepay-api, namespace: argocd}
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- list:
elements:
- {env: staging, project: staging, namespace: securepay-staging}
- {env: production, project: production, namespace: securepay-prod}
template:
metadata: {name: "securepay-api-{{.env}}"}
spec:
project: "{{.project}}"
source:
repoURL: https://gitlab.com/<groupe>/securepay-platform.git
targetRevision: main
path: charts/securepay-api
helm: {valueFiles: ["../../environments/{{.env}}/values.yaml"]}
destination: {server: https://kubernetes.default.svc, namespace: "{{.namespace}}"}
syncPolicy: {automated: {prune: true, selfHeal: true}}
Points d'attention
- Chaque Application générée reste soumise à son AppProject : l'ApplicationSet ne contourne pas les destinations autorisées
- Un ApplicationSet peut créer et supprimer des applications en masse : son édition est réservée au profil opérateur
missingkey=error évite qu'une variable absente produise silencieusement une application mal configurée - La suppression d'une Application générée peut entraîner celle de ses ressources : protégez la production (politique de synchronisation de l'ApplicationSet, conservation des ressources à la suppression)
Le chemin d'un changement
- La CI construit l'image une seule fois, la signe et l'atteste
- Une MR automatique place son digest dans
environments/staging/values.yaml - ArgoCD synchronise staging ; DAST, tests de charge et expériences s'y exécutent
- Si tout est vert, une MR de promotion recopie le même digest dans
environments/production/values.yaml - Le profil opérateur l'approuve ; la politique d'admission de production vérifie signature et attestations
- ArgoCD synchronise la production, en dehors des fenêtres de gel
promote:production:
stage: promote
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
script:
- DIGEST=$(yq '.image.digest' environments/staging/values.yaml)
- yq -i ".image.digest = \"$DIGEST\"" environments/production/values.yaml
- git checkout -b "promote/$DIGEST"
- git commit -am "promote: securepay-api $DIGEST vers la production"
- git push origin "promote/$DIGEST"
-o merge_request.create -o merge_request.target=main
-o merge_request.title="Promotion production $DIGEST"
- La promotion ne reconstruit rien : elle déplace une référence immuable, et la MR en garde la trace
Des outils pour aller plus loin
- ArgoCD Image Updater : met à jour les références d'images et réécrit le dépôt GitOps
- Kargo : modélise les étapes de promotion entre environnements, avec vérifications et approbations
- Un job de CI qui ouvre une MR reste la solution la plus simple et la plus lisible pour commencer
- Quel que soit l'outil, la promotion doit rester une modification Git revue
Ce qui diffère, ce qui ne diffère pas
| Diffère selon l'environnement | Identique partout |
| Réplicas, ressources, autoscaling | Le digest de l'image promue |
| Action des politiques : Audit en staging, Deny en production | Le chart et sa version |
| Adresse du coffre (une instance par environnement) | La liste des politiques appliquées |
| Routage des alertes, fenêtres de synchronisation | Les sondes et l'instrumentation |
- Plus l'écart entre staging et production est grand, moins les tests de staging prouvent quelque chose
Prouver l'étanchéité
- ArgoCD : une application de staging qui vise la production est refusée par l'AppProject
- Réseau : aucun flux entre
securepay-staging et securepay-prod (NetworkPolicies) - Secrets : un jeton de staging est refusé par l'OpenBao de production
- Identités : le profil développeur n'a aucun droit en production
- Admission : une image non attestée passe en staging (Audit) mais est refusée en production (Deny)
- Ces tests négatifs sont versionnés, rejoués en CI, et leurs résultats rangés dans le dossier de preuves
Anti-patterns à proscrire
- Une branche Git par environnement
- Des manifests copiés-collés d'un environnement à l'autre
- Des applications réelles dans le projet
default - Reconstruire l'image au moment de la promotion
- Promouvoir par
kubectl ou par un clic dans l'interface, sans trace Git - Un staging si différent de la production qu'il ne prouve plus rien
À retenir
- Une seule branche, un chart commun, des valeurs par environnement
- L'AppProject borne sources, destinations et ressources : c'est la frontière de confiance d'ArgoCD
- L'ApplicationSet génère des environnements identiques par construction
- Les fenêtres de synchronisation traduisent la gestion des changements dans l'outil
- La promotion déplace un digest validé par une MR revue, jamais une reconstruction
Pour aller plus loin