Aller au contenu
ApplicationSet : des applications en série

ApplicationSet : des applications en série

300 Concevoir ⏱ 1 h 15 argocdkubernetesgitopshelm

À la fin, vous saurez

  • Remplacer des Applications écrites à la main par un ApplicationSet, sans interrompre le service
  • Choisir un générateur : liste, répertoires Git, matrice, demandes de fusion
  • Créer un environnement d'aperçu par demande de fusion, détruit à sa fermeture
  • Protéger les environnements contre la suppression en cascade
  • Vérifier la génération avant de l'appliquer

Prérequis

Testé avec argocd 3.5.3 forgejo 16.0.5 kubernetes 1.36.4 , vérifié le 2 octobre 2026

Pourquoi

À la leçon 4, chaque environnement de Signalements avait son fichier d'Application dans le dépôt gitops, presque identique à son voisin. Avec deux environnements, c'est supportable. Avec dix applications et trois environnements, ce sont trente fichiers qui ne diffèrent que par deux mots, et qui finissent par dériver comme les copies de workflows du cours GitHub Actions.

L'ApplicationSet remplace ces copies par un modèle et un générateur : le générateur produit une liste de jeux de paramètres (un par environnement, par cluster, par demande de fusion...), et le contrôleur crée une Application par jeu, à partir du modèle. Ajouter un environnement devient ajouter un répertoire ; un environnement d'aperçu par demande de fusion devient possible sans écrire une ligne de script.

C'est aussi un outil puissant au sens propre : un ApplicationSet crée et supprime des applications de lui-même, avec leurs ressources. Cette leçon montre ce que cela permet, et ce que cela détruit si l'on n'y prend pas garde.

Les concepts

Générateur, paramètres, modèle

    flowchart LR
  G["Générateur<br/>(répertoires Git, liste,<br/>demandes de fusion...)"] -->|"paramètres<br/>{path: environnements/recette}<br/>{path: environnements/production}"| T["Modèle<br/>d'Application"]
  T --> A1["signalements-recette"]
  T --> A2["signalements-production"]
  
GénérateurProduit un jeu de paramètres par...Exemple d'usage
listélément d'une liste écrite dans l'ApplicationSetun petit nombre de valeurs fixes
git (répertoires)répertoire correspondant à un motifun environnement par répertoire
git (fichiers)fichier de configuration (JSON, YAML)une application par fichier de description
clustercluster déclaré dans Argo CDune application par cluster (leçon 7)
pullRequestdemande de fusion ouverteun environnement d'aperçu par demande de fusion
scmProviderdépôt d'une organisationune application par dépôt
matrix, mergecombinaison de deux générateursapplications × environnements
clusterDecisionResource, pluginressource ou service externecas avancés

Le modèle s'écrit de préférence en modèles Go (goTemplate: true), avec l'option missingkey=error : une faute de frappe dans un paramètre fait alors échouer la génération, au lieu de produire silencieusement un champ vide.

Le cycle de vie

Le contrôleur d'ApplicationSet est lui-même une boucle de réconciliation : il régénère périodiquement la liste des paramètres, crée les Applications nouvelles, met à jour les existantes, et supprime celles qui ne sont plus générées. Les Applications générées lui appartiennent (référence de propriétaire), et, par défaut, elles portent le finalizer de suppression en cascade : supprimer une Application générée, c'est supprimer ses ressources.

En pratique

Un environnement par répertoire

L'ApplicationSet qui remplace les deux fichiers de la leçon 4 utilise le générateur de répertoires Git sur le dépôt de déploiement :

# gitops/applications/signalements.yaml
# Une application par répertoire de environnements/ dans le dépôt de déploiement.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: signalements
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - git:
        repoURL: http://forgejo.forge.svc.cluster.local:3000/plateforme/signalements-deploiement.git
        revision: main
        directories:
          - path: environnements/*
  template:
    metadata:
      name: 'signalements-{{ .path.basename }}'
    spec:
      project: default
      source:
        repoURL: http://forgejo.forge.svc.cluster.local:3000/plateforme/signalements-deploiement.git
        targetRevision: main
        path: chart
        helm:
          releaseName: signalements
          valueFiles:
            - '../{{ .path.path }}/values.yaml'
      destination:
        server: https://kubernetes.default.svc
        namespace: 'signalements-{{ .path.basename }}'
      ignoreDifferences:
        - group: apps
          kind: Deployment
          jsonPointers:
            - /spec/replicas
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
          - RespectIgnoreDifferences=true

Pour chaque répertoire environnements/<nom>, le générateur fournit .path.path (le chemin complet) et .path.basename (le dernier élément, recette ou production). Le modèle en tire le nom de l'application, le fichier de valeurs et le namespace.

Migrer sans redémarrer

Remplacer deux Applications par un ApplicationSet qui génère les mêmes noms doit être invisible pour le cluster : les Applications changent de propriétaire, pas de contenu. On note l'heure de démarrage des pods avant, pour vérifier après :

$ for e in recette production; do kubectl -n signalements-$e get pods -l app=signalements \
    -o jsonpath='{range .items[*]}{.metadata.name} {.status.startTime}{"\n"}{end}'; done > pods-avant.txt

Pendant la préparation de ce cours, la première tentative de cette migration a mal tourné, de façon instructive : le commit a supprimé les deux fichiers d'Application sans ajouter le nouveau (le répertoire applications/, vidé, avait disparu, et l'écriture du fichier suivant a échoué). L'application racine, en élagage automatique, aurait pu supprimer les deux environnements. Elle n'a rien supprimé :

$ argocd app get racine -o json | jq -r '.status.conditions[]?|"\(.type): \(.message)"'
ComparisonError: Failed to load target state: failed to generate manifest for source 1 of 1: rpc error: code = Unknown desc = applications: app path does not exist

Un chemin de source qui n'existe plus est une erreur de rendu, pas un rendu vide : l'application passe à Unknown et rien n'est élagué. Les deux environnements ont continué de tourner. Le commit suivant a ajouté l'ApplicationSet :

$ git add applications && git commit -qm "Signalements : l'ApplicationSet" && git push -q origin main
$ argocd app get racine --refresh | grep -E "^Sync Status|ApplicationSet|  Application"
Sync Status:        Synced to main (81768c7)
argoproj.io  Application     argocd     signalements-production  Succeeded  Pruned         pruned
argoproj.io  Application     argocd     signalements-recette     Succeeded  Pruned         pruned
argoproj.io  ApplicationSet  argocd     signalements             Synced     Healthy        applicationset.argoproj.io/signalements created
$ kubectl -n argocd get applications -o custom-columns='NOM:.metadata.name,PROPRIETAIRE:.metadata.ownerReferences[0].kind'
NOM                       PROPRIETAIRE
racine                    <none>
signalements-production   ApplicationSet
signalements-recette      ApplicationSet
$ for e in recette production; do kubectl -n signalements-$e get pods -l app=signalements \
    -o jsonpath='{range .items[*]}{.metadata.name} {.status.startTime}{"\n"}{end}'; done > pods-apres.txt
$ diff pods-avant.txt pods-apres.txt && echo "aucun pod redémarré"
aucun pod redémarré

La racine a élagué les deux anciennes Applications (sans finalizer de cascade, leurs ressources sont restées), l'ApplicationSet les a recréées sous les mêmes noms, et elles ont retrouvé leurs ressources grâce à l'annotation de suivi, qui porte le nom de l'application. Aucun pod n'a redémarré.

Ajouter et retirer un environnement

Note

Les lignes qui commencent par # dans les blocs suivants résument ce qu'a affiché une boucle de relevé (un kubectl get à intervalles réguliers), omise pour la lisibilité.

Ajouter un environnement de démonstration, c'est créer un répertoire :

$ mkdir -p environnements/demo
$ printf 'image:\n  tag: "1.2.0"\nreplicas: 1\nenvironnement: demo\n' > environnements/demo/values.yaml
$ git add environnements && git commit -qm "Environnement de démonstration" && git push -q origin main
# application signalements-demo créée après ~155 s
$ ./version.sh demo
{"environnement":"demo","version":"1.2.0"}

Deux minutes et demie : le générateur Git réinterroge le dépôt toutes les trois minutes par défaut (un webhook le rend immédiat). Et le retirer, c'est supprimer le répertoire... avec une conséquence qu'il faut voir de ses yeux :

$ kubectl -n argocd get application signalements-recette -o jsonpath='finalizers : {.metadata.finalizers}{"\n"}'
finalizers : ["resources-finalizer.argocd.argoproj.io"]
$ git rm -rq environnements/demo && git commit -qm "Fin de l'environnement de démonstration" && git push -q origin main
# application supprimée après ~175 s
$ kubectl -n signalements-demo get all
No resources found in signalements-demo namespace.

Les Applications générées portent le finalizer de cascade. Retirer le répertoire a supprimé l'Application, et toutes ses ressources. Pour un environnement de démonstration, c'est le comportement voulu. Pour la production, cela veut dire qu'un git rm malencontreux, ou un déplacement de répertoire, détruit la production trois minutes plus tard.

Protéger les environnements

Deux réglages de l'ApplicationSet s'en chargent :

spec:
  # Protections : ne jamais supprimer en cascade, ne jamais supprimer une application générée.
  syncPolicy:
    preserveResourcesOnDeletion: true
    applicationsSync: create-update
  goTemplate: true
  # ...
  • preserveResourcesOnDeletion: true n'ajoute plus le finalizer aux Applications générées : supprimer une Application laisse ses ressources en place.
  • applicationsSync: create-update interdit au contrôleur de supprimer une Application qui n'est plus générée (il peut toujours en créer et en modifier). Les autres valeurs sont create-only, create-delete et sync (la valeur par défaut, tout est permis).

Le même essai, avec ces protections :

$ kubectl -n argocd get application signalements-production -o jsonpath='finalizers production : {.metadata.finalizers}{"\n"}'
finalizers production :
$ git rm -rq environnements/demo && git commit -qm "Fin de l'environnement de démonstration" && git push -q origin main
$ kubectl -n argocd get application signalements-demo
signalements-demo   Synced        Healthy
$ kubectl -n signalements-demo get deploy,svc
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/signalements   1/1     1            1           4m23s

NAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/signalements   ClusterIP   10.96.18.87   <none>        80/TCP    4m25s

Le générateur ne produit plus que deux applications (son journal le dit : generated 2 applications), mais l'Application signalements-demo est conservée, ressources comprises. La suppression devient un geste explicite : kubectl -n argocd delete application signalements-demo, sans cascade puisque le finalizer n'est plus posé, puis le namespace.

Ces protections ne couvrent pas la suppression de l'ApplicationSet lui-même : ses Applications lui appartiennent (références de propriétaire), et Kubernetes les supprime avec lui. Avec preserveResourcesOnDeletion, leurs ressources restent en place, mais les Applications disparaissent. Pour retirer un ApplicationSet en gardant ses Applications, la documentation indique kubectl delete applicationset <nom> --cascade=orphan.

Un environnement d'aperçu par demande de fusion

Le générateur de demandes de fusion interroge l'API de la forge. Forgejo expose une API compatible avec celle de Gitea, que le générateur prend en charge :

# gitops/applications/apercus.yaml
# Un environnement d'aperçu par demande de fusion ouverte sur le dépôt de déploiement.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apercus
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - pullRequest:
        gitea:
          owner: plateforme
          repo: signalements-deploiement
          api: http://forgejo.forge.svc.cluster.local:3000
        requeueAfterSeconds: 30
  template:
    metadata:
      name: 'apercu-{{ .number }}'
    spec:
      project: default
      source:
        repoURL: http://forgejo.forge.svc.cluster.local:3000/plateforme/signalements-deploiement.git
        targetRevision: '{{ .head_sha }}'
        path: chart
        helm:
          releaseName: signalements
          valueFiles:
            - ../environnements/recette/values.yaml
          valuesObject:
            environnement: 'apercu-{{ .number }}'
            replicas: 1
            postgresql:
              enabled: false
      destination:
        server: https://kubernetes.default.svc
        namespace: 'apercu-{{ .number }}'
      syncPolicy:
        automated:
          prune: true
        syncOptions:
          - CreateNamespace=true
  • targetRevision: '{{ .head_sha }}' : l'aperçu déploie exactement le dernier commit de la demande de fusion, et suit chaque nouveau commit.
  • valuesObject surcharge les valeurs de la recette : une réplique, pas de base de données, un nom d'environnement propre à l'aperçu.
  • requeueAfterSeconds: 30 : la forge est interrogée toutes les 30 secondes.
  • Pas de preserveResourcesOnDeletion ici : un aperçu doit disparaître complètement avec sa demande de fusion.

Une développeuse ouvre une demande de fusion qui ajoute une sonde de vivacité au chart :

$ git switch -c fonction/sonde-vivacite
$ # ... ajout de livenessProbe dans chart/templates/deployment.yaml
$ git commit -qam "Sonde de vivacité" && git push -q origin fonction/sonde-vivacite
$ curl -sf ... -X POST .../repos/plateforme/signalements-deploiement/pulls \
    -d '{"title":"Sonde de vivacité","head":"fonction/sonde-vivacite","base":"main"}'
demande de fusion n° 3 ouverte
# application apercu-3 créée 6 s après l'ouverture
$ argocd app list -o name
argocd/apercu-3
argocd/racine
argocd/signalements-production
argocd/signalements-recette
$ kubectl -n apercu-3 get deploy signalements -o jsonpath='{.spec.template.spec.containers[0].livenessProbe.httpGet.path}{"\n"}'
/sante
$ ...
{"environnement":"apercu-3","version":"1.2.0","stockage":"memoire"}

Six secondes après l'ouverture, un environnement complet tourne avec le chart de la demande de fusion : la personne qui relit peut tester le changement, pas seulement lire son diff. À la fusion :

$ curl -sf ... -X POST .../pulls/3/merge -d '{"Do":"merge","delete_branch_after_merge":true}'
fusion : HTTP 200
# aperçu supprimé 21 s après la fusion
$ kubectl -n apercu-3 get deploy
No resources found in apercu-3 namespace.
$ kubectl get namespace apercu-3
NAME       STATUS   AGE
apercu-3   Active   48s

L'aperçu a disparu, ressources comprises, mais le namespace est resté : Argo CD ne supprime pas les namespaces créés par CreateNamespace. Pour des aperçus, on ajoute un nettoyage (un hook PostDelete, ou une tâche planifiée qui supprime les namespaces apercu-* vides), ou l'on fait du namespace une ressource du chart, élaguée comme les autres.

Combiner : la matrice

Pour plusieurs applications sur plusieurs environnements, le générateur matrix combine deux générateurs. Avec templatePatch, une partie du modèle peut dépendre des paramètres, ici la synchronisation automatique, réservée à la recette :

# Toutes les combinaisons application × environnement.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: plateforme
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - matrix:
        generators:
          - list:
              elements:
                - application: signalements
                - application: annuaire
          - list:
              elements:
                - environnement: recette
                  automatique: true
                - environnement: production
                  automatique: false
  template:
    metadata:
      name: '{{ .application }}-{{ .environnement }}'
    spec:
      project: default
      source:
        repoURL: 'http://forgejo.forge.svc.cluster.local:3000/plateforme/{{ .application }}-deploiement.git'
        targetRevision: main
        path: chart
        helm:
          valueFiles:
            - '../environnements/{{ .environnement }}/values.yaml'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{ .application }}-{{ .environnement }}'
  templatePatch: |
    {{- if .automatique }}
    spec:
      syncPolicy:
        automated:
          selfHeal: true
    {{- end }}

Avant d'appliquer un ApplicationSet, on vérifie ce qu'il générerait, sans rien créer :

$ kubectl apply --dry-run=server -f matrice.yaml
applicationset.argoproj.io/plateforme created (server dry run)
$ argocd appset generate matrice.yaml -o json | jq -r '.[] | "\(.metadata.name)  \(.spec.destination.namespace)  automatique=\(.spec.syncPolicy.automated != null)"'
signalements-recette  signalements-recette  automatique=true
signalements-production  signalements-production  automatique=false
annuaire-recette  annuaire-recette  automatique=true
annuaire-production  annuaire-production  automatique=false

La première commande valide le schéma ; la seconde exécute réellement les générateurs et montre les quatre Applications, avec la synchronisation automatique sur les seules recettes. C'est le réflexe à prendre avant toute modification d'un ApplicationSet en production.

Sous le capot

Le contrôleur d'ApplicationSet rend chaque générateur en une liste de paramètres, applique le modèle à chacun, puis compare la liste des Applications voulues à celle des Applications qu'il possède (par référence de propriétaire). Les différences donnent les créations, les mises à jour et les suppressions, filtrées par la politique applicationsSync. Les générateurs Git et de demandes de fusion sont rafraîchis périodiquement (trois minutes par défaut pour Git, requeueAfterSeconds pour les demandes de fusion), ou immédiatement par un webhook.

La documentation indique que la politique applicationsSync d'un ApplicationSet n'est prise en compte que si l'option enable-policy-override du contrôleur est activée, et la présente comme désactivée par défaut. Le code de la version 3.5.3 dit autre chose (ligne 303 de applicationset_controller.go, abrégée) :

command.Flags().BoolVar(&enablePolicyOverride, "enable-policy-override",
    env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE", policy == ""), ...)

La valeur par défaut est policy == "", évaluée à partir de la variable ARGOCD_APPLICATIONSET_CONTROLLER_POLICY, alimentée par la clé applicationsetcontroller.policy de argocd-cmd-params-cm : la politique de chaque ApplicationSet est respectée tant que l'administrateur n'a pas fixé de politique globale. C'est ce que confirme l'essai plus haut. Si une politique globale est fixée, elle s'impose à tous les ApplicationSet, sauf activation explicite de la surcharge.

Avec missingkey=error, une référence à un paramètre absent fait échouer la génération pour tout l'ApplicationSet, avec une condition ErrorOccurred=True : aucune Application n'est modifiée ni supprimée tant que l'erreur n'est pas corrigée. Sans cette option, le paramètre absent devient la chaîne <no value>, ce qui peut produire des noms ou des namespaces inattendus.

Pièges courants

Un environnement supprimé par un git rm. Les Applications générées portent par défaut le finalizer de cascade, et le contrôleur supprime les applications qui ne sont plus générées. Protégez les environnements durables avec preserveResourcesOnDeletion et applicationsSync: create-update.

Une Application générée « revient » après une modification manuelle. Le contrôleur d'ApplicationSet réécrit les Applications selon le modèle : une modification faite à la main sur une Application générée est annulée. On modifie le modèle, ou on exclut des champs (ignoreApplicationDifferences).

Le nouvel environnement n'apparaît qu'après trois minutes. Le générateur Git est rafraîchi périodiquement. Webhook, ou patience.

Les namespaces d'aperçu s'accumulent. CreateNamespace ne supprime pas ce qu'il crée. Prévoyez le nettoyage.

Deux générateurs produisent le même nom d'Application. La génération échoue pour éviter que deux jeux de paramètres se disputent une même Application. Incluez assez de paramètres dans le nom.

<no value> dans un nom ou un chemin. Un paramètre mal orthographié avec les modèles par défaut. goTemplate: true et missingkey=error.

Sécurité

Seuls les administrateurs devraient créer des ApplicationSet. C'est la recommandation explicite de la documentation : un ApplicationSet peut générer des Applications dans n'importe quel projet que son modèle désigne, y compris un projet privilégié, et un générateur peut contacter des API externes avec des secrets. Le dépôt qui les contient (gitops ici) doit être relu par l'équipe plateforme.

Le générateur de demandes de fusion déploie du code non relu. Par construction, un aperçu exécute le contenu d'une demande de fusion avant sa relecture. Sur un dépôt où n'importe qui peut ouvrir une demande de fusion (un dépôt public, une bifurcation), c'est l'équivalent de la pwn request du cours GitHub Actions : quelqu'un fait exécuter ce qu'il veut dans votre cluster. Restreignez le générateur (étiquettes de demande de fusion, filtres sur les branches ou le titre), placez les aperçus dans un projet aux droits très limités (leçon 9), dans des namespaces isolés par des politiques réseau, et sans accès aux secrets de production.

Ne jamais rendre le projet paramétrable. Un champ project: '{{ .projet }}' alimenté par une source que d'autres contrôlent permet de choisir le projet le plus privilégié.

En production

Chez Lyneko, chaque application a aujourd'hui son Application écrite à la main, ce qui suffit à cette échelle. L'ApplicationSet devient intéressant avec les environnements d'aperçu, ou si les applications passent à plusieurs environnements chacune ; le générateur de répertoires Git ou la matrice remplacent alors les copies.

Les aperçus coûtent des ressources. Chaque demande de fusion ouverte occupe un namespace et des pods. Sur un petit cluster comme celui de Lyneko, où la mémoire et le disque sont comptés, on limite les aperçus (une étiquette apercu à poser explicitement sur la demande de fusion, filtrée par le générateur) et on les réduit au minimum (une réplique, pas de base, ou une base partagée de démonstration).

La synchronisation progressive. En bêta depuis la 3.3, la stratégie RollingSync d'un ApplicationSet synchronise les applications générées par groupes successifs (d'abord la recette, puis une région, puis le reste), en attendant que chaque groupe soit sain. Elle s'active explicitement dans la configuration du contrôleur, et désactive la synchronisation automatique des Applications générées : c'est l'ApplicationSet qui décide quand chaque groupe se synchronise.

Exercices

1. Transformez l'ApplicationSet signalements pour qu'il n'utilise que la synchronisation automatique en recette, et une synchronisation manuelle en production.

Solution

Retirer automated du modèle et l'ajouter par templatePatch selon le répertoire :

  templatePatch: |
    {{- if eq .path.basename "recette" }}
    spec:
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
    {{- end }}

Vérifier avec argocd appset generate avant de pousser.

2. Pour le générateur de demandes de fusion, faites en sorte que seules les demandes de fusion portant l'étiquette apercu créent un environnement.

Solution

Le fournisseur Gitea du générateur accepte une liste d'étiquettes (champ labels, sous gitea:) :

    - pullRequest:
        gitea:
          owner: plateforme
          repo: signalements-deploiement
          api: http://forgejo.forge.svc.cluster.local:3000
          labels:
            - apercu
        requeueAfterSeconds: 30

Seules les demandes de fusion portant l'étiquette créent un aperçu ; retirer l'étiquette supprime l'aperçu. Poser une étiquette demandant en général un droit sur le dépôt, c'est aussi une barrière contre les demandes de fusion de personnes extérieures.

3. Avec les protections de cette leçon, comment supprime-t-on réellement un environnement devenu inutile ?

Solution

Supprimer son répertoire ne suffit plus. Après l'avoir retiré de Git : supprimer l'Application à la main (kubectl -n argocd delete application signalements-<nom>, sans cascade puisque le finalizer n'est plus posé), puis supprimer le namespace et ce qu'il contient. Si l'on veut une suppression complète par Argo CD, on peut poser temporairement le finalizer de cascade sur l'Application avant de la supprimer. C'est volontairement en plusieurs gestes : la suppression d'un environnement doit être une décision.

4. Le générateur de répertoires utilise environnements/*. Que se passe-t-il si quelqu'un crée environnements/archives/ancienne-recette/values.yaml ? Et environnements/notes.md ?

Solution

* ne traverse pas les / : environnements/archives est un répertoire correspondant, et devient une application signalements-archives, qui échouera à trouver environnements/archives/values.yaml (ComparisonError). notes.md est un fichier, pas un répertoire : ignoré. On peut exclure des répertoires avec une entrée exclude: true dans directories.

5. Pourquoi la migration de deux Applications vers l'ApplicationSet n'a-t-elle redémarré aucun pod ? Qu'est-ce qui aurait changé si le modèle avait produit signalements-env-recette au lieu de signalements-recette ?

Solution

Les Applications générées portent les mêmes noms que les anciennes ; les ressources, marquées par une annotation de suivi qui contient le nom de l'application, sont reconnues comme les leurs, et leur contenu est identique : rien à appliquer. Avec un autre nom, la nouvelle Application aurait trouvé des ressources marquées au nom d'une autre application : elle les aurait considérées comme ne lui appartenant pas, puis réécrit leur annotation à la synchronisation, et les anciennes Applications, si elles avaient encore existé avec leur finalizer, auraient pu les supprimer. Garder les noms est la clé d'une migration sans interruption.

Récapitulatif

  • Un ApplicationSet = un générateur (liste, répertoires Git, demandes de fusion, clusters, matrice...) et un modèle d'Application ; écrivez les modèles en Go avec missingkey=error.
  • Remplacer des Applications par un ApplicationSet qui génère les mêmes noms ne redémarre rien.
  • Par défaut, les Applications générées portent le finalizer de cascade, et le contrôleur supprime celles qui ne sont plus générées : un git rm peut détruire un environnement.
  • preserveResourcesOnDeletion: true et applicationsSync: create-update protègent les environnements durables (respecté par défaut tant qu'aucune politique globale n'est fixée).
  • Le générateur de demandes de fusion crée un aperçu par demande, détruit à la fermeture ; il exécute du code non relu, à cloisonner.
  • argocd appset generate montre ce qu'un ApplicationSet produirait, avant de l'appliquer.

Pour aller plus loin

Voir ma constellation →

Sources