diff --git a/CHANGELOG.md b/CHANGELOG.md index c2768e3..1c35716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # CHANGELOG — Set-OPS +## 2026-09-10 (13) — D-88 : le noeud du gabarit, point unique de la REPRODUCTION + +Question de l'exploitant : *« le modele vit sur vishnu, les clones sont sur asgard — qu' +arriverait-il si vishnu tombait ? »*. Mesuree, la reponse se coupe en deux. + +**Les donnees survivent.** + + pool CephNVMe size=3 min_size=2 + OSD sur asgard, gandalf, vishnu + base-9006-disk-0, base-9006-disk-1 repliquees + +L'image reste lisible avec un noeud en moins, et les quatorze VM d'un ecosysteme tournent +ailleurs sur leurs propres disques Ceph : elles ne s'apercoivent de rien. + +**La reproduction, non.** La configuration du gabarit porte le nom du noeud dans son chemin +meme — `/etc/pve/nodes/vishnu/qemu-server/9006.conf` — et le clonage appelle +`nodes/vishnu/qemu/9006/clone`. Noeud eteint, API muette, **aucune VM nouvelle ne peut +naitre**. Or la reproduction est ce que ce depot existe pour garantir. + +### La decision est d'ASSUMER la dependance et de la rendre COURTE + +Pas de la supprimer. Depuis que le disque du gabarit vit sur un stockage partage (migration +du matin), la remise en route est un **deplacement de fichier de configuration** — quelques +minutes, aucun mouvement de donnees — suivi de la declaration `gabarit.noeud`. Sur un +stockage local, il aurait fallu recopier 16 Go ou refabriquer le gabarit. + +*Un benefice de la migration sur Ceph qu'on n'avait pas cherche : elle a raccourci une panne +qu'on n'avait pas encore nommee.* + +### Trois endroits, parce qu'un seul ne suffit pas + +- **D-88** dans les decisions : la dependance est nommee et son perimetre borne ; +- **`runbooks-exploitation.md` §7** : la manoeuvre, dans l'ordre, avec ce qui casse si on + l'inverse — deplacer sans declarer laisse `gabarit_etat` en ecart, declarer sans deplacer + fait echouer le clonage ; +- **le plan du site**, dans le bloc `gabarit` lui-meme : c'est la que l'exploitant lit + `noeud: vishnu`, et c'est donc la que l'avertissement doit vivre. + +### Ce qui n'est PAS fait, et qui est dit + +**Rien ne MESURE cette dependance.** `gabarit_etat` compare le declare au reel ; il ne +demande pas si le noeud du gabarit heberge autre chose que le gabarit. Deux remedes de fond +restent ouverts : deplacer le gabarit la ou vivent deja les VM (ce qui ne supprime pas le +point unique mais cesse d'en avoir DEUX), ou une garde qui refuse quand la reproduction +depend d'un noeud qui ne porte rien d'autre. + +*Une dependance qu'on documente sans la mesurer reste une dependance qu'on decouvrira au +mauvais moment.* + ## 2026-09-10 (12) — Un fichier vide existe, et une sonde pour les correctifs ### La garde « fichier entier » — et pourquoi il a fallu DEUX corrections diff --git a/docs/carte-set-ops.md b/docs/carte-set-ops.md index 98ff451..165a1e1 100644 --- a/docs/carte-set-ops.md +++ b/docs/carte-set-ops.md @@ -28,7 +28,7 @@ README de rôles). Cette page comble ces deux trous. | documents | 40 | `docs/*.md` | | pièces d'audit | 41 | `docs/audit/*` | | unités de wiki | 27 | `wiki/*.md` | -| décisions en vigueur | 84 | lignes `\| **D-nn** \|` de `decisions-architecture.md` | +| décisions en vigueur | 85 | lignes `\| **D-nn** \|` de `decisions-architecture.md` | | décisions renversées | 3 | lignes `\| **D-nn** —` du même document | ## 1. À lire d'abord (dans l'ordre) diff --git a/docs/decisions-architecture.md b/docs/decisions-architecture.md index 7361d49..2eea413 100644 --- a/docs/decisions-architecture.md +++ b/docs/decisions-architecture.md @@ -61,6 +61,7 @@ sont les seules vérifiables. | **D-85** | **cloud-init naît avec la VM et ne lui survit pas** | cloud-init n'est pas un logiciel d'installation : c'est une **source de vérité externe**, qui se réveille à *chaque* démarrage et relit le lecteur attaché par l'hyperviseur — lequel peut redéfinir comptes, clés SSH autorisées, mots de passe et réseau. Sur une machine que le plan possède, c'est un **second maître** : le plan ne le décrit pas, `make valider` ne le mesure pas, et il parle en premier. Sa tâche est pourtant finie à la première seconde — c'est parce qu'il a **réussi** à poser l'adresse et les clés qu'Ansible a pu entrer. **Trois moitiés, qui se défont séparément** : le **gabarit** le garde (sans lui un clone ne naît pas — P56) ; le **socle** ne l'installe plus (le garder produisait un va-et-vient à chaque déploiement : le socle installe, le durcissement retire, deux `changed` par passage) ; le **durcissement** le retire (`cloud_init_retrait`, en dernier). **Ce qui rend le retrait sûr est mesuré, pas supposé** (2026-09-09, `obs-01`) : `/etc/network/interfaces.d/50-cloud-init` n'appartient à aucun paquet — `dpkg -S` ne le trouve pas — et le `postrm` ne le nomme jamais, même en `purge`. L'adresse survit. Le rôle le **vérifie** malgré tout, avant et après : une VM qui perd ce fichier ne se plaint pas, elle repart sans adresse et plus personne ne peut entrer pour le constater. **⚠ CE QUE CETTE DÉCISION NE FERME PAS — et il faut le dire, sinon elle se lit comme une émancipation qu'elle n'est pas.** Retirer cloud-init **n'ôte aucun pouvoir à l'hébergeur**. `qemu-guest-agent` est au gabarit (P56 : il doit y être — c'est par lui que `creer-vm` confirme la matérialisation sans entrer chez le tenant), et l'API Proxmox expose sur son dos, sur toute VM vivante de la flotte, un pouvoir **strictement plus grand** que le lecteur cloud-init : `exec`, `file-write`, `file-read`, `set-user-password`, `shutdown` (relevé le 2026-09-09 sur `edge-mta-01`, jeton d'API du site). Ce que D-85 ferme est donc **précis et étroit** : (a) une réapplication **automatique, à chaque démarrage**, depuis un support que le plan ne possède pas et qu'aucune preuve ne lit ; (b) le code de cloud-init lui-même — un interpréteur Python complet, exécuté en root au démarrage, et ses ~29 dépendances. Elle ne ferme **pas** la mainmise de l'hyperviseur sur ses invités : celle-là est une propriété de la virtualisation, pas de cloud-init, et elle appelle sa propre décision — non prise. **Le seuil où le remplacer deviendrait juste** : le jour où une première seconde ne peut plus être amorcée par Proxmox (autre hyperviseur, métal nu, hébergeur sans API), le chemin par l'agent invite cesse d'être une réimplémentation d'un standard — que `positionnement.md` interdit — et devient **le chemin portable**. Tant que ce seuil n'est pas atteint, écrire soi-même l'amorçage serait échanger un standard éprouvé contre du code maison au moment le plus fragile, dont le mode de panne est le pire : une VM injoignable | `roles/cloud_init_retrait/`, `serveur_durci.yml`, `positionnement.md` | **P63** | | **D-86** | **La supervision se déploie juste après la PKI, pas à la fin** | L'observabilité (`prometheus`, `loki`, `grafana`) et le **moteur** de supervision (`icinga`) passent en couche 4, immédiatement après `client_pki` ; les agents qui les nourrissent (`client_metrique`, `client_journal`, `client_sante`) en couche 5. **Le raisonnement** : ce qui se déploie ensuite l'est *sous l'œil* de la supervision — une unité qui casse se voit à la minute, pas à la fin. Une **reconstruction depuis zéro** est précisément le moment où l'on a le plus besoin de voir, et c'était le seul moment où l'on ne voyait rien. **Déplacer les serveurs sans les agents n'aurait rien changé** : ce sont les agents qui rapportent, et ils étaient en dernière couche. **Ce que ça a coûté en dépendances** : `serveur_postgresql` monte aussi (il n'exige rien lui-même, et `icinga` l'exige). **Ce qui reste tard, à dessein** : `icingaweb2` et `oauth2_proxy` réclament LDAP et Keycloak — c'est la CONSOLE, pas la mesure. L'interface humaine peut attendre. **Limite dite franchement** : les *notifications* dépendent de `client_smtp`, encore en dernière couche — pendant une reconstruction, l'état est mesuré et consultable, mais rien ne part par courriel avant la fin. **CE QUI REND CE DÉPLACEMENT POSSIBLE**, et qui n'est pas un détail : le DNS (`powerdns`, `resolveur`) reste en couche 6, donc *après* la supervision. Or `icinga` joint sa base par un **nom** (`data-sql-01.chezlepro.internal`), et `client_sante` pousse vers un **nom**. Ça tient parce que le **plancher `/etc/hosts`**, posé dès la couche 1 par `hosts_statiques`, porte déjà les 34 entrées de l'écosystème — vérifié. C'est exactement ce pour quoi il existe : *« il ne s'installe pas, il rend installable »*. Sans lui, cette décision serait impossible. **P08** valide l'ordre : aucune arête en arrière | `docs/couches-deploiement.yml`, `catalogue-services.md` §Ordre | **P08** | | **D-87** | **Ce que l'hébergeur n'a pas le droit de VOIR** — l'observabilité découpée, la PKI tranchée | La question *« quels rôles ne dois-je pas embarquer dans le site ? »* a mis à l'épreuve la table de mutualisation de `filiation-emancipation.md`, et **une ligne contredisait ce qui tourne**. Elle disait « observabilité \| oui \| l'hébergeur surveille ses locataires » — or chaque écosystème a son propre Icinga, et celui du site ne voit que ses sept machines (mesuré). Surtout, elle autorisait en une case ce que la ligne du dessous interdit : **les journaux contiennent du contenu** — un mot de passe dans un message d'erreur, une donnée métier dans une trace. Un hébergeur qui ingère les journaux de son locataire en sait **plus** que s'il détenait son annuaire ; l'annuaire dit qui existe, les journaux disent ce qu'ils font. **Découpée en trois** : *disponibilité* oui (une VM tombée est un fait de la fabric), *métriques* oui avec réserve (elles disent quand et combien, ce qui suffit à lire l'activité d'une organisation), *journaux* **non**. **Et la ligne PKI, « à trancher », est tranchée : non.** Une AC intermédiaire signée par l'hôte lui donnerait le pouvoir d'émettre des certificats valides pour les noms du locataire, donc de se présenter comme n'importe lequel de ses services — devant les propres machines du locataire, qui les accepteraient, puisque c'est ce que la chaîne de confiance leur demande. Même pouvoir que l'annuaire, sous une forme **moins visible** : aucune trace côté locataire. Une PKI par écosystème, jamais dérivée de l'hôte. **Le revers, mesuré et assumé** : le site n'a NI `client_journal` NI `client_metrique`, aucun `loki` ni `prometheus` — à refuser de voir ceux des locataires, il s'est privé des siens. Le remède n'est pas d'assouplir la règle, c'est de lui donner sa propre pile | `filiation-emancipation.md` §mutualisable | — | +| **D-88** | **Le nœud qui porte le gabarit est un point unique de défaillance — pour la REPRODUCTION, pas pour l'exploitation** | Question posée par l'exploitant le 2026-09-10 : *« le modèle vit sur vishnu, les clones sont sur asgard — qu'arriverait-il si vishnu tombait ? »*. **Mesuré, la réponse se coupe en deux.** Les **données** survivent : le pool `CephNVMe` est en `size=3 / min_size=2`, avec des OSD sur les trois hôtes, et `base-9006-disk-0/1` y sont répliquées — l'image reste lisible avec un nœud en moins, et les VM d'un écosystème tournent ailleurs sans s'apercevoir de rien. Mais la **configuration** du gabarit porte le nom du nœud dans son chemin (`/etc/pve/nodes/vishnu/qemu-server/9006.conf`), et le clonage appelle `nodes/vishnu/qemu/9006/clone` : nœud éteint, API muette, **aucune VM nouvelle ne peut naître**. Or la reproduction est ce que ce dépôt existe pour garantir. **La décision est d'ASSUMER cette dépendance et de la rendre courte**, pas de la supprimer : depuis que le disque du gabarit vit sur un stockage partagé (2026-09-10), la remise en route est un **déplacement de fichier de configuration** — quelques minutes, aucun mouvement de données — suivi de la déclaration `gabarit.noeud`. Sur stockage local il aurait fallu recopier 16 Go ou refabriquer. **Ce qui n'est PAS fait, et qui est dit** : rien ne *mesure* cette dépendance. `gabarit_etat` compare le déclaré au réel, il ne demande pas si le nœud du gabarit héberge autre chose que le gabarit. *Une dépendance qu'on documente sans la mesurer reste une dépendance qu'on découvrira au mauvais moment.* | `runbooks-exploitation.md` §7, `SITE-Chezlepro/plan/10-intrants.yml` §gabarit | — | | **D-81** | **La forge du SITE fait autorité pour le génome.** Toute autre copie — y compris celle d'où le moteur a été poussé jusqu'ici — est un **miroir**. Le poste de l'exploitant ne route pas jusqu'à elle : c'est le **runner du site** qui publie, par `make genome-pousser` | un écosystème se reproduit depuis la forge de son site : c'est de là qu'il clone son moteur, ses plans, ses modèles. Si l'autorité est ailleurs, cette forge devient un cache qu'on croit à jour — et le 2026-08-26 elle était **quatre commits en arrière** sans que rien ne le signale, dont le correctif qui désarme le pare-feu Proxmox. **Un écosystème qui se reproduit depuis une forge en retard reproduit ses défauts.** Le poste n'a de patte que sur l'administration, et on ne perce pas de chemin pour lui : le runner existe pour ce travail | `playbooks/maintenance/genome_pousser.yml`, `scripts/genome_colis.py`, `Makefile` §genome-pousser | — | | **D-13** | Un **hébergeur** sert plusieurs **tenants** et a son tenant par défaut | Chezlepro est les deux à la fois, ce qui masquait la distinction | `frontiere-opnsense.md` §2 | — | | **D-14** | `underlay.yml` appartient à l'**hébergeur**, monté par symlink | ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas | `sdn-evpn.md`, `underlay.yml.example` | — | diff --git a/docs/runbooks-exploitation.md b/docs/runbooks-exploitation.md index 43cdd71..6a32e14 100644 --- a/docs/runbooks-exploitation.md +++ b/docs/runbooks-exploitation.md @@ -215,3 +215,70 @@ Les hyperviseurs gardent **deux** plans, et c'est voulu : > préfixe le plus long, pas une coïncidence. Il faut cependant le **déclarer** > (`bande_basse_de:` dans `underlay.yml`), sinon le validateur ne peut pas distinguer ce > chevauchement voulu d'un chevauchement accidentel. + +## 7. Le nœud qui porte le gabarit tombe — la reproduction s'arrête + +**Symptôme.** Aucune VM nouvelle ne peut naître. `make creer-vm`, `make flotte-creer` et +`make reconstruire` échouent au clonage. Les machines existantes, elles, ne bronchent pas. + +**Ce qui se passe.** Le gabarit doré vit sur un nœud nommé — `vishnu` chez l'hébergeur de +référence — et sa configuration porte ce nom dans son chemin même : + +``` +/etc/pve/nodes/vishnu/qemu-server/9006.conf +``` + +Le clonage appelle `nodes/vishnu/qemu/9006/clone` : c'est l'API de **ce nœud-là** qui doit +répondre. Nœud éteint, API muette, aucune naissance. + +**Ce qui ne se passe PAS, et qu'il faut savoir avant de paniquer.** Les données du gabarit +ne sont pas perdues. Mesure du 2026-09-10 : + +``` +pool CephNVMe size=3 min_size=2 +OSD sur les trois hôtes : asgard, gandalf, vishnu +base-9006-disk-0, base-9006-disk-1 présentes dans le pool +``` + +L'image est répliquée trois fois et reste lisible avec un nœud en moins. Et les quatorze VM +d'un écosystème tournent ailleurs, sur leurs propres disques Ceph : elles ne s'aperçoivent +de rien. + +> **`vishnu` n'est pas un point unique de défaillance pour l'exploitation. Il l'est pour la +> reproduction** — et la reproduction est ce que ce dépôt existe pour garantir. + +**La manœuvre.** Deux gestes, quelques minutes, aucun mouvement de données : + +```bash +# 1. re-héberger la configuration sur un nœud debout +mv /etc/pve/nodes/vishnu/qemu-server/9006.conf \ + /etc/pve/nodes/asgard/qemu-server/9006.conf + +# 2. le déclarer, sinon la garde refusera +# SITE-Chezlepro/plan/10-intrants.yml : gabarit.noeud: asgard +``` + +Puis vérifier : + +```bash +make gabarit-etat # doit rendre « Conforme » +``` + +**Pourquoi c'est aussi court.** Parce que le disque du gabarit vit sur un stockage +**partagé** depuis le 2026-09-10 : le déplacement ne bouge qu'un fichier de configuration. +Sur un stockage local, il aurait fallu recopier 16 Go — ou refabriquer le gabarit. + +**L'ordre compte.** Déplacer sans déclarer laisse `gabarit_etat` en écart ; déclarer sans +déplacer fait échouer le clonage sur un nœud qui ne détient pas le modèle. Faire les deux, +dans cet ordre. + +**Ce que cette section ne fait pas.** Elle ne supprime pas la dépendance : elle la rend +connue et courte. Deux remèdes de fond existent, aucun n'est appliqué : + +- **déplacer le gabarit là où vivent déjà les VM** — ça ne supprime pas le point unique, + ça cesse d'en avoir *deux* (le nœud des VM et celui du modèle) ; +- **une garde** qui refuse quand le nœud du gabarit n'héberge aucune machine de la flotte, + c'est-à-dire quand la reproduction dépend d'un nœud qui ne porte rien d'autre. + +*Une dépendance qu'on documente sans la mesurer reste une dépendance qu'on découvrira au +mauvais moment.*