SPB Git forge
29commits 1branches 0releases
684.0 KBsize
maindefault branch
2 days agolast push
Python 95.8% Shell 4.2%
9.1 KB · 119 lines markdown
Rendered Raw Blame History
1# maclustr-dispatch — `mld`23Orchestrateur de déploiement du cluster MacLustr. Il tourne **sur la passerelle M1M32** et4déploie chaque application sur le **nœud le plus efficient** du cluster, via le LAN5(192.168.2.x, clé SSH `maclustr-m1m32` autorisée sur tous les nœuds).67```8laptop ──ssh──▶ M1M32 (passerelle : spbgit + mld + staging ~/dispatch/stage)9                  │  scan live (RAM libre, charge, disque, ports, runtimes)10                  │  choix du nœud (score) → rsync LAN → PM2/launchd → santé locale → route MacLustr Tunnel → santé publique → registre11                  ▼12        M3U96a · M3U96b · M2U64 · M4M64a · M4M64b · M4M36 · M2M32 · …13                  │  wg1 (WireGuard)14                  ▼15        BHS64 (OVH Québec) : Caddy https://<domaine> → 10.67.0.x:<port>   ← `tunnelctl`, piloté par mld/tunnel.py16```1718Depuis le **2026-09-10**, plus aucun ngrok : l'exposition publique est une route Caddy sur la passerelle du MacLustr Tunnel19(`tunnel.domain` dans le manifeste), que `mld deploy`/`move` **repointent vers le nœud courant** (rollback compris) et que20`mld retire` supprime. `mld heal` (LaunchDaemon `io.maclustr.mld-heal` sur M1M32, toutes les 5 min) relance ce qui manque21après une coupure ; `mld harden` rend chaque nœud capable de revenir seul (auto-login, pas de veille, MAJ off).2223## Depuis le laptop2425`~/Desktop/cluster-skill/mld <commande>` relaie à la passerelle et rapatrie le registre26(`cluster-skill/cluster-registry.json`).2728| Commande | Effet |29|---|---|30| `mld scan` / `mld nodes` | sonde (ou relit) ressources + runtimes des nœuds |31| `mld apps` / `mld show <app>` | manifestes et emplacement courant |32| `mld pick <app>` · `mld pick --ram 4 --runtime node` | classement des nœuds pour une app / un besoin |33| `mld plan [apps…]` | plan de placement global (glouton, projection des ressources) |34| `mld stage <dir-local> <app>` | pousse un dossier du laptop dans le staging de la passerelle |35| `mld deploy <app> [--node X]` | staging → nœud choisi (auto sans `--node`), démarrage, santé, registre |36| `mld move <app> [--to X] [--from Y]` | migration : pré-copie à chaud, arrêt, delta final, déploiement, santé, retrait (rollback auto) |37| `mld stop/start/restart <app>` · `mld logs <app>` · `mld health <app>` | cycle de vie |38| `mld status [--live]` · `mld registry` | registre (`~/dispatch/registry.json` sur M1M32) |39| `mld subscribers [--add app\|node NOM CHEMIN] [--remove …]` · `mld registry --push` | abonnés du registre (copie poussée à chaque sauvegarde) · rediffusion manuelle |40| `mld retire <app>` | arrête et efface la copie du nœud |41| `mld prepare <nœud> [--ka-helpers] [--no-tunnel] [--no-harden]` | installe node/pnpm/pm2/python via Homebrew, pm2 startup, marqueur, raccordement wg1 au tunnel, `harden` ; `--ka-helpers` = pousseur Ka + alias `gitsrv` + clé autorisée sur la passerelle |42| `mld bootstrap <nœud>` | Xcode CLT + Homebrew sur un Mac vierge (sudo NOPASSWD temporaire) |43| `mld import <nœud>` | brouillons de manifestes depuis les PM2 vivants d'un nœud (`~/dispatch/apps/_drafts/`) |44| `mld tunnel status [-g R9128]` | pairs WireGuard (handshake, trafic) + routes Caddy de la passerelle |45| `mld tunnel peer <nœud>…` | raccorde un Mac au hub (wireguard-tools + LaunchDaemon `io.maclustr.wireguard-wg1`, IP fixe de `/etc/maclustr-tunnel/ipmap`) |46| `mld tunnel route <app>…` · `mld tunnel rm <app>…` | (re)pointe / retire la route publique d'une app (normalement fait par deploy/retire) |47| `mld heal [--dry-run] [--apps …] [--nodes …]` | auto-réparation : 1 SSH par nœud (PM2, launchd, wg1, HTTP local) puis resurrect/start/restart/kickstart/route ; `--install` pose le LaunchDaemon (5 min) |48| `mld harden <nœud>… \| --all [--autologin\|--no-autologin]` | résilience : autorestart + restartfreeze + sleep 0 + womp, MAJ macOS/App Store off, Tailscale auto-update off, auto-login (kcpassword) sauf FileVault/nœud loué, vérif pm2 startup + wg1 |4950## Manifestes (`~/dispatch/apps/<app>.json`, mode 600)5152Un manifeste décrit *comment* tourne une app : `dir` (même chemin absolu sur tous les nœuds),53`processes` (PM2 : script, args, interpreter, cwd, env, cron_restart…), `tunnel` (`domain`, `gateway`54BHS64|R9128, `redirects` apex, `websocket` : route publique https://domain → nœud courant:port ; l'ancien bloc `ngrok`55est converti automatiquement et les vieux processus `<app>-ngrok` supprimés au start), `launchd` (plists générés), `requires` (runtimes, RAM, ports),56`placement` (`pin`, `prefer`, `avoid` + raison), `sync_excludes`, `extra_paths`, `env_overrides`57(réécriture de `.env` au déploiement), `hooks.post_sync` / `post_start`, `ka_repo` (inscription au pousseur).58Gabarits : `{{HOME}}`, `{{NODE}}`, `{{LAN_IP}}`, `{{IP:<alias>}}`.5960## Scoring6162`0.45·RAM libre après placement + 0.25·cœurs idle + 0.10·disque + 0.15·cœurs − 0.04·apps hébergées63(+0.15 prefer, +0.05 si déjà sur ce nœud)`. Inéligible : hors ligne, rôle gateway (sauf épingle),64runtime manquant, RAM libre < besoin + 2 Go, disque, port occupé (hors nœud d'origine).6566## Registre = source de vérité des emplacements (consommateurs)6768`~/dispatch/registry.json` (M1M32) est la **seule** référence « quelle app tourne sur quel nœud ».69Personne ne code plus d'emplacement en dur : les consommateurs lisent le registre.7071- **Push** : à chaque sauvegarde (deploy/move/stop/start/retire), `registry.save()` pousse une copie72  atomique chez chaque abonné de `~/dispatch/subscribers.json` (`mld subscribers` pour lister/ajouter).73  Un abonné `{"app": X, "path": P}` est résolu dans le registre lui-même (nœud + dir de X) — il suit74  les migrations. Abonnés au 2026-09-04 : `admin-ka` → `data/registry.json` (console75  administration-ka.com : sites, projets Claude Code, sweep des nœuds, CLAUDE.md de l'orchestrateur76  multi-sites) et `ka2` → `~/ka-guardian-spool/registry.json` (gardiens ka2/ka4/ka6 : nœud, dir,77  port, process PM2 de chaque service surveillé).78- **Pull** : tout nœud peut aussi tirer `ssh gitsrv cat ~/dispatch/registry.json` (alias `gitsrv`79  = M1M32, clé `ka_guardian_ed25519` autorisée). admin-ka le fait toutes les 2 min, le service launchd80  `com.ka.registry-sync` de M4M36 toutes les 2 min (chaîne zsh pure : macOS 26 LNP interdit le LAN aux81  descendants de python).82- Champs consommés : `node`, `ip`, `port`, `domain`, `dir`, `processes` (PM2), `launchd`, `status`,83  `updated`. Garder ces noms stables.8485## Ajouter un nœud86871. Activer *Session à distance* sur le Mac, autoriser la clé de la passerelle88   (`~/.ssh/id_ed25519.pub` de M1M32) et celle du laptop dans `~/.ssh/authorized_keys`.892. `echo <alias> > ~/.maclustr-node` sur le nœud ; ajouter l'alias dans `mld/config.py` (`NODES`).903. `mld discover` → `mld bootstrap <alias>` (si Mac vierge) → `mld prepare <alias>`.9192Nœud **hors LAN** (IP publique, autre utilisateur — ex. M1M64, Mac Studio loué, 2026-09-08) : le déclarer aussi93dans `REMOTE_NODES` (`host`, `user`, `sudo_pw_file`) ; `discover` s'y connecte directement au lieu de balayer94192.168.2.x, `ssh.target`/`home_of`/`sudo_pw_file` suivent l'utilisateur du nœud. Ces nœuds sont `RESERVED`95(pas de LAN vers les apps sœurs, pas de gardiens Ka) : déploiement seulement avec `--node`. Si le sshd du96fournisseur a `AllowUsers` (ex. `/etc/ssh/sshd_config.d/100-rental.conf`), ajouter un fichier97`000-maclustr.conf` avec `AllowUsers <user>` (les directives se cumulent).9899## Fichiers100101- `mld/config.py` inventaire statique, rôles, chemins, détection des runtimes102- `mld/nodes.py` découverte LAN, sonde · `mld/picker.py` score/plan · `mld/deploy.py` fetch/deploy/move/health103- `mld/render.py` ecosystem PM2 + plists · `mld/prepare.py` runtimes/bootstrap/ka-helpers · `mld/importer.py`104- `mld/tunnel.py` MacLustr Tunnel (tunnelctl : routes, pairs) · `mld/heal.py` auto-réparation + LaunchDaemon · `mld/harden.py` résilience des nœuds105- `assets/` pousseur Ka (script + plist), `wg-node-setup.sh` (raccordement WireGuard d'un Mac, copie de cluster-skill/maclustr-tunnel)106107## Résilience (coupure de courant, mise à jour, redémarrage)108109Chaîne de retour à la vie d'un nœud : `autorestart` (rallumage après coupure) → boot → **ouverture de session automatique**110(`/etc/kcpassword` + `autoLoginUser`, posés par `mld harden`) → LaunchAgents (`pm2.<user>.plist` → `pm2 resurrect`, gardiens,111pousseur) + LaunchDaemons (`io.maclustr.wireguard-wg1`, PM2 en daemon sur M2U64 et les nœuds loués) → apps up → route Caddy112inchangée sur BHS64. Filet : `mld heal` toutes les 5 min depuis M1M32 (délai de grâce 3 min après un boot, 6 actions max113par app et par heure, journal `~/dispatch/logs/heal.log`, état `heal-state.json`). Attention : **FileVault** (désactivé le 2026-09-10 sur M3U96a et114M3U96b via `fdesetup disable -inputplist`, sudo pré-authentifié) bloque le démarrage jusqu'à la saisie du mot de passe : ne jamais le réactiver sur un nœud d'apps.115Les mises à jour macOS / App Store / Tailscale sont désactivées partout (une MAJ Tailscale relance tailscaled et fait116disparaître le nœud de `*.maclustr.io`, dont les noms résolvent vers l'IP Tailscale).117118Pas de dépendance : Python 3 système (3.9) + ssh/rsync (GNU rsync via Homebrew sur la passerelle).119