Aller au contenu

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)

Promotion


Le chemin d'un changement

  1. La CI construit l'image une seule fois, la signe et l'atteste
  2. Une MR automatique place son digest dans environments/staging/values.yaml
  3. ArgoCD synchronise staging ; DAST, tests de charge et expériences s'y exécutent
  4. Si tout est vert, une MR de promotion recopie le même digest dans environments/production/values.yaml
  5. Le profil opérateur l'approuve ; la politique d'admission de production vérifie signature et attestations
  6. ArgoCD synchronise la production, en dehors des fenêtres de gel

Le job de promotion

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