diff --git a/README.md b/README.md index f653387..0eee559 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ On n'arrive pas avec un *sujet*, on arrive avec une **situation**. Il y en a cin |---|---| | « Je veux **monter** mon écosystème sur ma grappe Proxmox » | [`QUICKSTART.md`](QUICKSTART.md) | | « Je **prête mon matériel** à quelqu'un qui déploiera dessus » | [`docs/preparer-un-site-hebergeur.md`](docs/preparer-un-site-hebergeur.md) | +| « Je vais **implanter un tenant existant sur un site neuf** » | [`docs/implanter-un-tenant-sur-un-site.md`](docs/implanter-un-tenant-sur-un-site.md) | | « Je viens d'**hériter** d'un écosystème déjà déployé, je dois l'exploiter » | [`wiki/Reprendre-l-écosystème.md`](wiki/Reprendre-l-%C3%A9cosyst%C3%A8me.md) | | « Je dois **modifier** le moteur » | [`docs/carte-set-ops.md`](docs/carte-set-ops.md) | | « J'**apprends** le métier » | [`wiki/Home.md`](wiki/Home.md) | diff --git a/docs/audit/preuve-2026-08-13.md b/docs/audit/preuve-2026-08-13.md index 63466d9..ec6d3c1 100644 --- a/docs/audit/preuve-2026-08-13.md +++ b/docs/audit/preuve-2026-08-13.md @@ -46,7 +46,7 @@ | P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 45 scripts expliques et atteignables, 93 cibles make documentees, 54 roles avec README. | | P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 35 exigence(s) de role, toutes satisfaites (118 cle(s) declaree(s) par l'instance). | | P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 32 revendication(s) de port, aucune collision entre roles co-localises (33 groupes). | -| P34 | Chaque document declare son lecteur | — | ✅ OK | 39 document(s) declarent leur lecteur (16 genere(s) exempte(s)). | +| P34 | Chaque document declare son lecteur | — | ✅ OK | 40 document(s) declarent leur lecteur (16 genere(s) exempte(s)). | | P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). | | P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 9 hote(s) detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). | | P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (OPS-Chezlepro) : noeud, stockage, pont — tous offerts. | diff --git a/docs/implanter-un-tenant-sur-un-site.md b/docs/implanter-un-tenant-sur-un-site.md new file mode 100644 index 0000000..9b49a88 --- /dev/null +++ b/docs/implanter-un-tenant-sur-un-site.md @@ -0,0 +1,253 @@ +# Implanter un tenant sur un site hébergeur neuf + +> **Pour qui :** l'**exploitant**, sur place, le jour de l'intervention. Le tenant existe +> déjà (son plan est écrit, éprouvé) ; le site, lui, n'a jamais rien porté. Rien à +> migrer, rien à interrompre. + +Trois documents voisins, à ne pas confondre : + +| Situation | Document | +|---|---| +| L'hébergeur prépare son matériel, **avant** qu'on arrive | [`preparer-un-site-hebergeur.md`](preparer-un-site-hebergeur.md) | +| Un tenant **vivant** change d'hébergeur, sans coupure | [`migration-tenant.md`](migration-tenant.md) | +| **Ce document** — un tenant existant prend corps sur un site vierge | *(ici)* | + +## La règle qui commande tout l'ordre + +**Rien n'est fait tant que ce n'est pas mesuré sur place.** Une valeur transmise par +courriel, une liste relevée dans l'interface web, un `vmbr` cité de mémoire : chacun de +ces trois a déjà produit une panne dans ce dépôt. Chaque phase ci-dessous se termine donc +par une **commande qui interroge le système**, jamais par une conviction. + +> **Le piège qui revient le plus souvent : le chèque vert sur un périmètre vide.** Une +> sauvegarde qui réussit sur zéro fichier, un devis qui lit un intrant périmé, une preuve +> qui ne peut pas échouer. À chaque « ✅ », se demander **sur quoi** il a porté. + +--- + +## Phase 0 — au bureau, avant de partir + +- [ ] **Recevoir la fiche de l'hébergeur** — les huit lignes du §7 de + [`preparer-un-site-hebergeur.md`](preparer-un-site-hebergeur.md). Sans le nom exact + du nœud et des stockages, la journée s'arrête à la phase 1. +- [ ] **Fixer l'`index` du site = l'`index` de son tenant.** Il n'y a pas de second + registre : la gestion du site est `10..0.0/24`, dérivée du même seed que les + zones du tenant. *(Technolibre → index 23 → gestion `10.23.0.0/24`.)* +- [ ] **Créer le dépôt de l'hébergeur** — un dossier frère, avec deux fichiers : + `underlay.yml` et `proxmox-hebergeur.yml` (squelettes en annexe). +- [ ] **Emporter le gabarit** `modeleSetOPS` sur disque (`vzdump`), *et* la procédure de + fabrication en repli : [`procedure-template-debian13-proxmox.md`](procedure-template-debian13-proxmox.md). +- [ ] **Préparer la voûte** de l'instance à recevoir les deux paires de secrets + (hyperviseur, frontière). Jamais dans un dépôt git, jamais dans une conversation. +- [ ] **Vérifier la version de Proxmox.** Tout ce dépôt a été éprouvé sur PVE 8. Sur PVE 9, + traiter chaque écart comme inconnu jusqu'à mesure — en particulier le SDN EVPN et le + moteur de pare-feu. + +> **Un site neuf se construit d'emblée dans l'adressage cible** — gestion en +> `10..0.0/24`, chemins en `192.168..0/24` (D-77, D-78). Le site historique +> est encore en `10.0.x` et migrera par [`runbooks-exploitation.md`](runbooks-exploitation.md) §6. +> Sur un site vierge, la cible ne coûte rien — et deux sites en `10.0.0.0/24` rendraient +> la **reprise mutuelle impossible** : deux plans de gestion identiques ne peuvent pas +> s'atteindre. + +--- + +## Phase 1 — reconnaissance du cluster (aucune écriture) + +- [ ] **Relever le cluster par son API**, pas par son interface : nœuds, stockages qui + acceptent `images`, ponts présents. +- [ ] **Écrire `proxmox-hebergeur.yml` depuis ce relevé** — jamais depuis une union + devinée. C'est en recopiant des listes chez chaque tenant qu'elles ont divergé. +- [ ] **Décider le nom du contrôleur EVPN** : `EVPN00` (site 23 → `EVPN0023`). Un + seul contrôleur par site sert **tous** ses tenants ; chaque tenant n'a que sa zone. +- [ ] **Sur un site à un seul nœud** : ce nœud est aussi le nœud de sortie et la sortie + primaire. Aucun pont ne peut être « partiel », et il n'y a pas de haute + disponibilité — le dire à l'hébergeur plutôt que le laisser supposer. + +```bash +make underlay # affiche et valide la fabric — preuve P23 +make placement-plan # nœud, stockage, pont, gabarit : existent-ils VRAIMENT ? +``` + +`placement-plan` est la commande qui sauve la journée : elle confronte les quatre objets +au cluster **avant** quarante minutes de déploiement. C'est elle qui a trouvé un pont +déclaré mais disparu depuis dix jours. + +--- + +## Phase 2 — la frontière + +- [ ] **Interfaces** : WAN (noter l'IP publique) ; gestion `10..0.1/24` ; transit + `192.168.40.1/24`. Un point de routage porte `.1` partout — invariant P23. +- [ ] **Compte `ansible`** avec clé publique. **Aucun `sudo`** : l'outil lit et appelle + l'API. +- [ ] **Clé d'API** (*System → Access → Users → API keys*). Le secret n'est affiché + **qu'une fois**. +- [ ] Poser la clé dans la voûte, puis : + +```bash +make frontiere-plan # écart, sans rien écrire +make frontiere-appliquer CONFIRMER=true # écrit ET retire le périmé +``` + +--- + +## Phase 3 — le gabarit + +- [ ] `qmrestore` de l'archive, puis **convertir en template**. Une VM ordinaire se + clonerait aussi — et produirait quatorze copies d'une machine vivante. +- [ ] **Noter son VMID sur ce cluster** et le porter dans le `group_vars/proxmox.yml` du + tenant (`proxmox_clone_vmid_modele`). +- [ ] **Ne pas le personnaliser.** Une clé d'hôte SSH, un `/etc/resolv.conf` figé ou un + compte nominatif se recopient dans **chaque** clone. C'est arrivé ; il a fallu + recapturer puis reconstruire. + +> Transférer un binaire est le geste rapide, pas le geste juste : D-76 vise à +> **construire** le gabarit depuis le dépôt. À faire une fois, pas à ériger en méthode. + +--- + +## Phase 4 — composer le moteur sur ce site + +Les deux symlinks sont des axes **indépendants** (D-80) : le tenant ne sait rien de la +fabric qui le porte. + +- [ ] `instance` → le dépôt du **tenant** (`make instance-utiliser NOM=`) +- [ ] `underlay.yml` → le fichier du **site** : + `ln -sfn ..//underlay.yml underlay.yml` + *(`proxmox-hebergeur.yml` est trouvé par dérivation de ce symlink — rien d'autre à + déclarer.)* +- [ ] Les **quatre valeurs de placement** du tenant, dans son + `inventories/*/group_vars/proxmox.yml` : `proxmox_clone_noeud`, + `proxmox_clone_stockage`, `proxmox_clone_pont`, `proxmox_clone_vmid_modele`. +- [ ] Le **jeton d'API** de ce cluster dans la voûte de l'instance. + +```bash +make instancier && make instancier-appliquer # FORCE=1 si le plan a changé exprès +make prouver && make test +make placement-plan # re-mesurer : les valeurs ont changé +``` + +--- + +## Phase 5 — matérialiser + +- [ ] **Le SDN d'abord** : + +```bash +make sdn-plan # écart +make sdn-appliquer CONFIRMER=true # crée ce qui manque, RETIRE ce qui est périmé +``` + +- [ ] **Puis la flotte, en une commande** : + +```bash +make reconstruire CONFIRMER=true +``` + +Elle enchaîne, **dans cet ordre et pour de bonnes raisons** : + +| | | Pourquoi cet ordre | +|---|---|---| +| 1 | `make flux` | Sans lui, `flux-genere/` est **vide** et le socle pose nftables en `policy drop` **sans aucune règle**. La flotte monte, SSH répond depuis l'administration, et tout le reste est mur. Trouvé le 2026-08-10 en montant un tenant depuis zéro : l'AC était debout, son port 8443 en écoute, et `step ca bootstrap` expirait depuis le même sous-réseau. | +| 2 | `flotte-creer` | clone les VM manquantes ; une VM déjà présente est sautée | +| 3 | `_amorcer-socle` | **PKI et DNS complètement debout d'abord** (D-71), hôte par hôte. Sinon chaque VM réclame un certificat à une autorité absente — et l'échec se lit comme un défaut du rôle, pas comme un défaut d'ordre. | +| 4 | `deployer-tout` | le reste, par couches | + +- [ ] Si les clones expirent : `proxmox_clone_timeout` est **court sous clonage + parallèle**. Le relever, ou baisser `PARALLELE=n`. + +--- + +## Phase 6 — la recette (rien n'est « prêt » avant) + +- [ ] `make valider` — la recette sur la flotte +- [ ] Les devis, qui interrogent le **système en marche**, pas le dépôt : + +```bash +make certificats-plan make identite-plan make courriel-plan +make expositions-plan make postgresql-plan make devis-reseau +make frontiere-plan make sdn-plan make placement-plan +``` + +- [ ] **La sauvegarde emporte-t-elle quelque chose ?** Compter les fichiers, pas les + succès. Une sauvegarde verte sur zéro fichier a tenu six semaines ici. +- [ ] **Éprouver une restauration** — [`runbooks-exploitation.md`](runbooks-exploitation.md) §5. + Une sauvegarde jamais restaurée n'est pas une sauvegarde. +- [ ] **La supervision voit-elle l'unité de sauvegarde ?** Sans + `vault_icinga_api_depot`, personne ne surveille les sauvegardes — et rien ne le dit. +- [ ] `make prouver` (toutes les preuves) et `make test`. + +--- + +## Ce qui n'est PAS fait en repartant + +À dire à l'hébergeur, explicitement, plutôt que de le laisser supposer : + +- **Le resserrement du compte d'API.** `Administrator` le premier jour évite de courir + après des `403` ; le réduire est un rôle sur mesure, dix minutes — **un geste, pas une + intention**. +- **Le lien inter-sites**, si la reprise mutuelle est prévue : il doit tenir sans qu'aucun + poste soit allumé, et sa politique (`allowed-ips`) *est* l'isolation. +- **Le gabarit des deux côtés** — une reprise sans gabarit chez le survivant n'est pas une + reprise. +- **La voûte de l'instance conservée hors de son propre site.** Les sauvegardes sont + chiffrées côté client : le site d'accueil héberge du chiffré qu'il ne peut pas lire. + +--- + +## Annexe — les deux fichiers du site + +`underlay.yml` — squelette d'un site à un nœud, dans l'adressage cible : + +```yaml +--- +underlay: + routeur: # racine du spanning-tree, pas un routeur + routage_tenants: sdn # le routage inter-zone vit sur l'hyperviseur + mtu_overlay: 1450 # 1450 + 50 de VXLAN = 1500 de transport + acl_inter_tenant: false # true seulement si le matériel sait lier une ACL à un SVI + dialecte: cisco # cisco | binardat — propriété du MATÉRIEL + stp: { mode: mstp, topologie: etoile } + reseaux: + - { nom: management, vlan: 10, sous_reseau: 10..0.0/24, passerelle: 10..0.1, mtu: 1500 } + - { nom: transit-frontiere, vlan: 40, sous_reseau: 192.168.40.0/24, passerelle_sortie: 192.168.40.1, mtu: 1500 } + - { nom: underlay-vxlan, vlan: 50, sous_reseau: 192.168.50.0/24, mtu: 1500 } + # Stockage : uniquement les réseaux réellement câblés (fabric: stockage, mtu 9000). + hotes: + - { nom: , reseau: management, ip: 10..0.1, role: frontiere } + - { nom: , reseau: transit-frontiere, ip: 192.168.40.1, role: frontiere } + - { nom: , reseau: management, ip: 10..0.41, role: hyperviseur, via: vmbr0 } + - { nom: , reseau: underlay-vxlan, ip: 192.168.50.41, role: hyperviseur, via: } + - { nom: , reseau: transit-frontiere, ip: 192.168.40.41, role: hyperviseur, via: } +``` + +`proxmox-hebergeur.yml` — **rempli depuis la reconnaissance, jamais de mémoire** : + +```yaml +--- +proxmox_api_host: +proxmox_api_port: '8006' +proxmox_api_user: ansible@pve +proxmox_validate_certs: false +proxmox_noeuds: [] +proxmox_stockages: [] +proxmox_ponts: [] +proxmox_sdn: + controleur: EVPN00 + asn: 65000 + noeuds_de_sortie: [] + sortie_primaire: +``` + +Le MTU n'est pas un détail : **à moitié configuré, le jumbo ne fonctionne pas du tout.** +Un MTU rogné en chemin donne le pire des symptômes — les petites requêtes passent, les +grosses meurent, et rien n'est signalé. `make mtu-mesurer` tranche. + +## Voir aussi + +- [`preparer-un-site-hebergeur.md`](preparer-un-site-hebergeur.md) — ce que l'hébergeur prépare. +- [`migration-tenant.md`](migration-tenant.md) — déplacer un tenant **vivant**. +- [`multi-instances.md`](multi-instances.md) — un moteur, N écosystèmes. +- [`sdn-evpn.md`](sdn-evpn.md) — pourquoi une zone EVPN par tenant. +- [`decisions-architecture.md`](decisions-architecture.md) — D-71 (PKI/DNS d'abord), D-77, D-78, D-80.