|
|
|
@@ -0,0 +1,143 @@
|
|
|
|
|
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **9. Service compagnon**
|
|
|
|
|
|
|
|
|
|
# 9. Service compagnon (namespace partagé)
|
|
|
|
|
|
|
|
|
|
> **Status:** ✅ Active
|
|
|
|
|
> **Upstream:** [7. Enregistrement ArgoCD](07-argocd-register.md) (le champ `namespace:` utilisé ici)
|
|
|
|
|
> **Related:** [Conventions de nommage](conventions.md) · [4. Chart Helm](04-helm-chart.md) · [2. Base de données](02-database.md) · [Checklist](08-checklist.md)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 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](conventions.md)) **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](07-argocd-register.md) : la clé **`namespace:`**.
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
# 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](02-database.md) (pas de nouvelle base), **PAS** l'[étape 5](05-app-terraform.md) (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 :
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
# 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](04-helm-chart.md) « via pgbouncer, jamais en direct »).
|
|
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
|
> **Le piège du VaultAuth manquant.** Le `VaultDynamicSecret` a besoin d'un CR **`VaultAuth`** dans 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é `auth` par convention, [étape 4](04-helm-chart.md)). 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éé de `VaultAuth` dans le namespace. Le compagnon doit alors **poser le sien**, mais pointant le rôle et le SA du **primaire** :
|
|
|
|
|
>
|
|
|
|
|
> ```yaml
|
|
|
|
|
> # 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 Vault `kadans` n'accepte que le SA `kadans`.
|
|
|
|
|
|
|
|
|
|
## Carte
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
%%{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`](https://gitea.arcodange.lab/arcodange/kadans-jobs) | `kadans` | namespace seul (sans état) | aucun |
|
|
|
|
|
| [`kadans-api`](https://gitea.arcodange.lab/arcodange/kadans-api) | `kadans` | namespace + base + Vault | `vaultauth` (le primaire n'a pas de Vault) + `vaultdynamicsecret` |
|
|
|
|
|
|
|
|
|
|
## Delta de checklist
|
|
|
|
|
|
|
|
|
|
Par rapport à la [checklist standard](08-checklist.md), un compagnon **saute** :
|
|
|
|
|
|
|
|
|
|
- ❌ [Étape 2](02-database.md) — pas de nouvelle base ni de rôle propriétaire.
|
|
|
|
|
- ❌ [Étape 5](05-app-terraform.md) — pas d'`iac/`, pas d'`app_roles`, rien à ajouter aux listes `applications`.
|
|
|
|
|
|
|
|
|
|
…et **ajuste** :
|
|
|
|
|
|
|
|
|
|
- ✅ [Étape 4](04-helm-chart.md) — `VaultDynamicSecret` pointe `creds/<primaire>` ; poser un `vaultauth.yaml` **seulement** si le primaire ne consomme pas déjà Vault (rôle + SA = ceux du primaire).
|
|
|
|
|
- ✅ [Étape 7](07-argocd-register.md) — ajouter `namespace: <primaire>` à l'entrée `gitea_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](07-argocd-register.md) — le champ `namespace:` qui place le compagnon dans le namespace du primaire.
|
|
|
|
|
- [4. Chart Helm](04-helm-chart.md) — la forme des CRD VSO et la connexion via `pgbouncer.tools`.
|
|
|
|
|
- [Conventions de nommage](conventions.md) — la règle « tout est `<app>` » que ce chapitre nuance pour un compagnon.
|
|
|
|
|
- [Référence VSO faisant autorité](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/README.md) — VaultConnection/VaultAuth/VaultDynamicSecret côté `tools`.
|