Set-OPS-Public/docs/implanter-un-tenant-sur-un-site.md
Daniel Allaire 3445ccb836 portee : les trois devis d'un site partagent enfin la meme regle
Le commit du 2026-08-14 nommait lui-meme ce qui restait : « meme hypothese ailleurs, non
corrigee — devis_sdn et devis_reseau partent du meme decouvrir(). A traiter quand ils
serviront sur un second site. » C'est fait AVANT, pas pendant la visite.

Les trois devis equipent le MATERIEL d'un site : la frontiere (regles, routes), le
commutateur (VLAN, SVI, routes) et le SDN de l'hyperviseur (zones, VNets). Un tenant
d'ailleurs y ajoutait des objets que le materiel accepte, qui ne correspondent jamais a
rien, et que rien ne signale.

UNE SEULE FONCTION AU LIEU D'UN FILTRE RECOPIE TROIS FOIS :
`devis_reseau.decouvrir_du_site()` = decouvrir() restreint par `underlay.tenants`, la
doctrine ecrite une fois. Le filtre inline de devis_opnsense est retire au profit d'elle.
`admin_tous_tenants()` la suit : le routeur d'un site n'a pas a savoir revenir vers le
plan de gestion d'un tenant qu'il ne porte pas.

EPROUVE dans les trois situations : underlay sans la cle -> les deux tenants, comme avant ;
underlay du second site -> OPS-Technolibre seul ; nom declare qu'aucun dossier ne fournit
-> ATTENTION et le reste est retenu ; filtre qui ne retient rien -> refus, code 1.

SANS EFFET SUR LE SITE ACTUEL : l'underlay de Chezlepro ne declare pas `tenants`, et cle
absente = toute la federation (verifie : decouvrir() et decouvrir_du_site() rendent la
meme liste ici).

ET LA CLE EST ENFIN DOCUMENTEE — c'etait le vrai trou. `underlay.tenants` existait depuis
le 14 sans figurer ni dans underlay.yml.example ni dans l'annexe du runbook
d'implantation : indecouvrable pour qui monte un second site.

prouver 37 OK, 0 echec, 0 saute ; make test inchange.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 16:38:30 -04:00

265 lines
13 KiB
Markdown

# 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.<index>.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.<index>.0.0/24`, chemins en `192.168.<vlan>.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<index>` (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.<index>.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=<dossier>`)
- [ ] `underlay.yml` → le fichier du **site** :
`ln -sfn ../<depot-hebergeur>/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:
# LE SEED DU SITE — le même que celui de son tenant, et la clé qu'on oublie.
# C'est elle qui dit au validateur que 10.<index>.0.0/16 est SON supernet, donc que
# la bande basse lui appartient. Sans elle, `make underlay` refuse le réseau de
# gestion en le prenant pour celui d'un AUTRE site — message déroutant, cause triviale.
index: <index>
# LES TENANTS QUE CE SITE PORTE — noms de dossier, pas de fantaisie. Les trois devis
# d'équipement (frontière, commutateur, SDN) découvrent TOUTE la fédération : sans
# cette clé, le second site se voit proposer les règles, les VLAN et les zones du
# premier. Le matériel les accepte, aucune ne correspond jamais à un paquet, et rien
# ne le signale. Un site UNIQUE n'a rien à déclarer ; c'est le second qui se nomme.
tenants: [OPS-<tenant>]
routeur: <nom-du-commutateur> # racine du spanning-tree, pas un routeur
# `stp` exige `routeur` : ne pas le déclarer tant qu'aucun commutateur ne l'est.
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.<index>.0.0/24, passerelle: 10.<index>.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: <frontiere>, reseau: management, ip: 10.<index>.0.1, role: frontiere }
- { nom: <frontiere>, reseau: transit-frontiere, ip: 192.168.40.1, role: frontiere }
- { nom: <noeud>, reseau: management, ip: 10.<index>.0.41, role: hyperviseur, via: vmbr0 }
- { nom: <noeud>, reseau: underlay-vxlan, ip: 192.168.50.41, role: hyperviseur, via: <bond> }
- { nom: <noeud>, reseau: transit-frontiere, ip: 192.168.40.41, role: hyperviseur, via: <bond> }
```
`proxmox-hebergeur.yml` — **rempli depuis la reconnaissance, jamais de mémoire** :
```yaml
---
proxmox_api_host: <noeud>
proxmox_api_port: '8006'
proxmox_api_user: ansible@pve
proxmox_validate_certs: false
proxmox_noeuds: [<noeud>]
proxmox_stockages: [<ceux qui portent `images`>]
proxmox_ponts: [<présents sur TOUS les nœuds>]
proxmox_sdn:
controleur: EVPN00<index>
asn: 65000
noeuds_de_sortie: [<noeud>]
sortie_primaire: <noeud>
```
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.