Le runbook new-web-app couvre l'app autonome (dépôt/base/Vault/namespace propres, tout nommé <app>). Il manquait le cas du SERVICE COMPAGNON : un second service qui partage le namespace — et parfois le stack Vault/DB — d'une app existante (API cœur à côté de son front, façade d'analyse). Deux précédents vivants non documentés : kadans-jobs (namespace seul) et kadans-api (namespace + base + Vault). - Nouvelle page 09-service-compagnon.md : compagnon vs app autonome ; les deux formes (sans état / partage Vault+DB) ; le PIÈGE du VaultAuth manquant quand l'app primaire ne consomme pas Vault (front statique) → le compagnon pose son propre VaultAuth mais avec le rôle+SA du PRIMAIRE ; carte, précédents, delta de checklist. - 07-argocd-register.md : ajoute la ligne `namespace:` aux options (elle existait dans values.yaml — kadans-jobs — mais n'était pas documentée) ; corrige le callout qui affirmait le namespace « non configurable ». - conventions.md : note l'exception compagnon à la règle « tout est <app> ». - README.md : entrée 09 dans l'index + Last Updated. Vérifié : VaultAuth erp nommé `auth` ; connexion via pgbouncer.tools ; liens internes tous résolus. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_013ws8L74dVZmp97Wu36fm8j
8.1 KiB
Factory > Doc > Runbooks > Nouvelle application web > 9. Service compagnon
9. Service compagnon (namespace partagé)
Status: ✅ Active Upstream: 7. Enregistrement ArgoCD (le champ
namespace:utilisé ici) Related: Conventions de nommage · 4. Chart Helm · 2. Base de données · Checklist
Summary
Tous les autres chapitres décrivent une app autonome : son dépôt, sa base, son stack Vault, son namespace, son ServiceAccount — tout porte le même nom <app>. Mais certains services ne sont pas une app à part entière : ce sont des compagnons d'une app existante. Une API cœur à côté de son front, une façade d'analyse qui sert une app — ils vivent dans le même namespace que l'app qu'ils servent et réutilisent son identité (Vault, base, ServiceAccount) plutôt que d'en provisionner une nouvelle.
Ce chapitre décrit ce raccourci et son piège principal : la convention « tout est nommé <app> » (conventions) ne tient plus pour un compagnon — ses identités Vault/DB/SA restent celles de l'app primaire, pas les siennes.
Compagnon ou app autonome ?
Fais un compagnon quand le service partage réellement l'identité et les données de l'app primaire. Fais une app autonome (chapitres 1→8) dès qu'il lui faut sa propre base ou ses propres accès.
| Prends un compagnon si… | Prends une app autonome si… |
|---|---|
| Il lit/écrit la base de l'app primaire (même données) | Il lui faut sa propre base |
| Il partage le cycle de vie de l'app (déployé avec, pour elle) | Il a un cycle de vie indépendant |
| Une seule origine CORS / un seul domaine logique | Domaine et exposition propres |
Un compagnon garde son propre dépôt Gitea et son propre chart (donc sa propre image, sa CI de build, son ingress). Ce qu'il ne refait pas : base, rôles Vault, ServiceAccount, namespace.
Les deux formes de compagnon
A. Compagnon sans état — juste le namespace partagé
Le service n'a ni base ni secret Vault (ex. façade d'analyse kadans-jobs). Il suffit de le déployer dans le namespace de l'app primaire. Une seule chose le distingue d'une app normale à l'étape 7 : la clé namespace:.
# factory/argocd/values.yaml
kadans-jobs:
org: arcodange
namespace: kadans # ← sinon ArgoCD déduirait « kadans-jobs »
annotations:
argocd-image-updater.argoproj.io/image-list: kadans-jobs=…/kadans-jobs:latest
argocd-image-updater.argoproj.io/kadans-jobs.update-strategy: digest
Son chart ne contient que deployment / service / ingress. Pas d'iac/, rien dans postgres/iac/terraform.tfvars, pas de CRD Vault.
B. Compagnon partageant le stack Vault/DB de l'app primaire
Le service lit la base de l'app primaire avec ses creds dynamiques (ex. API cœur kadans-api sur la base kadans). Il réutilise, à l'identique, tout ce qui a été provisionné pour le primaire :
| Ressource | Elle porte le nom du primaire, jamais du compagnon |
|---|---|
| Base PostgreSQL | kadans (pas kadans-api) |
| Rôle DB dynamique Vault | postgres/creds/kadans |
| Rôle d'auth K8s Vault | kadans (bound au SA kadans / ns kadans) |
| Policy KV runtime | kadans (accès kvv2/kadans/*) |
| ServiceAccount K8s | kadans (créé par le chart du primaire) |
Donc le compagnon ne fait PAS l'étape 2 (pas de nouvelle base), PAS l'étape 5 (pas de nouvel app_roles, pas d'iac/), et ne s'ajoute PAS à la liste applications de postgres / tools. Son VaultDynamicSecret pointe simplement le mount/chemin du primaire :
# chart du compagnon — vaultdynamicsecret.yaml
spec:
mount: postgres
path: creds/kadans # = le rôle DB du PRIMAIRE
vaultAuthRef: kadans # cf. le VaultAuth ci-dessous
Le host DB reste pgbouncer.tools, base = celle du primaire (étape 4 « via pgbouncer, jamais en direct »).
Important
Le piège du VaultAuth manquant. Le
VaultDynamicSecreta besoin d'un CRVaultAuthdans le namespace (VSO le résout par nom, dans le même namespace). Deux cas :
- Le primaire consomme déjà Vault → son chart a déjà posé un
VaultAuth(nomméauthpar convention, étape 4). Le compagnon le référence (vaultAuthRef: auth) et ne crée rien.- Le primaire ne consomme PAS Vault (front statique, aucune base — cas de
kadans) → personne n'a créé deVaultAuthdans le namespace. Le compagnon doit alors poser le sien, mais pointant le rôle et le SA du primaire :# chart du compagnon — vaultauth.yaml (cas « primaire sans Vault ») apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: kadans # ou « auth » ; l'important est spec.kubernetes.* namespace: {{ .Release.Namespace }} spec: vaultConnectionRef: default # VaultConnection cluster-wide (VSO) method: kubernetes mount: kubernetes kubernetes: role: kadans # ← rôle K8s Vault du PRIMAIRE serviceAccount: kadans # ← SA du PRIMAIRE (créé par SON chart) audiences: [vault]C'est la seule raison pour laquelle le SA et le rôle du VaultAuth ne portent pas le nom du service qui le déploie. Ne crée pas un second SA
kadans-api: le rôle K8s Vaultkadansn'accepte que le SAkadans.
Carte
%%{init: {'theme': 'base'}}%%
flowchart TB
classDef prim fill:#059669,stroke:#047857,color:#fff
classDef comp fill:#b45309,stroke:#92400e,color:#fff
classDef sh fill:#7c3aed,stroke:#6d28d9,color:#fff
subgraph NS["namespace « kadans »"]
SA["ServiceAccount kadans<br>(chart du PRIMAIRE)"]:::sh
VA["VaultAuth<br>role kadans · SA kadans"]:::sh
PRIM["Deployment kadans<br>(front, sans Vault)"]:::prim
COMP["Deployment kadans-api<br>(compagnon, lit la base)"]:::comp
end
VA -->|"vaultAuthRef"| VDS["VaultDynamicSecret<br>postgres/creds/kadans"]:::sh
COMP --> VA
VDS --> COMP
COMP --> PGB["pgbouncer.tools → base kadans"]:::sh
SA -.->|"identité empruntée"| VA
Précédents vivants
| Compagnon | Primaire | Partage | CRD Vault dans son chart |
|---|---|---|---|
kadans-jobs |
kadans |
namespace seul (sans état) | aucun |
kadans-api |
kadans |
namespace + base + Vault | vaultauth (le primaire n'a pas de Vault) + vaultdynamicsecret |
Delta de checklist
Par rapport à la checklist standard, un compagnon saute :
- ❌ Étape 2 — pas de nouvelle base ni de rôle propriétaire.
- ❌ Étape 5 — pas d'
iac/, pas d'app_roles, rien à ajouter aux listesapplications.
…et ajuste :
- ✅ Étape 4 —
VaultDynamicSecretpointecreds/<primaire>; poser unvaultauth.yamlseulement si le primaire ne consomme pas déjà Vault (rôle + SA = ceux du primaire). - ✅ Étape 7 — ajouter
namespace: <primaire>à l'entréegitea_applications. - ✅ Ordre de merge : le fix/chart du compagnon avant son enregistrement ArgoCD, pour que la 1ʳᵉ synchro parte d'un chart correct.
Related
- 7. Enregistrement ArgoCD — le champ
namespace:qui place le compagnon dans le namespace du primaire. - 4. Chart Helm — la forme des CRD VSO et la connexion via
pgbouncer.tools. - Conventions de nommage — la règle « tout est
<app>» que ce chapitre nuance pour un compagnon. - Référence VSO faisant autorité — VaultConnection/VaultAuth/VaultDynamicSecret côté
tools.