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>
265 lines
13 KiB
Markdown
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.
|