App-of-Apps — instanciation déclarative des Application applicatives
App-of-Apps — instanciation déclarative des Application applicatives
Implémente
l’ADR 0094 §2.
Ajouter une app = pousser un fichier. Plus besoin d’éditer un script bash
pour créer l’Application Argo CD d’une nouvelle app applicative.
Le mécanisme en une phrase
cluster pousse une déclaration d’Application dans le repo Gitea de prod
dédié cluster/apps → l’Application racine (« app-of-apps ») réconcilie
ce repo et crée l’Application fille → celle-ci réconcilie le repo
atlas (le code applicatif).
cluster (socle) Gitea: cluster/apps Argo CD ─────────────── ──────────────────── ─────── push apps/citation.yaml ───► apps/ ◄────── Application RACINE (repoURL atlas injecté) ├── citation.yaml (cluster-apps) └── mediawatch.yaml │ crée ▼ Gitea: atlas (le code) ◄────── Application FILLE dataops/citation-dagster/ (citation-dagster, deploy/overlays/prod projet atlas)Deux repos Gitea distincts, deux rôles :
cluster/apps(nouveau, prod) : ne contient QUE des déclarations d’Application. C’est l’état déclaratif des apps installées. Possédé par cluster.atlas(existant, ADR 0044) : le CODE applicatif (manifestes, overlays,.sql). Possédé par atlas.
Fichiers de ce dossier (patrons *.example, ADR 0023)
| Fichier | Rôle |
|---|---|
appproject-cluster-apps.example.yaml | AppProject cluster-apps — cadre la SEULE Application racine (privilège isolé). |
root-application.example.yaml | L’Application RACINE : surveille cluster/apps, path: apps, prune activé. |
apps/citation.example.yaml | La déclaration d’Application citation que cluster POUSSE dans cluster/apps. |
Aucune de ces valeurs (repoURL, targetRevision) n’est réelle : ce sont des
exemples génériques. Les URL réelles sont des valeurs de déploiement injectées
au seed (ci-dessous), jamais gravées en versionné (ADR 0023). C’est la
généralisation du pattern déjà livré dans
bench/lima/gitea-init.sh (Application
atlas-workflows, codée en dur pour un seul cas) vers un chemin déclaratif.
Flux d’ajout d’une app
- atlas fournit le code + un
code-location.manifest.yaml(déclaration montante, ADR 0094 §3). - cluster lit et valide ce manifeste (validateur, cf. plus bas) : version de contrat connue, dépendances présentes (base, secret, OBC, migration), capacité suffisante.
- cluster rend la déclaration d’
Applicationà partir du patronapps/<app>.example.yamlen injectant lerepoURLatlas réel et letargetRevision(= champrevisiondu manifeste, le SHA git). - cluster pousse le fichier
apps/<app>.yamldans le repo Giteacluster/apps. - la racine app-of-apps réconcilie → crée l’
Applicationfille → celle-ci déploie le code atlas dansdagster.
Retirer une app = supprimer son fichier de cluster/apps : la racine
(prune: true) supprime l’Application fille, dont le finalizer cascade la
suppression des ressources.
La frontière (ADR 0094 §1)
| atlas DÉCLARE + FOURNIT | cluster VALIDE + INSTANCIE + ORCHESTRE |
|---|---|
| manifestes de l’app, overlays | lit le manifeste de déclaration, valide capacité |
.sql de migration (schéma métier) | applique la migration (Job hook PreSync) |
code-location.manifest.yaml | provisionne base/secret dérivé/OBC |
| le code de la code-location | crée l’Application (pousse dans cluster/apps) |
Seed en prod (généralisation de gitea-init.sh)
Le seed n’est pas encore codé (proposition). Il généralise le pattern de
bench/lima/gitea-init.sh (l. 108-200 : crée
org/repo Gitea via l’API, pousse des fichiers par PUT/POST /contents, crée
l’Application par kubectl apply avec repoURL injecté). Étapes et points
d’injection :
-
Créer le repo Gitea
cluster/apps(orgcluster, repoapps,auto_init: true, branchemain) viaPOST /orgs+POST /orgs/cluster/repos— idempotent, comme l’org/repo atlas aujourd’hui. Injection :GITEA_APPS_ORG/GITEA_APPS_REPO(défauts génériquescluster/apps). -
Rendre chaque déclaration fille depuis
apps/<app>.example.yamlen substituant :repoURL→ URL Gitea réelle du repo atlas (${GITEA_SVC}/${ATLAS_ORG}/${ATLAS_REPO}.git) ;targetRevision→ champrevisionducode-location.manifest.yamlatlas.
Injection : ces deux valeurs sont des valeurs de déploiement (jamais versionnées). Réutiliser le helper
push_gitea_file(Contents API, lecture du SHA pour MAJ idempotente, vérification de la réponse — un PUT raté laisse une version périmée, drift à ne pas reproduire). -
Pousser les fichiers rendus dans
cluster/appssousapps/<app>.yaml(PUT/POST /repos/cluster/apps/contents/apps/<app>.yaml). -
Appliquer l’
AppProject cluster-appsdepuisappproject-cluster-apps.example.yaml(kubectl apply,sourceRepossurchargé par l’URL Gitea réelle de l’instance — comme l’AppProject atlas). -
Appliquer l’
Applicationracine depuisroot-application.example.yamlen injectantrepoURL=${GITEA_SVC}/cluster/apps.git(valeur de déploiement,kubectl apply -f -, exactement commegitea-init.shl. 177-198 pouratlas-workflows). -
(optionnel) Webhook Gitea
cluster/apps→argocd-server/api/webhookpour la réconciliation immédiate (réutiliser le secret partagéargocd-webhook-shared,gitea-init.shl. 145-170).
Le seed pose AUSSI les dépendances que le manifeste déclare (base, secret dérivé
pgvector-pg-auth, OBCatlas-datalake) AVANT de pousser la déclaration — c’est le « cluster VALIDE/PROVISIONNE » de la frontière. Ces briques préexistent en partie (rôleplatform-dagster, OBC côté cluster).
Analyse de cohérence (questions de conception)
AppProject : cluster-apps dédié vs réutiliser atlas — DÉDIÉ (tranché)
La racine app-of-apps est rattachée à un AppProject cluster-apps distinct,
pas à atlas. Raison RBAC : la racine est une Application dont la seule
mission est de créer/supprimer d’autres Application dans le namespace
argocd. C’est un privilège que les apps métier (projet atlas, qui
déploient dans dagster/citation-*) ne doivent jamais posséder — sinon
n’importe quelle code-location pourrait se forger une Application arbitraire.
On isole donc ce privilège :
cluster-apps:destinations=argocdseul ;namespaceResourceWhitelist=argoproj.io/Applicationuniquement ;clusterResourceWhitelist= vide.atlas(inchangé) :destinations=dagster/citation-*/marquez; whitelist*/*namespacée pour les workloads, mais pasargocd.
Moindre privilège, frontière nette — cohérent avec le rôle de garde-fou
multi-tenant de l’AppProject atlas.
Validation du code-location.manifest.yaml — intention (non codée)
cluster doit LIRE le manifeste de déclaration montant atlas
(ADR 0094 §3)
avant de rendre/pousser la déclaration d’Application. Le validateur
(futur, langage selon
ADR 0049 —
Python probable : parse YAML + graphe de dépendances) doit, échouer
bruyamment si :
contractVersioninconnue du contrat cluster (contract/endpoints.example.yaml) — c’est le remplacement de la copie figée (cause racine de l’audit) : déclaration + version validée, pas duplication silencieuse ;- une dépendance déclarée est absente :
database,secrets,buckets(OBC),migrations, ou unedependsOn.codeLocationsnon-ready; - les
resources(cpu/mem,diskbloc RBD) oubuckets(objet RGW, taille + classe) dépassent la marge de capacité réelle du cluster.
Le SHA revision du manifeste est la source unique du « quelque chose a
changé » : il alimente le targetRevision injecté (point 2 du seed) et le tag
d’image. Pas de détection magique.
Question ouverte (à trancher) : le validateur s’exécute-t-il (a) dans le seed bash avant le push (garde-fou côté cluster, simple), ou (b) en hook
PreSyncArgo CD côté fille (validation au plus près du déploiement) ? §3 de l’ADR penche pour « valider AVANT d’instancier » → plutôt (a).
Migration SQL (hook PreSync) — où se branche-t-elle ?
ADR 0094 §5 :
atlas FOURNIT le .sql (schéma métier) ; cluster l’APPLIQUE via un Job
Kubernetes en hook PreSync Argo CD — la migration tourne avant que les
workloads ne démarrent.
Question ouverte (à trancher) : le Job de migration vit-il (a) dans l’overlay atlas (le
.sqlet le Job sont au même endroit que le code, atlas les versionne, l’Applicationles réconcilie « pour rien » côté cluster), ou (b) dans la déclaration cluster (apps/<app>.yamlporte/référence le Job, cluster maîtrise l’orchestration mais doit accéder au.sqlfourni par atlas) ? Le partage de frontière (atlas fournit le.sql, cluster orchestre le Job) suggère un montage hybride :.sqlversionné atlas (overlay), Job PreSync rendu par cluster pointant ce.sql. À instruire au banc.
Validation locale (avant tout apply)
# Les YAML (Application/AppProject = CRD argoproj.io) — schémas absents en local,# d'où -ignore-missing-schemas (comme le check kubeconform du dépôt).kubeconform -strict -ignore-missing-schemas \ -schema-location default \ platform/argocd/app-of-apps/*.example.yaml \ platform/argocd/app-of-apps/apps/*.example.yaml
# Markdown, format YAML : repris par lefthook/CI (markdownlint, prettier, yamllint).Voir aussi
- ADR 0094 — frontière de déploiement applicatif (cet ADR).
- ADR 0022 — Argo CD applicatif / frontière infra-app.
- ADR 0044 — flux GitOps Gitea → Argo CD (le push de CONTENU, non inversé ici).
platform/argocd/— Argo CD + AppProject atlas.bench/lima/gitea-init.sh— le pattern de seed généralisé ici.