← Retour au journal

Quand séparer le Mac M4 CI ? Modèle de décision & migration dual-node (2026)

OpenClaw · 2026.06.23 · ~11 min

Nœud build Mac M4 CI et architecture dual-node OpenClaw Gateway

Début juin, nous avons empilé Xcode CI et OpenClaw Gateway sur le même Mac M4 au Canada, en tenant deux semaines avec launchd Nice=-5 et une concurrence de compilation limitée. La troisième semaine, l’équipe produit a commencé à dire dans l’IM : « le Gateway rame encore ». Les logs de build étaient verts, mais le health check curl passait de 50 ms à plus de 400 ms aux heures de pointe. Nous avons réduit la concurrence, vidé DerivedData, monté temporairement en 24 Go — le problème est passé de « deux fois par jour » à « une fois », sans disparaître.

Ce n’était pas une mauvaise config, mais le plafond du modèle same-host. Les ports ne se marchent pas dessus (20300 vs 18789), les cœurs CPU suffisent — le combat se joue sur la mémoire unifiée et le swap. Cet article prolonge le co-hébergement et le tuning launchd : quand passer en dual-node, comment dessiner la topologie, comment basculer le trafic sans casser les Channels. Mémoire et builds : documentation Apple sur le cache de compilation Xcode ; réseau : guide d’installation Tailscale.

En bref : la ligne de partage, c’est l’isolation des ressources, pas « acheter une autre machine ».

  • Quatre signaux durs — un seul suffit

    ≥50 builds/jour, ≥3 pipelines parallèles, Gateway face aux utilisateurs finaux, ou ≥3 swap Critical/semaine — continuer à tuner Nice ne fait que reporter.

    ≥50/jour

  • Dual-node minimal : build + Gateway dédié

    Nœud build 24 Go+ et gros SSD ; nœud Gateway 16 Go pour ~500 Mo en charge. Deux M4 via Tailscale ; exposition publique minimale.

    16 Go Gateway

  • Migration = état + DNS, pas réinstall

    rsync de la config, réutiliser les tokens, bascule MagicDNS Tailscale ; le build ne s’arrête pas, blue/green Gateway ≤15 minutes.

    Blue/green ≤15 min

1. Pourquoi le same-host « sature d’un coup »

Beaucoup d’équipes démarrent avec un Mac cloud pour trois rôles : xcodebuild, OpenClaw Gateway sur 18789, VNC occasionnel pour le trousseau. À faible charge, 16 Go semblent larges. Avec plus de branches, des tests UI parallèles et des Channels passés du pilote interne à l’IM prod, la courbe mémoire passe de « dent de scie » à « plateau » — dix minutes après la fin d’un build, les pages compressées restent au-dessus de 8 Go et le heap Gateway continue en swap.

Le tuning same-host (moins de parallélisme, Nice plus haut, vider le cache) réduit la probabilité de pics qui se chevauchent, pas les pics eux-mêmes. Quand builds APAC en journée et pic IM nord-américain le soir se recoupent presque toujours, vous ne optimisez plus des paramètres — vous pariez sur l’ordonnanceur. Apple Silicon n’a pas de VRAM séparée ; Xcode et le Gateway Node.js partagent le même pool physique. Dès memory_pressure en Warn, les utilisateurs sentent le jitter ; la CI voit des timeouts et des échecs de signature sporadiques.

Séparer n’est pas un mythe de « scale-up », c’est séparer des pics non compressibles : les builds uniquement sur le nœud A, la RAM Gateway jamais évincée par xcsbuildd. Même logique que dans GitHub Actions runner macOS auto-hébergé sur Mac cloud : runner dédié sans mélange bureau — ici on sépare Gateway et CI.

Pour les équipes FR/EU avec SLA documentés, le déclencheur n’est souvent pas le premier swap, mais le troisième ticket « Gateway lent » en une semaine alors que les dashboards CI restent verts. À ce stade, le same-host n’économise plus — il cache de l’indisponibilité perçue.

Expérience utile : mesurez le P95 Gateway uniquement pendant les jobs xcodebuild actifs. Si la latence triple à octuple alors que le CPU reste sous 70 %, ce n’est pas le réseau — c’est la contention RAM. Chez nous, la mémoire compressée restait >6 Go dix minutes après la fin du build ; le heap Node.js du Gateway ne pouvait pas rester résident. Chaque baisse de -jobs ou hausse de Nice réduisait le débit CI sans sauver le SLA Gateway. C’est le piège classique du same-host : deux équipes s’optimisent l’une contre l’autre sur la même machine.

2. Trois topologies : same-host, dual-node, triple-node

Classer avant d’acheter :

  • T0 same-host : un M4 pour Xcode Server / runner auto-hébergé + Gateway. <30 builds/jour, Gateway interne, latence occasionnelle acceptable.
  • T1 dual-node (focus) : nœud A CI seul (24 Go+, gros SSD) ; nœud B Gateway seul (16 Go). Tailscale ou LAN datacenter ; Channels/Dashboard → B, déclencheurs build → A.
  • T2 triple-node : ajout d’un hôte signature/upload ou tests parallèles. >150 builds/jour ou séparation dure TestFlight/compile ; T1 suffit pour la plupart des PME.

Conclusion asymétrique : ce n’est pas le M4 qui manque, c’est deux classes de SLA dans le même pool RAM. Le Gateway veut 99,9 % de réponse stable ; la CI veut du débit. Le same-host compromet les deux avec un seul SLA.

T1 dual-node : build + Gateway dédié Nœud A · CI build M4 24Go+ · 1To SSD xcodebuild / Runner DerivedData local :20300 Xcode Server Nœud B · Gateway dédié M4 16Go · 256Go OpenClaw Gateway Channels / Dashboard :18789 latence stable Tailscale <5ms SSH ingénieur / trigger CI → A ; IM / mobile / agent → B
Modèle dual-node minimal : pics build et SLA Gateway isolés physiquement

3. Same-host vs dual-node vs upgrade : comparer

Chemins de montée en charge Mac M4 CI (2026)
Dimension Continuer le tuning Nice / moins de parallèle Monter à 32 Go Plus de RAM, même topo Split dual-node CI + Gateway séparés
Cause racineAtténue le chevauchementRelève le plafond RAMIsole deux SLA
Latence GatewayJitter avec les buildsMieux, pas zéro<100 ms possible en build
Coût mensuel (ordre)Le plus basMoyen (upgrade)Moyen (+ petit 16 Go)
Complexité opsFaibleFaibleMoyenne (+1 hôte, Tailscale)
Phase adaptée<30 builds/jour2 jobs parallèles, GW interne≥50/jour ou GW externe
Checklist signaux de split (un critère → T1)
Signal Seuil Observation Faux positif
Fréquence build≥50/jourLogs CI / compteur runnerExclure archive locale manuelle
Parallèlisme≥3 builds completsxcodebuild -jobs, profondeur fileLint léger ne compte pas
Audience GatewayUtilisateurs finaux / Channels 7×24SLA produitWebhook interne seul peut attendre
Pression mémoire≥3 Critical/semaineLogs memory_pressureFuite ponctuelle à corriger d’abord
Disque>50 Go/mois + I/O wait élevédf -h + iostatNettoyer archives avant juger
L’upgrade ne remplace pas le split
32 Go same-host convient à « 2 jobs parallèles + Gateway interne ». Si le Gateway est en prod externe, plus de RAM déplace le jitter de trois fois à une fois par jour — les utilisateurs diront encore « ça rame ».

4. Scénarios : matrice de décision

  • Binôme, <20 builds/jour, Gateway perso : rester T0, tuning launchd suffit.
  • Builds APAC, Gateway pour utilisateurs IM : T1 direct. Follow-the-sun garantit presque toujours le chevauchement.
  • Runner GHA cloud, Gateway tout neuf : runner sur A, Gateway sur B ; ne pas monter 18789 sur la machine runner.
  • Upload TestFlight vs compilation sur disque : T1 d’abord ; T2 hôte signature si besoin — ne pas confondre avec ce runbook.
  • Budget une seule machine : prioriser le SLA Gateway, builds de nuit ou moins de parallèle — compromis, pas architecture long terme.

Si votre post-mortem montre que le P95 Gateway ne monte que pendant xcodebuild, ce n’est pas le réseau — c’est la contention RAM. T1 règle cela sans pause pipeline.

5. Stacks recommandés (cumulables)

  1. Dual-node minimal : Hashvps Canada M4 24 Go (A, CI) + M4 16 Go (B, Gateway) + tailnet Tailscale + openclaw doctor hebdo. Upgrade standard depuis T0.
  2. Hybride CI : A avec runner auto-hébergé GitHub Actions ; B Gateway + Channels. Orchestration GitHub, exécution macOS, Gateway hors pics build. Aligné sur la « souveraineté d’environnement » dans marché build macOS 2027.
  3. Observabilité : sonde latence Gateway sur B (curl toutes les 5 s) + snapshot memory_pressure sur A après chaque build ; deux semaines suffisent pour le ROI split auprès de la direction.

6. Pièges avant migration

  • Arrêter la CI avant de déplacer le Gateway : coûteux ; blue/green Gateway, CI continue.
  • Réinstaller au lieu de rsync : perte ~/.openclaw, tokens, appairage Channels → reconnexion générale.
  • Deux IP publiques sur 18789 : pendant le cutover, Tailscale ou DNS interne — jamais deux Gateways sur les Channels.
  • Supprimer trop tôt le Gateway sur le build : fenêtre rollback : ancien processus vivant, DNS seul change.
  • Horloge et certificats : dérive NTP >30 s → échecs token sporadiques ; jour J sudo sntp -sS time.apple.com.

7. Runbook : dual-node en sept étapes

Hypothèse : mac-ci-01 CI+Gateway ; nouveau mac-gw-02 (16 Go) Gateway seul. Tailscale installé (voir runbook ops OpenClaw).

Étape 1 : baseline (veille)

Capturer P50/P95 Gateway, nombre de builds, distribution memory_pressure. Exporter openclaw status et liste Channels.

bash — baseline latence Gateway et pression mémoire
# 延迟采样 60 次
for i in $(seq 1 60); do
  curl -o /dev/null -s -w "%{time_total}\n" http://127.0.0.1:18789/health
  sleep 5
done | sort -n | awk '{a[NR]=$1} END{print "p50="a[int(NR*0.5)],"p95="a[int(NR*0.95)]}'

memory_pressure
vm_stat | head -8

Étape 2 : nouvel hôte + Tailscale

Sur mac-gw-02 : mise à jour macOS, Homebrew, Node, Tailscale ; ping vers mac-ci-01 <5 ms. Pas de xcodebuild sur B.

Étape 3 : rsync état Gateway (fenêtre maintenance)

bash — sync config OpenClaw vers hôte Gateway
# 在原机 mac-ci-01 执行;先停 Gateway 避免写入分裂
sudo launchctl unload /Library/LaunchDaemons/com.openclaw.gateway.plist

rsync -avz --delete \
  ~/.openclaw/ \
  builder@mac-gw-02.tailnet-abc.ts.net:~/.openclaw/

# 同步 launchd plist
scp /Library/LaunchDaemons/com.openclaw.gateway.plist \
  builder@mac-gw-02.tailnet-abc.ts.net:/tmp/

Étape 4 : démarrer Gateway sur le nouvel hôte

bash — charger le service et vérifier health
sudo cp /tmp/com.openclaw.gateway.plist /Library/LaunchDaemons/
sudo launchctl load /Library/LaunchDaemons/com.openclaw.gateway.plist

openclaw doctor
curl -s http://127.0.0.1:18789/health
sudo lsof -iTCP:18789 -sTCP:LISTEN

Étape 5 : trafic — MagicDNS ou reverse proxy

Hostname Gateway d’équipe (ex. gateway.tailnet-abc.ts.net) vers le nouvel hôte ; mobile et Channels sur le nouveau nom MagicDNS. Ne pas redémarrer le Gateway sur l’ancien hôte pendant le cutover.

Étape 6 : alléger le nœud build

Après validation Channels sur B : décharger Gateway launchd sur A, remonter parallèle CI à 5–6 (24 Go).

bash — retirer Gateway du nœud build
# mac-ci-01:确认已无流量打到 18789 后
sudo launchctl unload /Library/LaunchDaemons/com.openclaw.gateway.plist
sudo mv /Library/LaunchDaemons/com.openclaw.gateway.plist \
        /Library/LaunchDaemons/com.openclaw.gateway.plist.bak

defaults write com.apple.dt.Xcode \
  IDEBuildOperationMaxNumberOfConcurrentCompileTasks 6

Étape 7 : observer 48 h + rollback

Conserver backup .openclaw et plist.bak sept jours. Si P95 >200 ms ou Channels down : DNS retour, ancien plist — file CI intacte. Détails : onboarding OpenClaw Mac distant.

8. FAQ

Q1. 16 Go seul — rôles logiques sans second hôte ?

Ne remplace pas un split physique. Décaler dans le temps (builds la nuit) aide peu ; follow-the-sun ou Channels 7×24 recréent les pics. La séparation logique sert à prouver le besoin d’achat en mesurant la latence après déplacement Gateway.

Q2. Tailscale obligatoire ?

Non, mais fortement recommandé. LAN fournisseur, WireGuard ou tunnel SSH fonctionnent ; Tailscale apporte MagicDNS, ACL, peu d’ops. Deux M4 Hashvps Canada : RTT souvent <2 ms — suffisant pour webhooks build B → A.

Q3. Dual-node vs un seul 32 Go ?

Souvent proche : 24 Go build + 16 Go petit hôte vs 32 Go solo. L’enjeu est un SLA Gateway démontrable ; les équipes externes chiffrent les minutes d’indisponibilité, pas seulement le loyer.

Q4. Runner GHA hébergé — Mac dédié quand même ?

Souveraineté d’environnement. macOS hébergé à la minute pour les pics ; Mac cloud auto-hébergé dès >50 builds/jour fixes avec contrôle keychain/DerivedData. Gateway reste indépendant du modèle runner.

Q5. Rollback le plus rapide ?

DNS retour + launchctl load sur l’ancien hôte. Pas de gros builds le jour du rollback ; health + un message Channel, puis CI. Double copie d’état jusqu’à métriques stables.

Q6. Quelles métriques convainquent la direction ?

Deux semaines suffisent. Sur l’ancien setup, enregistrez P95 Gateway et événements memory_pressure Critical ; après migration, les mêmes mesures sur hôtes séparés. Si le P95 en pic de build passe de 400 ms à <100 ms et que Critical tombe à zéro sur l’hôte Gateway, le ROI est plus clair qu’un upgrade CPU. Documentez aussi le temps de build gagné grâce à plus de tâches de compilation parallèles sur 24 Go sans risque Gateway.

9. Conclusion

Séparer le Mac M4 CI n’est pas un échec, c’est l’étape naturelle après un tuning same-host réussi : build et Gateway sont tous deux nécessaires, mais plus dans la même barrette. Quatre signaux, T1 pour isoler les SLA, sept étapes blue/green — la ligne de partage, c’est l’isolation, pas le nombre de machines.

Si vous êtes coincés entre « Gateway lent » et « build ne doit pas s’arrêter », un hôte Gateway 16 Go dédié corrige plus que du Nice. Builds sur cloud 24 Go, agents et utilisateurs sur 18789 stable — topologie quasi-prod abordable pour petites équipes en 2026.

Dual-node : deux Mac mini cloud

Après séparation, le nœud build veut 24 Go et gros SSD pour DerivedData ; le Gateway 16 Go suffit en 7×24 faible latence. Hashvps Canada M4 bare metal, IPv4 dédiée, SSH/VNC prêts — deux machines même région, faible latence Tailscale, point de départ T1 souvent meilleur qu’un dur 32 Go sur un seul hôte.

Vous planifiez le passage same-host → dual-node ? Hashvps Mac mini M4 cloud est un excellent second nœud Voir les offres et séparer CI et Gateway proprement.

Hashvps · Mac Cloud

Séparer build et Gateway pour plus de stabilité

Nœud CI 24GB + hôte Gateway 16GB, macOS bare metal, IP dédiée, Tailscale.

Voir les offres
Offre