docs(runbooks): add "new web app" setup runbook under doc/runbooks/
Document, as a tree-docs tree, the end-to-end procedure to stand up a new web application on the Arcodange platform — a mechanic spread across the factory, tools and app repos with non-trivial ordering dependencies. Covers: Gitea repo creation (org-secret inheritance), Postgres DB + owner role (factory/postgres/iac), platform Vault declaration (gitea_cicd_<app> + policies, tools/hashicorp-vault/iac), the app Helm chart (VSO dynamic secrets via pgbouncer), the app Terraform (app_roles module), the CI workflows (tofu apply + image build, incl. the copy-pasted role pitfall), and ArgoCD registration (factory/argocd/values.yaml). Adds a naming- conventions concept page and an ordered checklist. Wires the legacy doc/adr "setup hello world web app" item and the factory README to the runbook. New docs live under doc/ (singular) per the PR #8 convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
21
doc/runbooks/README.md
Normal file
21
doc/runbooks/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
[Factory](../../README.md) > [Doc](../README.md) > **Runbooks**
|
||||
|
||||
# Runbooks
|
||||
|
||||
> **Scope :** procédures opérationnelles pas-à-pas de la plateforme Arcodange. Chaque runbook se lit du début à la fin et mène à un résultat vérifiable. Pour le *pourquoi* (décisions d'architecture), voir les [ADR](../adr/README.md).
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart LR
|
||||
classDef node fill:#059669,stroke:#047857,color:#fff
|
||||
OP["Opérateur"]:::node --> RB["Runbook<br>(procédure ordonnée)"]:::node --> RES["Résultat vérifiable<br>(app en ligne, etc.)"]:::node
|
||||
```
|
||||
|
||||
## Index
|
||||
|
||||
| Runbook | Résumé | Statut |
|
||||
|---|---|---|
|
||||
| [Nouvelle application web](new-web-app/README.md) | Créer une app web de zéro dans un nouveau dépôt Gitea : dépôt, base de données, Vault, chart Helm, Terraform, CI, ArgoCD | ✅ |
|
||||
|
||||
> [!TIP]
|
||||
> Pour ajouter un runbook : créer un dossier `kebab-case/` avec son `README.md` (front door : intro + diagramme + index), puis ajouter sa ligne ci-dessus.
|
||||
88
doc/runbooks/new-web-app/01-gitea-repo.md
Normal file
88
doc/runbooks/new-web-app/01-gitea-repo.md
Normal file
@@ -0,0 +1,88 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **1. Dépôt Gitea**
|
||||
|
||||
# 1. Créer le dépôt Gitea
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Downstream:** [2. Base de données](02-database.md), [3. Vault plateforme](03-vault-platform.md)
|
||||
> **Related:** [Conventions de nommage](conventions.md) · [6. Workflows CI](06-ci-workflows.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Tout part d'un dépôt Gitea nommé exactement `<app>` (voir [conventions](conventions.md)). Le créer **sous l'organisation `arcodange-org`** n'est pas un détail : c'est ce qui lui fait **hériter automatiquement des secrets Actions d'organisation** dont tous les workflows dépendent. Le dépôt contient un squelette fixe que les étapes suivantes viennent remplir.
|
||||
|
||||
## Pourquoi sous `arcodange-org`
|
||||
|
||||
Les workflows `.gitea/workflows/*` (voir [étape 6](06-ci-workflows.md)) référencent des secrets qui ne sont **pas** définis dans le dépôt mais au niveau de l'organisation et hérités par tous ses dépôts :
|
||||
|
||||
| Secret d'organisation | Usage |
|
||||
|---|---|
|
||||
| `HOMELAB_CA_CERT` | CA interne (base64) pour parler en TLS à `vault.arcodange.lab` |
|
||||
| `vault_oauth__sh_b64` | Script (base64) qui réalise l'échange OIDC Gitea → JWT Vault |
|
||||
| `PACKAGES_TOKEN` | Token de push vers le registre d'images `gitea.arcodange.lab` |
|
||||
|
||||
Ces secrets sont propagés par le rôle Ansible [`gitea_secret`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/ansible/arcodange/factory/roles/gitea_secret/defaults/main.yml) (`gitea_owner_type: organization`).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Un dépôt créé **hors** `arcodange-org` (par ex. sous l'org `arcodange`) n'hérite pas forcément de ces secrets. Si tu surcharges l'org (cf. `org:` à l'[étape 7](07-argocd-register.md)), assure-toi que les mêmes secrets y existent.
|
||||
|
||||
## Options pour créer le dépôt
|
||||
|
||||
| Méthode | Quand | Comment |
|
||||
|---|---|---|
|
||||
| UI Gitea | one-shot manuel | `https://gitea.arcodange.lab` → New Repository sous `arcodange-org` |
|
||||
| MCP Gitea | depuis un agent | outil `mcp__gitea__create_repo` (cf. règle « Gitea = MCP, pas `gh` » du guide global) |
|
||||
| Rôle Ansible `gitea_repo` | reproductible/inventaire | [`roles/gitea_repo`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/ansible/arcodange/factory/roles/gitea_repo/defaults/main.yml) |
|
||||
| Ressource Terraform `gitea_repository` | tout-en-IaC | dans [`factory/iac`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/iac) (provider `go-gitea/gitea` déjà configuré) |
|
||||
|
||||
## Squelette minimal du dépôt
|
||||
|
||||
```
|
||||
<app>/
|
||||
├── chart/ # chart Helm — ArgoCD déploie CE dossier (path: chart) → étape 4
|
||||
│ ├── Chart.yaml
|
||||
│ ├── values.yaml
|
||||
│ └── templates/
|
||||
├── iac/ # Terraform/OpenTofu de l'app → étape 5
|
||||
│ ├── providers.tf
|
||||
│ ├── backend.tf
|
||||
│ └── main.tf
|
||||
├── .gitea/workflows/ # CI (tofu apply + build image) → étape 6
|
||||
│ ├── vault.yaml
|
||||
│ └── dockerimage.yaml # uniquement si l'app build sa propre image
|
||||
├── Dockerfile # uniquement si l'app build sa propre image
|
||||
├── README.md
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Le dossier du chart **doit** s'appeler `chart/` et être à la racine : le template ArgoCD impose `path: chart` (cf. [étape 7](07-argocd-register.md)). Pas de `helm/`, pas de sous-dossier.
|
||||
|
||||
## `.gitignore` recommandé
|
||||
|
||||
Aligné sur les dépôts existants (exclut tout secret local) :
|
||||
|
||||
```gitignore
|
||||
.terraform
|
||||
.terraform.*
|
||||
.env
|
||||
*.key
|
||||
secrets/
|
||||
.DS_Store
|
||||
```
|
||||
|
||||
## Le bot `tofu_module_reader`
|
||||
|
||||
La CI de l'app clone le module Terraform partagé `tools` **en SSH** (cf. [étape 5](05-app-terraform.md)). C'est l'utilisateur restreint `tofu_module_reader` (créé dans [`factory/iac/gitea_tofu_ci_user.tf`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/iac/gitea_tofu_ci_user.tf), clé privée dans Vault `kvv1/gitea/tofu_module_reader`) qui sert d'identité de lecture. Rien à faire de spécial, mais le dépôt `tools` doit rester lisible par ce bot.
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- Le nom du dépôt = `<app>` **exactement** (kebab-case minuscule). Voir [conventions](conventions.md) — ce nom se propage partout.
|
||||
- Pas besoin de protéger `main` autrement que par la convention worktree/PR de l'équipe ; ArgoCD suit `HEAD`.
|
||||
|
||||
## Related
|
||||
|
||||
- [2. Base de données](02-database.md) — provisionner la base, en parallèle de l'étape 3.
|
||||
- [3. Vault plateforme](03-vault-platform.md) — déclarer l'app côté Vault, en parallèle de l'étape 2.
|
||||
- [6. Workflows CI](06-ci-workflows.md) — consomme les secrets d'org hérités ici.
|
||||
93
doc/runbooks/new-web-app/02-database.md
Normal file
93
doc/runbooks/new-web-app/02-database.md
Normal file
@@ -0,0 +1,93 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **2. Base de données**
|
||||
|
||||
# 2. Provisionner la base de données
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [1. Dépôt Gitea](01-gitea-repo.md)
|
||||
> **Downstream:** [5. Terraform de l'app](05-app-terraform.md)
|
||||
> **Related:** [3. Vault plateforme](03-vault-platform.md) · [4. Chart Helm](04-helm-chart.md) · [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
La base de l'app et son **rôle propriétaire** `<app>_role` sont créés par un Terraform **côté plateforme** (`factory/postgres/iac`), pas dans le dépôt de l'app. On ajoute simplement le nom de l'app à une liste, et la CI de `factory` applique. L'app, elle, ne se connectera **jamais** avec un mot de passe statique : elle obtiendra des identifiants éphémères de Vault (cf. [étape 4](04-helm-chart.md)) qui héritent de `<app>_role`.
|
||||
|
||||
## Action
|
||||
|
||||
Ajouter `"<app>"` au set `applications` de [`factory/postgres/iac/terraform.tfvars`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/postgres/iac/terraform.tfvars) :
|
||||
|
||||
```hcl
|
||||
applications = [
|
||||
"webapp",
|
||||
"erp",
|
||||
"crowdsec",
|
||||
"plausible",
|
||||
"dance-lessons-coach",
|
||||
"<app>", # ← ajouter
|
||||
]
|
||||
```
|
||||
|
||||
Puis pousser : la CI applique automatiquement (voir plus bas).
|
||||
|
||||
## Ce que ça crée
|
||||
|
||||
[`postgres/iac/main.tf`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/postgres/iac/main.tf) itère `for_each` sur le set et crée, **par app** :
|
||||
|
||||
| Ressource | Nom | Rôle |
|
||||
|---|---|---|
|
||||
| `postgresql_role` | `<app>_role` | Rôle **non-login**, propriétaire de la base |
|
||||
| `postgresql_grant_role` | `<app>_role` → `credentials_editor` (WITH ADMIN OPTION) | Laisse Vault rattacher les users dynamiques à ce rôle |
|
||||
| `postgresql_database` | `<app>` | La base (owner `<app>_role`, `template0`, `alter_object_ownership`) |
|
||||
| `postgresql_function` | `user_lookup()` (dans la base `<app>`) | Authentification pgbouncer (lit `pg_shadow`) |
|
||||
| `postgresql_grant` | EXECUTE sur `user_lookup` → `pgbouncer_auth` | Autorise pgbouncer à résoudre les users |
|
||||
|
||||
Extrait clé :
|
||||
|
||||
```hcl
|
||||
resource "postgresql_role" "app_role" {
|
||||
for_each = var.applications
|
||||
name = "${each.value}_role"
|
||||
login = false # non-login : ne sert que de "porteur de droits"
|
||||
}
|
||||
resource "postgresql_database" "app_db" {
|
||||
for_each = var.applications
|
||||
name = each.value
|
||||
owner = postgresql_role.app_role[each.value].name
|
||||
template = "template0"
|
||||
alter_object_ownership = true
|
||||
}
|
||||
```
|
||||
|
||||
## Comment c'est appliqué
|
||||
|
||||
Le workflow [`factory/.gitea/workflows/postgres.yaml`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/.gitea/workflows/postgres.yaml) se déclenche sur tout changement de `postgres/**/*.tf` ou `*.tfvars` :
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart LR
|
||||
classDef ci fill:#059669,stroke:#047857,color:#fff
|
||||
classDef db fill:#2563eb,stroke:#1e40af,color:#fff
|
||||
PUSH["push tfvars"]:::ci --> JWT["OIDC Gitea → JWT Vault<br>(role gitea_cicd)"]:::ci
|
||||
JWT --> READ["lit kvv1/postgres/credentials<br>→ TF_VAR_postgres_*"]:::ci
|
||||
READ --> APPLY["tofu apply postgres/iac"]:::ci
|
||||
APPLY --> DB["base ‹app› + ‹app›_role"]:::db
|
||||
```
|
||||
|
||||
Le provider PostgreSQL pointe l'hôte `192.168.1.202` (`sslmode=disable`, `superuser=true`) et s'authentifie avec le compte `credentials_editor`, dont les identifiants sont dans Vault à `kvv1/postgres/credentials_editor/credentials`.
|
||||
|
||||
## Le modèle de connexion (à retenir)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> L'application **ne se connecte pas** directement à Postgres avec un user fixe. Elle vise **`pgbouncer.tools:5432`** et utilise des **users dynamiques courts** émis par Vault, qui héritent de `<app>_role` (donc des droits sur la base `<app>`). C'est l'étape 4 (chart + VSO) et l'étape 5 (rôle Vault `creds/<app>`) qui câblent ça. Ici, on ne fait qu'établir *la base et le rôle propriétaire*.
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- `credentials_editor` est un compte unique partagé par toutes les apps, à fort privilège (il peut créer des rôles). Il sert aussi de compte de connexion au moteur Postgres de Vault (cf. [étape 3](03-vault-platform.md)).
|
||||
- La fonction `user_lookup()` est indispensable au mode `auth_query` de pgbouncer ; elle est `security_definer` et n'est exécutable que par `pgbouncer_auth`.
|
||||
|
||||
## Related
|
||||
|
||||
- [3. Vault plateforme](03-vault-platform.md) — la connexion Vault→Postgres réutilise `credentials_editor` ; à faire en parallèle.
|
||||
- [5. Terraform de l'app](05-app-terraform.md) — le module `app_roles` fait `GRANT <app>_role TO …` : il **exige** que `<app>_role` existe déjà (créé ici).
|
||||
- [4. Chart Helm](04-helm-chart.md) — où la connexion `pgbouncer.tools` + creds dynamiques est configurée.
|
||||
89
doc/runbooks/new-web-app/03-vault-platform.md
Normal file
89
doc/runbooks/new-web-app/03-vault-platform.md
Normal file
@@ -0,0 +1,89 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **3. Vault plateforme**
|
||||
|
||||
# 3. Déclarer l'app côté Vault plateforme
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [1. Dépôt Gitea](01-gitea-repo.md)
|
||||
> **Downstream:** [5. Terraform de l'app](05-app-terraform.md), [6. Workflows CI](06-ci-workflows.md)
|
||||
> **Related:** [2. Base de données](02-database.md) · [4. Chart Helm](04-helm-chart.md) · [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Avant que la CI de l'app puisse gérer ses propres secrets, Vault doit connaître l'app : il lui faut un **rôle JWT de CI** (`gitea_cicd_<app>`) pour que la pipeline s'authentifie, une **policy CI** (`<app>-ops`) qui l'autorise à créer ses rôles Postgres/K8s, et une **policy runtime** (`<app>`) que le pod utilisera. Tout ça est généré par un module, depuis une seule ligne ajoutée côté plateforme dans le dépôt `tools`.
|
||||
|
||||
## Action
|
||||
|
||||
Ajouter une entrée pour l'app au set `applications` de [`tools/hashicorp-vault/iac/terraform.tfvars`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/terraform.tfvars) :
|
||||
|
||||
```hcl
|
||||
applications = [
|
||||
{ name = "webapp" },
|
||||
{ name = "erp" },
|
||||
{ name = "<app>" }, # ← ajouter
|
||||
# options possibles :
|
||||
# {
|
||||
# name = "<app>"
|
||||
# ops_policies = ["factory__cf_r2_arcodange_tf"] # policies ops supplémentaires (ex. token Cloudflare)
|
||||
# service_account_names = ["cloudflared"] # SA additionnels autorisés à prendre la policy runtime
|
||||
# service_account_namespaces = ["tools"] # namespaces additionnels
|
||||
# },
|
||||
]
|
||||
```
|
||||
|
||||
Puis pousser : la CI [`tools/.gitea/workflows/vault.yaml`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/.gitea/workflows/vault.yaml) applique le Terraform.
|
||||
|
||||
## Ce que ça crée
|
||||
|
||||
Le module [`app_policy`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/app_policy/main.tf) (appelé en `for_each` depuis [`main.tf`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/main.tf)) crée, **par app** :
|
||||
|
||||
| Ressource Vault | Nom | À quoi ça sert |
|
||||
|---|---|---|
|
||||
| `vault_jwt_auth_backend_role` | `gitea_cicd_<app>` | **Identité de la CI** : la pipeline de l'app s'y authentifie (mount `gitea_jwt`, `user_claim=email`, `bound_audiences=[gitea_app_id]`) |
|
||||
| `vault_policy` (ops) | `<app>-ops` | Droits CI : créer `postgres/roles/<app>*`, `auth/kubernetes/role/<app>*`, éditer `kvv2/<app>/*`, lire les secrets de bootstrap google/gitea |
|
||||
| `vault_identity_group` | `<app>-ops` | Groupe Vault rattachant les comptes Gitea à la policy ops |
|
||||
| `vault_policy` (runtime) | `<app>` | Droits **du pod** : lire `kvv2/data/<app>/*` et `postgres/creds/<app>*` |
|
||||
|
||||
Extraits clés :
|
||||
|
||||
```hcl
|
||||
resource "vault_jwt_auth_backend_role" "gitea_jwt_cicd" {
|
||||
backend = data.vault_auth_backend.gitea_jwt.path # "gitea_jwt"
|
||||
role_name = "gitea_cicd_${local.name}"
|
||||
token_policies = concat(["default"], var.ops_policies)
|
||||
bound_audiences = [var.gitea_app_id]
|
||||
user_claim = "email"
|
||||
role_type = "jwt"
|
||||
}
|
||||
|
||||
resource "vault_policy" "app" { # policy runtime du pod
|
||||
name = local.name # = "<app>"
|
||||
policy = data.vault_policy_document.app.hcl # read kvv2/data/<app>/* + postgres/creds/<app>*
|
||||
}
|
||||
```
|
||||
|
||||
## Prérequis plateforme (déjà là)
|
||||
|
||||
Ces fondations vivent dans [`tools/hashicorp-vault/iac/main.tf`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/main.tf) et n'ont **pas** à être recréées :
|
||||
|
||||
- mounts `kvv2` (KV v2), `postgres` (moteur de bases de données), `transit` (cache VSO), auth `kubernetes` ;
|
||||
- la **connexion Vault→Postgres** (`vault_database_secret_backend_connection`) qui se connecte à `pgbouncer.tools:5432/postgres` avec le compte `credentials_editor` (issu de l'[étape 2](02-database.md)) ;
|
||||
- `var.gitea_app_id` = l'id de l'application OAuth2 Gitea, réglé une fois au setup ([`gitea_oidc_auth.yml`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml)).
|
||||
|
||||
## Pourquoi cette étape vient avant la CI de l'app
|
||||
|
||||
> [!IMPORTANT]
|
||||
> C'est ici que `gitea_cicd_<app>` **naît**. Le `iac/providers.tf` de l'app ([étape 5](05-app-terraform.md)) et le step `vault-action` du workflow ([étape 6](06-ci-workflows.md)) s'authentifient avec ce rôle. S'il n'existe pas encore, la toute première exécution de la CI de l'app échoue à l'authentification Vault. **Appliquer cette étape (et l'[étape 2](02-database.md)) avant de pousser le `iac/` de l'app.**
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- Découpage des privilèges : la policy **`<app>-ops`** (CI, large) est distincte de la policy **`<app>`** (runtime, en lecture seule sur ses propres secrets). Le pod ne peut jamais créer de rôles.
|
||||
- `ops_policies` permet d'octroyer à la CI des droits transverses (ex. `cms` lit un token Cloudflare R2 via `factory__cf_r2_arcodange_tf`).
|
||||
|
||||
## Related
|
||||
|
||||
- [2. Base de données](02-database.md) — fournit `credentials_editor`, réutilisé par la connexion Vault→Postgres.
|
||||
- [5. Terraform de l'app](05-app-terraform.md) — s'authentifie avec `gitea_cicd_<app>` et crée `creds/<app>` + le rôle K8s `<app>`.
|
||||
- [6. Workflows CI](06-ci-workflows.md) — le step `vault-action` et `tofu apply` utilisent `gitea_cicd_<app>`.
|
||||
- [4. Chart Helm](04-helm-chart.md) — le pod utilise la policy runtime `<app>` via son ServiceAccount.
|
||||
183
doc/runbooks/new-web-app/04-helm-chart.md
Normal file
183
doc/runbooks/new-web-app/04-helm-chart.md
Normal file
@@ -0,0 +1,183 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **4. Chart Helm**
|
||||
|
||||
# 4. Le chart Helm de l'application
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [5. Terraform de l'app](05-app-terraform.md) (crée les rôles Vault que le chart consomme)
|
||||
> **Downstream:** [7. Enregistrement ArgoCD](07-argocd-register.md) (déploie ce chart)
|
||||
> **Related:** [2. Base de données](02-database.md) · [3. Vault plateforme](03-vault-platform.md) · [6. Workflows CI](06-ci-workflows.md) · [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Le dossier `chart/` est l'**unité de déploiement** : c'est lui qu'ArgoCD applique (`path: chart`). Il décrit le Deployment, le Service, l'Ingress, et surtout le câblage des secrets via le **Vault Secrets Operator (VSO)** : pas un mot de passe en clair, des identifiants Postgres **dynamiques** injectés à l'exécution. On part d'un `helm create <app>` puis on ajoute les CRD Vault, la ConfigMap, et on ajuste l'ingress.
|
||||
|
||||
## Structure du chart
|
||||
|
||||
```
|
||||
chart/
|
||||
├── Chart.yaml # name: <app>, version, appVersion (= tag image par défaut)
|
||||
├── values.yaml # image, service, ingress, serviceAccount, autoscaling…
|
||||
└── templates/
|
||||
├── deployment.yaml # consomme vso-db-credentials + secretkv
|
||||
├── service.yaml # ClusterIP
|
||||
├── ingress.yaml # Traefik (voir patterns ci-dessous)
|
||||
├── serviceaccount.yaml # SA <app> (serviceAccount.create: true)
|
||||
├── config.yaml # ConfigMap : env non-secrets (host DB = pgbouncer)
|
||||
├── vaultauth.yaml # VaultAuth : SA <app> ↔ rôle Vault <app>
|
||||
├── vaultdynamicsecret.yaml # creds Postgres dynamiques (postgres/creds/<app>)
|
||||
├── vaultsecret.yaml # config statique (kvv2/<app>/config)
|
||||
├── hpa.yaml # désactivé par défaut
|
||||
├── _helpers.tpl # name/fullname/labels
|
||||
└── NOTES.txt
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Bootstrap : `helm create <app>` génère deployment/service/ingress/serviceaccount/hpa/_helpers/NOTES. Il reste à **ajouter** `config.yaml` (ConfigMap), les 3 CRD Vault (`vaultauth`, `vaultdynamicsecret`, `vaultsecret`), et à **ajuster** `values.yaml` (image, ingress) + `deployment.yaml` (envFrom). Copier ceux d'[`erp/chart`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart) ou [`webapp/chart`](https://gitea.arcodange.lab/arcodange-org/webapp/src/branch/main/chart) est le plus rapide.
|
||||
|
||||
## Connexion à la base : via pgbouncer, jamais en direct
|
||||
|
||||
La ConfigMap pointe **`pgbouncer.tools`** (le pooler dans le namespace `tools`), port 5432, base `<app>`. Pas de host Postgres en dur, pas d'utilisateur statique.
|
||||
|
||||
```yaml
|
||||
# chart/templates/config.yaml — exemple erp
|
||||
data:
|
||||
DOLI_DB_TYPE: pgsql
|
||||
DOLI_DB_HOST: pgbouncer.tools # ← le pooler, pas postgres directement
|
||||
DOLI_DB_HOST_PORT: !!str 5432
|
||||
DOLI_DB_NAME: erp # = <app>
|
||||
```
|
||||
|
||||
```yaml
|
||||
# chart/templates/config.yaml — exemple webapp (chaîne de connexion)
|
||||
data:
|
||||
DATABASE_URL: postgres://pgbouncer_auth:pgbouncer_auth@pgbouncer.tools/postgres?sslmode=disable
|
||||
```
|
||||
|
||||
Le **vrai** user/mot de passe vient du Secret K8s `vso-db-credentials` (voir ci-dessous), pas de la ConfigMap.
|
||||
|
||||
## Les secrets via VSO (le cœur)
|
||||
|
||||
Trois CRD du Vault Secrets Operator (extraits du chart `erp`) :
|
||||
|
||||
```yaml
|
||||
# vaultauth.yaml — authentifie le pod auprès de Vault
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: erp # = rôle K8s Vault <app> (créé à l'étape 5)
|
||||
serviceAccount: {{ include "erp.serviceAccountName" . }}
|
||||
audiences: [vault]
|
||||
```
|
||||
|
||||
```yaml
|
||||
# vaultdynamicsecret.yaml — identifiants Postgres dynamiques
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultDynamicSecret
|
||||
spec:
|
||||
mount: postgres
|
||||
path: creds/erp # = postgres/creds/<app>
|
||||
destination:
|
||||
create: true
|
||||
name: vso-db-credentials # Secret K8s consommé par le Deployment
|
||||
rolloutRestartTargets:
|
||||
- kind: Deployment
|
||||
name: {{ include "erp.fullname" . }} # redémarre le pod à chaque rotation
|
||||
vaultAuthRef: auth
|
||||
```
|
||||
|
||||
```yaml
|
||||
# vaultsecret.yaml — config statique (mots de passe admin initiaux, etc.)
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
spec:
|
||||
type: kv-v2
|
||||
mount: kvv2
|
||||
path: erp/config # = kvv2/<app>/config (rempli à l'étape 5)
|
||||
destination:
|
||||
name: secretkv
|
||||
create: true
|
||||
refreshAfter: 24h
|
||||
vaultAuthRef: auth
|
||||
```
|
||||
|
||||
Le Deployment consomme ensuite `vso-db-credentials` (clés `username`/`password`) et `secretkv` (via `envFrom`/`secretKeyRef`).
|
||||
|
||||
### Flux runtime
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart LR
|
||||
classDef pod fill:#b45309,stroke:#92400e,color:#fff
|
||||
classDef vault fill:#7c3aed,stroke:#6d28d9,color:#fff
|
||||
classDef db fill:#2563eb,stroke:#1e40af,color:#fff
|
||||
|
||||
SA["Pod · SA ‹app›"]:::pod
|
||||
VA["VaultAuth<br>role ‹app› (k8s)"]:::vault
|
||||
VDS["VaultDynamicSecret<br>postgres/creds/‹app›"]:::vault
|
||||
VSS["VaultStaticSecret<br>kvv2/‹app›/config"]:::vault
|
||||
SEC["Secret K8s<br>vso-db-credentials + secretkv"]:::pod
|
||||
PGB["pgbouncer.tools:5432"]:::db
|
||||
DB["base ‹app›<br>(user dynamique → ‹app›_role)"]:::db
|
||||
|
||||
SA --> VA
|
||||
VA --> VDS
|
||||
VA --> VSS
|
||||
VDS --> SEC
|
||||
VSS --> SEC
|
||||
SEC --> SA
|
||||
SA --> PGB --> DB
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `serviceAccount.create` doit valoir `true` dans `values.yaml` : c'est ce SA `<app>` que `VaultAuth` lie au rôle Vault `<app>`. Sans lui, pas d'authentification, donc pas de creds DB.
|
||||
|
||||
## Ingress : interne `.lab` vs public `.fr`
|
||||
|
||||
Deux patterns selon l'exposition voulue (annotations Traefik dans `values.yaml`) :
|
||||
|
||||
| | Interne (`.lab`) — ex. `erp` | Public (`.fr`) — ex. `webapp` |
|
||||
|---|---|---|
|
||||
| `entrypoints` | `websecure` | `web` |
|
||||
| TLS | `router.tls: "true"` + `certresolver: letsencrypt` | (terminé en amont) |
|
||||
| Middleware | `localIp@file` (restreint au LAN) | `kube-system-crowdsec@kubernetescrd` (WAF) |
|
||||
| `nodeSelector` | — | `kubernetes.io/hostname: pi1` (garde l'IP source, point d'entrée réseau) |
|
||||
| Hôte | `<app>.arcodange.lab` | `<app>.arcodange.fr` |
|
||||
|
||||
```yaml
|
||||
# values.yaml — ingress interne .lab (extrait erp)
|
||||
ingress:
|
||||
enabled: true
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.middlewares: localIp@file
|
||||
hosts:
|
||||
- host: erp.arcodange.lab
|
||||
paths: [{ path: /, pathType: Prefix }]
|
||||
```
|
||||
|
||||
## Image
|
||||
|
||||
| Cas | `image.repository` | Exemple |
|
||||
|---|---|---|
|
||||
| Image **maison** (build dans le dépôt) | `gitea.arcodange.lab/arcodange-org/<app>` | `webapp` (cf. [étape 6](06-ci-workflows.md)) |
|
||||
| Image **publique** | l'image upstream | `erp` → `dolibarr/dolibarr` |
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- Si l'app stocke des fichiers, ajouter un `pvc.yaml` (`storageClassName: longhorn`, `accessModes: [ReadWriteMany]`, annotation `helm.sh/resource-policy: keep` pour survivre à un `helm uninstall`) — cf. `erp/chart/templates/pvc.yaml`.
|
||||
- Les CRD VSO supposent que le VSO tourne dans le cluster (déployé par `tools`) et que le rôle K8s `<app>` + le rôle `creds/<app>` existent (étape 5).
|
||||
|
||||
## Related
|
||||
|
||||
- [5. Terraform de l'app](05-app-terraform.md) — crée `postgres/creds/<app>`, le rôle K8s `<app>` et remplit `kvv2/<app>/config` que ces CRD consomment.
|
||||
- [2. Base de données](02-database.md) — la base `<app>` et `pgbouncer.tools` ciblés ici.
|
||||
- [3. Vault plateforme](03-vault-platform.md) — la policy runtime `<app>` qu'utilise `VaultAuth`.
|
||||
- [6. Workflows CI](06-ci-workflows.md) — construit l'image référencée par `image.repository`.
|
||||
- [Référence VSO/secrets faisant autorité](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/README.md) — explication détaillée VaultConnection/VaultAuth/VaultDynamicSecret côté `tools` (à ne pas dupliquer ici).
|
||||
96
doc/runbooks/new-web-app/05-app-terraform.md
Normal file
96
doc/runbooks/new-web-app/05-app-terraform.md
Normal file
@@ -0,0 +1,96 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **5. Terraform de l'app**
|
||||
|
||||
# 5. Le Terraform de l'application
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [2. Base de données](02-database.md), [3. Vault plateforme](03-vault-platform.md)
|
||||
> **Downstream:** [4. Chart Helm](04-helm-chart.md), [6. Workflows CI](06-ci-workflows.md)
|
||||
> **Related:** [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Le `iac/` du dépôt déclare les ressources Vault **propres à l'app** : un rôle Postgres dynamique (`postgres/creds/<app>`), un rôle d'authentification Kubernetes (`<app>`), et les secrets de config KV. Le gros du travail est fait par un **module partagé** (`app_roles`, dans `tools`) ; le dépôt se contente de l'appeler avec son nom et d'ajouter ses secrets spécifiques.
|
||||
|
||||
## Les trois fichiers
|
||||
|
||||
### `providers.tf` — s'authentifier à Vault avec le rôle CI de l'app
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
vault = { source = "vault", version = "4.4.0" }
|
||||
}
|
||||
}
|
||||
provider "vault" {
|
||||
address = "https://vault.arcodange.lab"
|
||||
auth_login_jwt { # JWT fourni par la CI via TERRAFORM_VAULT_AUTH_JWT
|
||||
mount = "gitea_jwt"
|
||||
role = "gitea_cicd_<app>" # ← créé à l'étape 3 ; DOIT exister avant le 1er apply
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `backend.tf` — état distant sur GCS, préfixe par app
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "arcodange-tf"
|
||||
prefix = "<app>/main" # ← un préfixe d'état dédié par app
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `main.tf` — appeler le module partagé + secrets de l'app
|
||||
|
||||
```hcl
|
||||
module "app_roles" {
|
||||
source = "git::ssh://git@192.168.1.202:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main"
|
||||
name = "<app>"
|
||||
# database = "<autre>" # optionnel ; par défaut = name
|
||||
}
|
||||
|
||||
# Exemple : secrets de config statiques de l'app, écrits dans kvv2/<app>/config
|
||||
resource "vault_kv_secret_v2" "config" {
|
||||
mount = module.app_roles.mount_paths.kvv2 # "kvv2"
|
||||
name = format("%sconfig", module.app_roles.kvv2_path_prefix) # "<app>/config"
|
||||
data_json = jsonencode({
|
||||
# … clés propres à l'app (ex. erp : DOLI_ADMIN_LOGIN/PASSWORD, DOLI_INSTANCE_UNIQUE_ID) …
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Ce que le module `app_roles` crée
|
||||
|
||||
[`modules/app_roles/main.tf`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/app_roles/main.tf) :
|
||||
|
||||
| Ressource | Effet |
|
||||
|---|---|
|
||||
| `vault_database_secret_backend_role` → `postgres/creds/<app>` | À chaque demande : `CREATE ROLE "…" LOGIN PASSWORD … VALID UNTIL … ; GRANT <app>_role TO "…"`. Le user éphémère **hérite** de `<app>_role` (donc des droits sur la base). À la révocation : `REASSIGN OWNED … TO <app>_role` + `REVOKE`. |
|
||||
| `vault_kubernetes_auth_backend_role` → `<app>` | Lie le SA `<app>` du namespace `<app>` aux policies `default` + `<app>` (TTL 3600 s). C'est ce que `VaultAuth` cible (étape 4). |
|
||||
|
||||
Sorties utiles : `mount_paths` (`{k8s, pg, kvv2}`), `kvv2_path_prefix` (`<app>/`), `name`, `database`.
|
||||
|
||||
## Dépendances (à respecter)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Ce Terraform **suppose** que deux choses existent déjà :
|
||||
> - le rôle Postgres `<app>_role` (créé à l'[étape 2](02-database.md)) — sinon le `GRANT <app>_role TO …` du rôle dynamique est invalide ;
|
||||
> - le rôle JWT `gitea_cicd_<app>` et la policy `<app>` (créés à l'[étape 3](03-vault-platform.md)) — sinon l'authentification du provider échoue / le rôle K8s ne peut référencer la policy.
|
||||
>
|
||||
> Ce `iac/` est appliqué par la CI de l'app, voir [étape 6](06-ci-workflows.md). Ne pas pousser ce dossier avant d'avoir appliqué les étapes 2 et 3.
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- Le module est récupéré **en SSH** via le bot `tofu_module_reader` (cf. [étape 1](01-gitea-repo.md)) ; `?ref=main&depth=1` épingle la branche et limite le clone.
|
||||
- L'état est isolé par `prefix = "<app>/main"` : pas de collision entre apps dans le bucket `arcodange-tf`.
|
||||
- `erp` et `webapp` montrent deux variantes : `erp` passe par `module "app_roles"` ; `webapp` inline encore les ressources (`vault_database_secret_backend_role` + `vault_kubernetes_auth_backend_role`) — préférer le module pour une nouvelle app.
|
||||
|
||||
## Related
|
||||
|
||||
- [2. Base de données](02-database.md) — fournit `<app>_role`.
|
||||
- [3. Vault plateforme](03-vault-platform.md) — fournit `gitea_cicd_<app>` et la policy `<app>`.
|
||||
- [4. Chart Helm](04-helm-chart.md) — consomme `postgres/creds/<app>`, le rôle K8s `<app>` et `kvv2/<app>/config`.
|
||||
- [6. Workflows CI](06-ci-workflows.md) — applique ce `iac/`.
|
||||
108
doc/runbooks/new-web-app/06-ci-workflows.md
Normal file
108
doc/runbooks/new-web-app/06-ci-workflows.md
Normal file
@@ -0,0 +1,108 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **6. Workflows CI**
|
||||
|
||||
# 6. Les workflows CI (`.gitea/workflows/`)
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [1. Dépôt Gitea](01-gitea-repo.md) (secrets d'org), [3. Vault plateforme](03-vault-platform.md) (`gitea_cicd_<app>`)
|
||||
> **Related:** [4. Chart Helm](04-helm-chart.md) · [5. Terraform de l'app](05-app-terraform.md) · [7. Enregistrement ArgoCD](07-argocd-register.md) · [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deux workflows Gitea Actions vivent dans le dépôt : **`vault.yaml`** applique le Terraform de l'app (`iac/`) en s'authentifiant à Vault via OIDC, et **`dockerimage.yaml`** (optionnel) construit l'image et la pousse au registre Gitea. Le déploiement lui-même n'est pas dans la CI : c'est ArgoCD qui s'en charge ([étape 7](07-argocd-register.md)).
|
||||
|
||||
## `vault.yaml` — appliquer le `iac/` de l'app
|
||||
|
||||
Déclenché sur tout changement de `iac/*.tf`. Deux jobs : obtenir un JWT depuis Gitea, puis `tofu apply`.
|
||||
|
||||
```yaml
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push: { paths: ['iac/*.tf'] }
|
||||
pull_request: { paths: ['iac/*.tf'] }
|
||||
|
||||
# job 1 : échange OIDC Gitea → JWT (script base64 fourni en secret d'org)
|
||||
# run: echo -n "${{ secrets.vault_oauth__sh_b64 }}" | base64 -d | bash
|
||||
|
||||
# job 2 : lire les secrets de bootstrap puis appliquer
|
||||
- name: read vault secret
|
||||
uses: https://gitea.arcodange.lab/arcodange-org/vault-action.git@main
|
||||
with:
|
||||
url: https://vault.arcodange.lab
|
||||
caCertificate: ${{ secrets.HOMELAB_CA_CERT }}
|
||||
jwtGiteaOIDC: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }}
|
||||
role: gitea_cicd_<app> # ← le rôle JWT de l'app (étape 3)
|
||||
method: jwt
|
||||
path: gitea_jwt
|
||||
secrets: |
|
||||
kvv1/google/credentials credentials | GOOGLE_BACKEND_CREDENTIALS ;
|
||||
kvv1/gitea/tofu_module_reader ssh_private_key | TERRAFORM_SSH_KEY ;
|
||||
- uses: actions/checkout@v4
|
||||
- name: terraform apply
|
||||
uses: dflook/terraform-apply@v1
|
||||
with: { path: iac, auto_approve: true }
|
||||
```
|
||||
|
||||
Les deux secrets lus servent au backend (clé GCS `GOOGLE_BACKEND_CREDENTIALS`) et au clone du module partagé en SSH (`TERRAFORM_SSH_KEY`, cf. [étape 5](05-app-terraform.md)).
|
||||
|
||||
> [!WARNING]
|
||||
> **Piège du `role:` copié-collé.** Le `role:` du step `vault-action` **et** le `role` de `iac/providers.tf` doivent tous deux être `gitea_cicd_<app>`. L'exemple `erp` porte encore `role: gitea_cicd_webapp` dans son [`vault.yaml`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/.gitea/workflows/vault.yaml) (reliquat de copier-coller) alors que son `providers.tf` utilise bien `gitea_cicd_erp`. Vérifie et aligne sur le nom de **ton** app, sinon la CI lit/écrit avec la mauvaise identité.
|
||||
|
||||
## `dockerimage.yaml` — construire l'image (si image maison)
|
||||
|
||||
À n'ajouter **que** si l'app build sa propre image (pas pour une image publique comme `erp`/Dolibarr). Déclenché au push sur `main`, en ignorant `README.md` et `chart/**` (changer le chart ne reconstruit pas l'image).
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push: { branches: [main], paths-ignore: ['README.md', 'chart/**'] }
|
||||
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
steps:
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: gitea.arcodange.lab
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.PACKAGES_TOKEN }} # secret d'org (étape 1)
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
TAGS="latest ${{ github.ref_name }}"
|
||||
docker build -t app .
|
||||
for TAG in $TAGS; do
|
||||
docker tag app gitea.arcodange.lab/${{ github.repository }}:$TAG
|
||||
docker push gitea.arcodange.lab/${{ github.repository }}:$TAG
|
||||
done
|
||||
```
|
||||
|
||||
L'image atterrit donc en `gitea.arcodange.lab/arcodange-org/<app>:latest` — exactement ce que `image.repository` du chart référence ([étape 4](04-helm-chart.md)). Un `Dockerfile` multi-stage à la racine convient (cf. [`webapp/Dockerfile`](https://gitea.arcodange.lab/arcodange-org/webapp/src/branch/main/Dockerfile)).
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart TB
|
||||
classDef ci fill:#059669,stroke:#047857,color:#fff
|
||||
classDef out fill:#7c3aed,stroke:#6d28d9,color:#fff
|
||||
PUSH["push sur main"]:::ci
|
||||
PUSH -->|"iac/*.tf modifié"| TF["vault.yaml<br>OIDC → JWT → tofu apply iac/"]:::ci
|
||||
PUSH -->|"code modifié"| IMG["dockerimage.yaml<br>build + push image"]:::ci
|
||||
TF --> VR["creds/‹app› + rôle K8s ‹app› + KV"]:::out
|
||||
IMG --> REG["registre gitea.arcodange.lab/arcodange-org/‹app›"]:::out
|
||||
```
|
||||
|
||||
## Déploiement automatique sur nouvelle image
|
||||
|
||||
Pour qu'ArgoCD redéploie quand une nouvelle image est poussée, on n'ajoute **rien** dans la CI : ce sont les annotations `argocd-image-updater` posées à l'[étape 7](07-argocd-register.md) (stratégie `digest`) qui surveillent le tag `latest`.
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
- `concurrency: cancel-in-progress` est activé sur les deux workflows : un nouveau push annule le run précédent sur la même ref.
|
||||
- Le `vault-action` est lui-même un dépôt Gitea (`arcodange-org/vault-action`) épinglé `@main`.
|
||||
|
||||
## Related
|
||||
|
||||
- [3. Vault plateforme](03-vault-platform.md) — d'où vient `gitea_cicd_<app>`.
|
||||
- [5. Terraform de l'app](05-app-terraform.md) — ce que `vault.yaml` applique.
|
||||
- [4. Chart Helm](04-helm-chart.md) — `image.repository` = l'image poussée ici.
|
||||
- [7. Enregistrement ArgoCD](07-argocd-register.md) — déploie, et porte les annotations d'auto-update.
|
||||
91
doc/runbooks/new-web-app/07-argocd-register.md
Normal file
91
doc/runbooks/new-web-app/07-argocd-register.md
Normal file
@@ -0,0 +1,91 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **7. Enregistrement ArgoCD**
|
||||
|
||||
# 7. Enregistrer l'app dans ArgoCD
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [4. Chart Helm](04-helm-chart.md), [6. Workflows CI](06-ci-workflows.md)
|
||||
> **Related:** [Conventions de nommage](conventions.md) · [Checklist](08-checklist.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
ArgoCD fonctionne en **app-of-apps** : le chart `factory/argocd` lit une liste d'applications dans son `values.yaml` et génère une ressource `Application` par entrée. Enregistrer l'app = ajouter son nom à cette liste. ArgoCD se charge ensuite de cloner le dépôt, déployer `chart/` dans le namespace `<app>`, et resynchroniser à chaque push.
|
||||
|
||||
## Action
|
||||
|
||||
Ajouter `<app>` sous `gitea_applications` dans [`factory/argocd/values.yaml`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/argocd/values.yaml) :
|
||||
|
||||
```yaml
|
||||
gitea_applications:
|
||||
webapp: { … }
|
||||
erp:
|
||||
annotations: {}
|
||||
<app>: # ← cas simple
|
||||
annotations: {}
|
||||
```
|
||||
|
||||
Variante avec **auto-déploiement** sur nouvelle image (recommandé pour une image maison) :
|
||||
|
||||
```yaml
|
||||
<app>:
|
||||
annotations:
|
||||
argocd-image-updater.argoproj.io/image-list: <app>=gitea.arcodange.lab/arcodange-org/<app>:latest
|
||||
argocd-image-updater.argoproj.io/<app>.update-strategy: digest
|
||||
```
|
||||
|
||||
Options supplémentaires :
|
||||
|
||||
| Champ | Quand l'utiliser | Effet |
|
||||
|---|---|---|
|
||||
| `org: arcodange` | dépôt hors `arcodange-org` | change le `repoURL` (défaut `arcodange-org`) |
|
||||
| `syncPolicy: …` | contrôle manuel | surcharge la policy (défaut : `automated {prune, selfHeal}`) |
|
||||
|
||||
## Ce que ça génère
|
||||
|
||||
Le template [`argocd/templates/apps.yaml`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/argocd/templates/apps.yaml) rend, pour chaque entrée :
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: <app>
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://gitea.arcodange.lab/arcodange-org/<app> # <org> = arcodange-org par défaut
|
||||
targetRevision: HEAD
|
||||
path: chart # ← d'où l'exigence du dossier chart/
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: <app> # = nom de l'app
|
||||
syncPolicy:
|
||||
automated: { prune: true, selfHeal: true }
|
||||
syncOptions: [ CreateNamespace=true ] # le namespace est créé tout seul
|
||||
```
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart LR
|
||||
classDef gitops fill:#7c3aed,stroke:#6d28d9,color:#fff
|
||||
classDef k8s fill:#2563eb,stroke:#1e40af,color:#fff
|
||||
VAL["values.yaml<br>gitea_applications.‹app›"]:::gitops --> APP["Application ArgoCD<br>‹app›"]:::gitops
|
||||
APP -->|"path: chart, HEAD"| SYNC["sync du dépôt<br>arcodange-org/‹app›"]:::gitops
|
||||
SYNC --> NS["namespace ‹app›<br>(CreateNamespace=true)"]:::k8s
|
||||
NS --> DEP["Deployment + Service + Ingress + CRD Vault"]:::k8s
|
||||
```
|
||||
|
||||
## Notes / contraintes
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `path: chart` et `namespace: <app>` sont **déduits du nom**, pas configurables par entrée. C'est pourquoi le dossier doit s'appeler `chart/` ([étape 1](01-gitea-repo.md)) et le nom doit être cohérent partout ([conventions](conventions.md)).
|
||||
|
||||
- Le chart `factory/argocd` est lui-même réconcilié par ArgoCD (app-of-apps racine) : committer `values.yaml` sur `main` suffit à faire apparaître/synchroniser la nouvelle `Application`. Pas de `kubectl apply` manuel.
|
||||
- `prune: true` + `selfHeal: true` : ArgoCD supprime ce qui n'est plus dans le chart et réécrase les dérives manuelles. En tenir compte avant tout `kubectl edit`.
|
||||
|
||||
## Related
|
||||
|
||||
- [4. Chart Helm](04-helm-chart.md) — le contenu déployé (le dossier `chart/`).
|
||||
- [6. Workflows CI](06-ci-workflows.md) — les annotations `argocd-image-updater` collaborent avec l'image poussée.
|
||||
- [8. Checklist](08-checklist.md) — vérifier que l'`Application` passe `Healthy`/`Synced`.
|
||||
77
doc/runbooks/new-web-app/08-checklist.md
Normal file
77
doc/runbooks/new-web-app/08-checklist.md
Normal file
@@ -0,0 +1,77 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **8. Checklist**
|
||||
|
||||
# 8. Checklist & Definition of Done
|
||||
|
||||
> **Status:** ✅ Active
|
||||
> **Upstream:** [7. Enregistrement ArgoCD](07-argocd-register.md)
|
||||
> **Related:** [README du runbook](README.md) · [Conventions de nommage](conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Récapitulatif imprimable de toute la procédure, dans l'ordre des dépendances. À cocher au fur et à mesure ; chaque ligne renvoie à sa page détaillée.
|
||||
|
||||
## Ordre des dépendances
|
||||
|
||||
```
|
||||
[01] Dépôt Gitea ──┬──> [02] base + <app>_role ──┐
|
||||
└──> [03] gitea_cicd_<app> ────┤ (02 et 03 avant 05)
|
||||
▼
|
||||
[04+05+06] chart/ + iac/ + .gitea/ ── push → CI
|
||||
▼
|
||||
[07] ArgoCD ── déploie ── Runtime
|
||||
```
|
||||
|
||||
## Checklist
|
||||
|
||||
**Préparation**
|
||||
- [ ] Nom `<app>` choisi (kebab-case minuscule), cohérent partout — [conventions](conventions.md)
|
||||
|
||||
**1 · Dépôt** — [détails](01-gitea-repo.md)
|
||||
- [ ] Dépôt `arcodange-org/<app>` créé (hérite `HOMELAB_CA_CERT`, `vault_oauth__sh_b64`, `PACKAGES_TOKEN`)
|
||||
- [ ] Squelette en place : `chart/`, `iac/`, `.gitea/workflows/`, `.gitignore` (+ `Dockerfile` si image maison)
|
||||
|
||||
**2 · Base de données** — [détails](02-database.md)
|
||||
- [ ] `"<app>"` ajouté à `factory/postgres/iac/terraform.tfvars`
|
||||
- [ ] CI `factory` « Postgres » verte → base `<app>` + rôle `<app>_role` créés
|
||||
|
||||
**3 · Vault plateforme** — [détails](03-vault-platform.md)
|
||||
- [ ] `{ name = "<app>" }` ajouté à `tools/hashicorp-vault/iac/terraform.tfvars`
|
||||
- [ ] CI `tools` « Vault » verte → `gitea_cicd_<app>`, policies `<app>` / `<app>-ops` créées
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Ne pas pousser le `iac/` de l'app (étape 5/6) tant que **2 et 3** ne sont pas appliquées : la CI Terraform de l'app en dépend.
|
||||
|
||||
**4 · Chart Helm** — [détails](04-helm-chart.md)
|
||||
- [ ] `Chart.yaml` (`name: <app>`), `values.yaml` (image, ingress, `serviceAccount.create: true`)
|
||||
- [ ] `config.yaml` pointe `pgbouncer.tools` / base `<app>`
|
||||
- [ ] `vaultauth.yaml` (role `<app>`), `vaultdynamicsecret.yaml` (`creds/<app>`), `vaultsecret.yaml` (`kvv2/<app>/config`)
|
||||
- [ ] Ingress choisi : interne `.lab` (websecure + letsencrypt + `localIp@file`) ou public `.fr` (web + crowdsec + `nodeSelector pi1`)
|
||||
|
||||
**5 · Terraform de l'app** — [détails](05-app-terraform.md)
|
||||
- [ ] `providers.tf` : role `gitea_cicd_<app>`
|
||||
- [ ] `backend.tf` : prefix `<app>/main`
|
||||
- [ ] `main.tf` : `module "app_roles"` (`name = "<app>"`) + secrets `kvv2/<app>/config`
|
||||
|
||||
**6 · Workflows CI** — [détails](06-ci-workflows.md)
|
||||
- [ ] `vault.yaml` : `role: gitea_cicd_<app>` **aligné** (pas un reliquat copié-collé) ⚠️
|
||||
- [ ] `dockerimage.yaml` + `Dockerfile` (si image maison) → push `gitea.arcodange.lab/arcodange-org/<app>`
|
||||
- [ ] Push → CI « vault » verte (`creds/<app>` + rôle K8s `<app>` + KV créés), CI « image » verte
|
||||
|
||||
**7 · ArgoCD** — [détails](07-argocd-register.md)
|
||||
- [ ] `<app>` ajouté sous `gitea_applications` dans `factory/argocd/values.yaml` (+ annotations image-updater si voulu)
|
||||
- [ ] Commit sur `main` → `Application` `<app>` apparaît dans ArgoCD
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `Application` ArgoCD `<app>` = **Synced** + **Healthy**
|
||||
- [ ] Pod `Running` dans le namespace `<app>` (SA `<app>`)
|
||||
- [ ] Secret K8s `vso-db-credentials` présent et **roté** par VSO (TTL ~1 h) ; le pod redémarre à la rotation
|
||||
- [ ] L'app répond sur son ingress (`<app>.arcodange.lab` ou `<app>.arcodange.fr`)
|
||||
- [ ] Connexion DB OK via `pgbouncer.tools` avec un user dynamique héritant de `<app>_role`
|
||||
|
||||
## Related
|
||||
|
||||
- [README du runbook](README.md) — vue d'ensemble + carte de bout en bout.
|
||||
- [Conventions de nommage](conventions.md) — la cohérence du nom `<app>`, source de la plupart des ratés.
|
||||
104
doc/runbooks/new-web-app/README.md
Normal file
104
doc/runbooks/new-web-app/README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > **Nouvelle application web**
|
||||
|
||||
# Mettre en service une nouvelle application web
|
||||
|
||||
> **Last Updated:** 2026-05-31
|
||||
> **Status:** ✅ Procédure courante
|
||||
> **Related:** [Conventions de nommage](conventions.md) · [Checklist](08-checklist.md) · [ADR CI/CD](../../adr/03_cicd_gitea_action_argocd.md) · [ADR Vault](../../adr/04_tool_hashicorp_vault.md)
|
||||
|
||||
## C'est quoi ?
|
||||
|
||||
Ce runbook décrit, de zéro, comment faire vivre une nouvelle application web sur la plateforme Arcodange. Le pattern est **GitOps** : l'app habite son propre dépôt Gitea, sa base de données et ses accès Vault sont provisionnés par Terraform/OpenTofu, et **ArgoCD** déploie son chart Helm dans un namespace dédié. Les identifiants Postgres ne sont jamais écrits en clair : ils sont générés à la volée par Vault et injectés dans le pod par le **Vault Secrets Operator (VSO)**.
|
||||
|
||||
La mécanique est répartie sur **trois dépôts** — le dépôt plateforme [`factory`](https://gitea.arcodange.lab/arcodange-org/factory), le dépôt des services partagés [`tools`](https://gitea.arcodange.lab/arcodange-org/tools), et le **nouveau dépôt de l'app** — avec des **dépendances d'ordre** strictes (voir plus bas). Les exemples de référence sont [`erp`](https://gitea.arcodange.lab/arcodange-org/erp) (image publique + DB) et [`webapp`](https://gitea.arcodange.lab/arcodange-org/webapp) (image maison + DB).
|
||||
|
||||
**Sert à :**
|
||||
|
||||
1. Créer un dépôt Gitea et son squelette (`chart/`, `iac/`, `.gitea/workflows/`).
|
||||
2. Provisionner la base de données, son rôle propriétaire, et les accès Vault (statiques + dynamiques).
|
||||
3. Déployer l'app via ArgoCD et l'exposer derrière Traefik (interne `.lab` ou public `.fr` + CrowdSec).
|
||||
|
||||
## Carte de bout en bout
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme': 'base'}}%%
|
||||
flowchart TB
|
||||
classDef step fill:#2563eb,stroke:#1e40af,color:#fff
|
||||
classDef plat fill:#059669,stroke:#047857,color:#fff
|
||||
classDef gitops fill:#7c3aed,stroke:#6d28d9,color:#fff
|
||||
classDef run fill:#b45309,stroke:#92400e,color:#fff
|
||||
|
||||
REPO["1 · Dépôt Gitea<br>arcodange-org/‹app›"]:::step
|
||||
DB["2 · factory/postgres/iac<br>base ‹app› + rôle ‹app›_role"]:::plat
|
||||
VAULT["3 · tools/hashicorp-vault/iac<br>gitea_cicd_‹app› + policies ‹app› / ‹app›-ops"]:::plat
|
||||
CONTENT["4·5·6 · chart/ + iac/ + .gitea/<br>push → CI build image & tofu apply"]:::step
|
||||
ARGO["7 · factory/argocd/values.yaml<br>→ Application ArgoCD (ns ‹app›)"]:::gitops
|
||||
POD["Runtime · Pod(SA ‹app›) → VSO → Vault<br>creds/‹app› → pgbouncer.tools → base ‹app›"]:::run
|
||||
|
||||
REPO --> DB
|
||||
REPO --> VAULT
|
||||
DB --> CONTENT
|
||||
VAULT --> CONTENT
|
||||
CONTENT --> ARGO
|
||||
ARGO --> POD
|
||||
```
|
||||
|
||||
## Ordre des opérations (le point le plus important)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Les étapes ne sont pas interchangeables. Le rôle JWT de CI `gitea_cicd_<app>` (étape 3) et le rôle Postgres `<app>_role` (étape 2) doivent **exister avant** que la CI Terraform de l'app (étape 6 appliquant l'étape 5) ne s'exécute — sinon l'authentification Vault de la CI échoue, ou le module `app_roles` n'a pas de `<app>_role` à qui rattacher les credentials dynamiques.
|
||||
|
||||
```
|
||||
[01] Dépôt Gitea sous arcodange-org (hérite les secrets CI d'org)
|
||||
│
|
||||
├──> [02] factory/postgres/iac → base <app> + <app>_role + user_lookup()
|
||||
│
|
||||
└──> [03] tools/hashicorp-vault/iac → gitea_cicd_<app> (JWT CI) + policies <app> / <app>-ops
|
||||
│ (02 et 03 indépendants entre eux, mais TOUS DEUX avant 05)
|
||||
▼
|
||||
[04+05+06] Contenu du dépôt : chart/ + iac/ + .gitea/workflows/ (+ Dockerfile)
|
||||
│ push → CI « dockerimage » build l'image · CI « vault » applique iac/
|
||||
│ → creds/<app> (rôle DB dynamique) + rôle K8s <app> + secrets KV
|
||||
▼
|
||||
[07] factory/argocd/values.yaml → ArgoCD crée l'Application → déploie le chart dans le namespace <app>
|
||||
▼
|
||||
Runtime : Pod(SA <app>) → VSO → VaultAuth(role <app>) → creds/<app>
|
||||
→ user PG dynamique héritant de <app>_role → pgbouncer.tools → base <app>
|
||||
```
|
||||
|
||||
## Prérequis plateforme (déjà en place)
|
||||
|
||||
Ces fondations existent et ne sont **pas** à refaire pour chaque app :
|
||||
|
||||
| Brique | Où | Rôle |
|
||||
|---|---|---|
|
||||
| Mounts Vault `kvv2`, `postgres`, `transit`, auth `kubernetes` | [`tools/hashicorp-vault/iac/main.tf`](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/main.tf) | Moteurs de secrets + auth K8s |
|
||||
| Connexion Vault→Postgres (via `pgbouncer.tools`, user `credentials_editor`) | idem | Permet à Vault d'émettre des users PG dynamiques |
|
||||
| Rôle JWT de bootstrap `gitea_cicd` + app OAuth2 Gitea (`gitea_app_id`) | [`gitea_oidc_auth.yml`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml) | Échange OIDC Gitea → JWT Vault dans la CI |
|
||||
| Bot `tofu_module_reader` (clé SSH dans `kvv1/gitea/tofu_module_reader`) | [`factory/iac/gitea_tofu_ci_user.tf`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/iac/gitea_tofu_ci_user.tf) | Laisse la CI cloner le module partagé `tools` en SSH |
|
||||
| Secrets Actions d'organisation (`HOMELAB_CA_CERT`, `vault_oauth__sh_b64`, `PACKAGES_TOKEN`) | org Gitea `arcodange-org` | Hérités par tout dépôt de l'org |
|
||||
|
||||
## Index des étapes
|
||||
|
||||
| # | Page | Ce qu'on y fait | Statut |
|
||||
|---|---|---|---|
|
||||
| — | [Conventions de nommage](conventions.md) | Le nom `<app>` réutilisé à l'identique partout (à lire en premier) | ✅ |
|
||||
| 01 | [Dépôt Gitea](01-gitea-repo.md) | Créer le dépôt sous `arcodange-org` + squelette | ✅ |
|
||||
| 02 | [Base de données](02-database.md) | `factory/postgres/iac` → base `<app>` + rôle `<app>_role` | ✅ |
|
||||
| 03 | [Vault plateforme](03-vault-platform.md) | `tools/hashicorp-vault/iac` → `gitea_cicd_<app>` + policies | ✅ |
|
||||
| 04 | [Chart Helm](04-helm-chart.md) | Le chart de l'app (DB via pgbouncer, secrets VSO, ingress) | ✅ |
|
||||
| 05 | [Terraform de l'app](05-app-terraform.md) | `iac/` → module `app_roles` (creds dynamiques + rôle K8s) | ✅ |
|
||||
| 06 | [Workflows CI](06-ci-workflows.md) | `.gitea/workflows/` : `tofu apply` + build image | ✅ |
|
||||
| 07 | [Enregistrement ArgoCD](07-argocd-register.md) | `factory/argocd/values.yaml` → Application + déploiement | ✅ |
|
||||
| 08 | [Checklist](08-checklist.md) | Récapitulatif ordonné + definition of done | ✅ |
|
||||
|
||||
## Légende de statut
|
||||
|
||||
✅ actif · 🟡 dégradé/beta · 🔴 critique/EOL · ⚠️ problème connu · ❌ désactivé
|
||||
|
||||
## Comment éditer ce runbook
|
||||
|
||||
1. **Ajouter une page** → la créer depuis le template tree-docs adéquat **et** ajouter sa ligne dans l'index ci-dessus.
|
||||
2. **Garder les liens croisés bidirectionnels** → toute dépendance citée dans une page (`Upstream`/`Downstream`) doit avoir sa réciproque sur l'autre page.
|
||||
3. **Mettre à jour `Last Updated:`** ci-dessus après tout changement de structure.
|
||||
4. Les exemples cités (`erp`, `webapp`) sont vivants : revérifier les snippets contre le code réel avant de s'y fier aveuglément.
|
||||
55
doc/runbooks/new-web-app/conventions.md
Normal file
55
doc/runbooks/new-web-app/conventions.md
Normal file
@@ -0,0 +1,55 @@
|
||||
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **Conventions de nommage**
|
||||
|
||||
# Conventions de nommage
|
||||
|
||||
> **Pourquoi cette page.** Un seul nom — `<app>` — est réutilisé à l'identique dans une dizaine de systèmes (Gitea, Postgres, Vault, Kubernetes, ArgoCD, GCS, DNS). Toutes les étapes du runbook en dépendent ; on le centralise ici pour ne pas le ré-expliquer partout.
|
||||
> **Audience.** Quiconque crée ou audite une app sur la plateforme.
|
||||
> **Status.** Actif · revu le 2026-05-31.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Choisis **un** identifiant `<app>` en **kebab-case minuscule** (`webapp`, `erp`, `dance-lessons-coach`, `url-shortener`). Ce nom devient, **sans variation**, la clé de toutes les ressources. Une seule faute de frappe quelque part casse la chaîne (auth Vault, rattachement DB, sync ArgoCD).
|
||||
|
||||
## Le nom `<app>` dans chaque système
|
||||
|
||||
| Système | Identifiant dérivé de `<app>` | Exemple (`erp`) | Source de vérité |
|
||||
|---|---|---|---|
|
||||
| Dépôt Gitea | `arcodange-org/<app>` (ou `arcodange/<app>` si `org` surchargé) | `arcodange-org/erp` | [argocd/templates/apps.yaml](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/argocd/templates/apps.yaml) |
|
||||
| Base PostgreSQL | `<app>` | `erp` | [postgres/iac/main.tf](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/postgres/iac/main.tf) |
|
||||
| Rôle propriétaire PG (non-login) | `<app>_role` | `erp_role` | postgres/iac/main.tf |
|
||||
| Rôle DB dynamique Vault | `postgres/creds/<app>` | `postgres/creds/erp` | [modules/app_roles/main.tf](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/app_roles/main.tf) |
|
||||
| Rôle d'auth Kubernetes (Vault) | `<app>` | `erp` | modules/app_roles/main.tf |
|
||||
| Policy Vault **runtime** (pod) | `<app>` | `erp` | [modules/app_policy/main.tf](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault/iac/modules/app_policy/main.tf) |
|
||||
| Policy Vault **CI** (ops) | `<app>-ops` | `erp-ops` | modules/app_policy/main.tf |
|
||||
| Rôle JWT de CI (Vault) | `gitea_cicd_<app>` | `gitea_cicd_erp` | modules/app_policy/main.tf |
|
||||
| Groupe d'identité Vault | `<app>-ops` | `erp-ops` | modules/app_policy/main.tf |
|
||||
| Secret KV de config | `kvv2/<app>/config` | `kvv2/erp/config` | modules/app_roles (sortie `kvv2_path_prefix`) |
|
||||
| Namespace Kubernetes | `<app>` | `erp` | apps.yaml (`CreateNamespace=true`) |
|
||||
| ServiceAccount Kubernetes | `<app>` | `erp` | [chart/templates/serviceaccount.yaml](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/templates/serviceaccount.yaml) |
|
||||
| Application ArgoCD | `<app>` | `erp` | apps.yaml |
|
||||
| Préfixe d'état OpenTofu (GCS) | `<app>/main` | `erp/main` | [iac/backend.tf](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/iac/backend.tf) |
|
||||
| Domaine interne | `<app>.arcodange.lab` | `erp.arcodange.lab` | chart/values.yaml (ingress) |
|
||||
| Domaine public | `<app>.arcodange.fr` | `webapp.arcodange.fr` | chart/values.yaml (ingress) |
|
||||
|
||||
## Pourquoi cette uniformité est structurante
|
||||
|
||||
Les briques se « branchent » entre elles **par convention de nom**, pas par configuration explicite :
|
||||
|
||||
- Le module `app_roles` génère un user PG dynamique avec `GRANT <app>_role TO …` → il **suppose** que `<app>_role` (créé à l'[étape 2](02-database.md)) porte exactement ce nom.
|
||||
- Le `VaultDynamicSecret` du chart lit `postgres/creds/<app>` → il **suppose** que le rôle Vault (créé à l'[étape 5](05-app-terraform.md)) porte exactement `<app>`.
|
||||
- L'`Application` ArgoCD déduit `repoURL=.../<app>`, `path=chart`, `namespace=<app>` du seul nom → le dépôt ([étape 1](01-gitea-repo.md)) et le namespace doivent matcher.
|
||||
|
||||
✅ **Utilise un nom court, stable, kebab-case** dès le départ.
|
||||
❌ **N'introduis pas** de variantes (`my_app` vs `my-app`, `MyApp`, pluriels) : rien ne te préviendra, l'app échouera silencieusement à se connecter ou à se déployer.
|
||||
|
||||
## Références croisées
|
||||
|
||||
- [01 · Dépôt Gitea](01-gitea-repo.md) — fixe `<app>` comme nom de dépôt sous `arcodange-org`.
|
||||
- [02 · Base de données](02-database.md) — crée `<app>` et `<app>_role`.
|
||||
- [03 · Vault plateforme](03-vault-platform.md) — crée `gitea_cicd_<app>`, policies `<app>` / `<app>-ops`.
|
||||
- [04 · Chart Helm](04-helm-chart.md) — référence `<app>`, `creds/<app>`, `kvv2/<app>/config`.
|
||||
- [05 · Terraform de l'app](05-app-terraform.md) — appelle `app_roles` avec `name=<app>`.
|
||||
- [06 · Workflows CI](06-ci-workflows.md) — s'authentifie avec `gitea_cicd_<app>`.
|
||||
- [07 · Enregistrement ArgoCD](07-argocd-register.md) — déclare `<app>` dans `gitea_applications`.
|
||||
Reference in New Issue
Block a user