Deuxième mode de réplication, séparé de la réplication cluster vers cluster
(sélecteur « Clusters (API) | Nœuds (fichiers) » en haut de page) : un profil
réplique la configuration système d'un nœud maître vers des nœuds cibles.
- réseau par /nodes/{node}/network (interfaces.new puis rechargement), adresses
et passerelles propres à chaque nœud, mapping des noms d'interfaces par cible ;
DNS par /nodes/{node}/dns ; multipath.conf, wwids (ajout seul), chrony,
sysctl.d, interfaces.d (hors sdn) par SSH
- découverte des nœuds (/cluster/status), accès SSH par nœud (mot de passe du
cluster ou propre), clé d'hôte épinglée, contrôle du nom du nœud
- dry-run avec diff des fichiers ; apply (admin) avec contrôle SHA-256,
sauvegarde et restore.sh sur le nœud, écriture atomique, rechargements,
restauration si un rechargement échoue, timer systemd de restauration si le
nœud est injoignable après le rechargement du réseau, confirmation tapée
- vue Nœuds et profils, journal séparé par mode, démo avec nœuds simulés
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
118 lines
11 KiB
Markdown
118 lines
11 KiB
Markdown
# Proposition : réplication de fichiers système nœud à nœud (SSH)
|
|
|
|
Statut : validée par Cédric le 2026-10-23 (liste v1, API dès que possible, identifiants
|
|
SSH hérités du cluster avec surcharge par nœud), puis implémentée. Écart avec la
|
|
proposition : `/etc/network/interfaces` passe par l'API PVE (`/nodes/{node}/network`) et
|
|
non par fichier, et le DNS est ajouté par l'API (`/nodes/{node}/dns`). Le fonctionnement
|
|
réel est décrit dans le README (« Réplication de nœuds »).
|
|
|
|
Deuxième mode de réplication, séparé de la réplication cluster à cluster (API) :
|
|
un **nœud maître** et un ou plusieurs **nœuds cibles** (même cluster ou non),
|
|
fichiers lus et écrits en SSH root. Même moteur : lecture, normalisation, diff,
|
|
plan, apply, vérification.
|
|
|
|
Constat dans le code actuel : l'accès SSH est défini **par cluster, sur un seul
|
|
hôte** (`SSHHost`, `internal/config/config.go:33`). Il faut le passer **par
|
|
nœud** : nœuds découverts par l'API (`GET /cluster/status`, champ `ip`), identifiants
|
|
root hérités du cluster et surchargeables par nœud, clé d'hôte épinglée par nœud
|
|
(même TOFU que maintenant).
|
|
|
|
## 1. Fichiers candidats
|
|
|
|
Légende : 🟢 v1 recommandée, 🟡 v1 possible avec garde-fous, 🔴 exclu.
|
|
|
|
| Fichier | Intérêt | Risques | Propre au nœud (mapper / exclure) | Rechargement |
|
|
|---|---|---|---|---|
|
|
| 🟢 `/etc/network/interfaces` | bonds, VLAN, bridges, MTU identiques partout | **perte d'accès au nœud** ; noms de NIC différents d'un nœud à l'autre | `address`, `gateway`, `hwaddress`, IP de management, éventuellement noms de NIC (`nic1` maître ↔ `enp65s0f0` cible) | `ifreload -a` |
|
|
| 🟢 `/etc/network/interfaces.d/*` | idem | **`interfaces.d/sdn` est généré par le SDN PVE** | exclure `sdn` d'office | `ifreload -a` |
|
|
| 🟢 `/etc/multipath.conf` | `defaults`, `blacklist`, `devices` identiques sur tout le cluster | erreur de syntaxe = chemins perdus | rien en général | `multipathd reconfigure` |
|
|
| 🟢 `/etc/multipath/wwids` | mêmes LUN vues par tous les nœuds | ajouter un WWID local (disque système) par erreur | mode additif seulement, jamais de suppression | `multipathd reconfigure` |
|
|
| 🟡 `/etc/multipath/bindings` | alias `mpathX` identiques sur tous les nœuds (important si LVM partagé référence les noms) | renommer un alias en service | additif seulement, conflit d'alias = bloquant | `multipathd reconfigure` |
|
|
| 🟢 `/etc/chrony/chrony.conf` (+ `sources.d`) | mêmes sources NTP | faible | rien | `systemctl restart chrony` |
|
|
| 🟡 `/etc/systemd/timesyncd.conf` | si chrony absent | faible | rien | `systemctl restart systemd-timesyncd` |
|
|
| 🟢 `/etc/sysctl.d/*.conf` | tuning réseau / mémoire homogène | réglage inadapté au matériel | rien ; ne jamais toucher les fichiers des paquets (`/usr/lib/sysctl.d`) | `sysctl --system` |
|
|
| 🟡 `/etc/modprobe.d/*.conf` | options ZFS (`zfs_arc_max`), blacklist de modules | effet au reboot seulement ; ARC dépend de la RAM | `zfs_arc_max` à surcharger par nœud | `update-initramfs -u`, **redémarrage requis** (signalé, jamais fait) |
|
|
| 🟡 `/etc/apt/sources.list.d/*.sources` | mêmes dépôts (enterprise / no-subscription / ceph) | dépôt enterprise sans abonnement sur la cible | rien | aucun (pas d'`apt update` automatique) |
|
|
| 🟡 `/etc/lvm/lvm.conf` | `global_filter` pour iSCSI / multipath | **filtre faux = volumes invisibles** | rien en général | aucun ; vérification `pvs` avant / après |
|
|
| 🟡 `/etc/iscsi/iscsid.conf` | timeouts, `node.startup` | **contient les secrets CHAP** | secrets chiffrés au repos, jamais affichés en clair dans le diff | `systemctl restart iscsid` (signalé) |
|
|
| 🟡 `/etc/vzdump.conf` | défauts de sauvegarde par nœud | faible | `tmpdir` éventuellement | aucun |
|
|
| 🟡 `/etc/postfix/main.cf` | relais SMTP des notifications | faible | `myhostname` | `postfix reload` |
|
|
| 🔵 `/etc/resolv.conf` | DNS identiques | écrasé par DHCP / resolvconf | rien | aucun. **Existe aussi en API** (`/nodes/{node}/dns`), à préférer : à vérifier dans l'API viewer |
|
|
| 🔵 `/etc/hosts` | résolution des nœuds entre eux | ligne du nœud lui-même ; corosync dépend du nom | ligne `127.0.1.1` et la ligne du nœud cible | aucun. **Existe aussi en API** (`/nodes/{node}/hosts`), à vérifier |
|
|
| 🔴 `/etc/pve/*` (dont `corosync.conf`) | déjà partagé par pmxcfs dans un cluster | casser le cluster | | |
|
|
| 🔴 `/etc/corosync/*`, `/etc/hostname`, `/etc/machine-id`, clés SSH d'hôte, `/etc/iscsi/initiatorname.iscsi` | | identité unique du nœud | | |
|
|
| 🔴 `/etc/frr/*` | | géré par le SDN EVPN (hors périmètre) | | |
|
|
| 🔴 `/etc/kernel/cmdline`, `/etc/default/grub` | | démarrage du nœud | | v2 éventuellement |
|
|
|
|
🔵 = je propose de passer par l'API PVE plutôt que par le fichier (déjà le modèle du reste de l'outil).
|
|
|
|
**Ma recommandation v1 :** `interfaces` (+ `interfaces.d` hors `sdn`), `multipath.conf`, `wwids`, `chrony.conf`, `sysctl.d`. Le reste en v1.1 une fois le mécanisme éprouvé.
|
|
|
|
## 2. Modèle
|
|
|
|
**Profil de réplication** : un nœud maître, des nœuds cibles, une liste de fichiers, et par fichier des règles.
|
|
|
|
**Règles par fichier**, deux niveaux :
|
|
- **Adaptateur structuré** pour `interfaces` (stanzas `iface`) et `multipath.conf` (sections) : politique par champ comme pour l'API (sync / ignorer / surcharger par cible). Exemple : `iface vmbr0 / address` = surcharger, valeur par cible.
|
|
- **Mode texte** pour les autres : diff ligne à ligne, avec des **lignes protégées** (regex) conservées telles quelles sur la cible, et des **variables** (`{{hostname}}`, `{{ip}}`, valeurs saisies par cible).
|
|
- **Mapping de noms de NIC** par cible (`eno1` → `enp1s0f0`), appliqué avant le diff.
|
|
|
|
**Déroulé :**
|
|
1. **Lecture** : `cat` + `sha256sum` + propriétaire et droits, sur le maître et chaque cible.
|
|
2. **Diff** : diff unifié par fichier et par cible, après application des règles. Statut : à jour / écarts / bloquant (ex. NIC du maître absente sur la cible, alias multipath en conflit).
|
|
3. **Dry-run** (par défaut) : rien n'est écrit, on affiche le fichier final exact et la commande de rechargement.
|
|
4. **Apply**, après **confirmation explicite** (comme le SDN), **un nœud à la fois**, arrêt au premier échec :
|
|
- le hash de la cible doit être celui lu au plan (sinon : modifié entre-temps, on refuse) ;
|
|
- **sauvegarde** sur le nœud (`/var/lib/pve-confsync/backup/<chemin>.<horodatage>`) et en base (SQLite, historique) ;
|
|
- **validation** avant écriture quand l'outil existe : `multipath -t` sur un fichier temporaire, contrôle de syntaxe ifupdown2 (`ifup --no-act`, option exacte à vérifier) ;
|
|
- écriture **atomique** (fichier temporaire + `mv`), propriétaire et droits conservés ;
|
|
- **rechargement** (colonne de droite du tableau), seulement si coché ;
|
|
- **vérification** : relecture du hash, et pour le réseau, reconnexion SSH.
|
|
5. **Filet de sécurité réseau** : avant `ifreload -a`, on programme sur le nœud une restauration automatique (`systemd-run --on-active=120s` qui remet la sauvegarde et relance `ifreload -a`). Si l'outil se reconnecte en SSH dans le délai, il annule la restauration. Sinon le nœud revient seul à l'ancienne config.
|
|
6. **Suppression** : jamais par défaut. Un fichier absent du maître n'est pas supprimé sur la cible sauf option explicite (cohérent avec le mode additif du reste).
|
|
|
|
**Journal et audit** : chaque exécution (profil, nœud, fichier, hash avant / après, sauvegarde, sortie du rechargement) dans le journal existant, avec un type « Nœuds ».
|
|
|
|
**Droits** : opérateur = dry-run, admin = apply (le SSH root est le droit le plus fort de l'outil).
|
|
|
|
## 3. Maquette de l'interface
|
|
|
|
Un sélecteur en tête sépare les deux modes. Chaque mode garde les onglets Écarts / Profils (ou Clusters) / Journal.
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────────────┐
|
|
│ pve-confsync [ Clusters (API) | Nœuds (fichiers) ] 👤 ⏻ │
|
|
├──────────────────────────────────────────────────────────────────────┤
|
|
│ [Écarts] [Profils] [Journal] Profil: [Réseau stockage ▾] 2 écarts │
|
|
│ [Dry-run global] [Appliquer]│
|
|
├────────────────────────────┬─────────────────────────────────────────┤
|
|
│ MAÎTRE pve1 (lab-a) │ pve2 · /etc/network/interfaces ÉCARTS │
|
|
│ │ │
|
|
│ CIBLES │ auto bond0 │
|
|
│ ┌────────────────────────┐ │ iface bond0 inet manual │
|
|
│ │ pve2 (lab-a) ÉCARTS │ │ - bond-mode active-backup │
|
|
│ │ interfaces ✎ 3 │ │ + bond-mode 802.3ad │
|
|
│ │ multipath.conf ✓ │ │ + bond-xmit-hash-policy layer3+4 │
|
|
│ │ chrony.conf ✓ │ │ iface vmbr0 inet static │
|
|
│ └────────────────────────┘ │ address 10.0.0.12/24 🔒 surchargé │
|
|
│ ┌────────────────────────┐ │ bridge-ports bond0 │
|
|
│ │ pve3 (lab-a) À JOUR │ │ │
|
|
│ └────────────────────────┘ │ Après écriture : ifreload -a ☑ │
|
|
│ ┌────────────────────────┐ │ Restauration auto si perte SSH : 120 s │
|
|
│ │ pve7 (lab-b) BLOQUANT │ │ │
|
|
│ │ interfaces NIC eno2 │ │ [Dry-run] [Appliquer sur pve2] │
|
|
│ │ absente, à mapper │ │ │
|
|
│ └────────────────────────┘ │ │
|
|
└────────────────────────────┴─────────────────────────────────────────┘
|
|
```
|
|
|
|
Onglet **Profils** : maître, cibles (nœuds découverts par cluster, cases à cocher), fichiers (cases parmi la liste du §1), règles par fichier (champs à surcharger, lignes protégées, mapping de NIC par cible).
|
|
|
|
Fenêtre de **confirmation** d'apply : liste des nœuds dans l'ordre, fichiers, commandes de rechargement, avertissements (« redémarrage requis », « accès réseau »), saisie du nom du nœud pour confirmer quand `interfaces` est concerné.
|
|
|
|
## Questions ouvertes
|
|
|
|
1. Liste v1 : celle recommandée ci-dessus, ou plus large ?
|
|
2. `resolv.conf` et `hosts` par l'API plutôt que par fichier : d'accord ?
|
|
3. Identifiants SSH : hérités du cluster avec surcharge par nœud, d'accord ?
|