6 月初、Xcode CI と OpenClaw Gateway を同一のカナダ M4 に詰め込み、launchd の Nice=-5 と並列コンパイル制限で 2 週間凌いだ。3 週目から、業務チームが IM で「Gateway がまた重い」と文句を言い始める——ビルドログは正常、curl ヘルスチェックだけがピーク時に 50ms から 400ms 超へ漂う。並列数を下げ、DerivedData を掃除、一時的に 24GB プランへ上げても、問題は「1 日 2 回」から「1 日 1 回」に減っただけだった。
これは設定ミスではなく、同一ホストのリソースモデルが天井に達したサインだ。ポートは衝突しない(20300 vs 18789)、CPU コアも足りる。本当に争うのはユニファイドメモリと swap。本稿は 同一ホスト配置と launchd チューニング の次の段階——いつ二ノードに分けるか、トポロジの描き方、Channels を壊さず切り替える移行手順。公式のメモリ・プロセス挙動は Apple の Xcode ビルドキャッシュドキュメント、Tailscale 組網は Tailscale インストールガイド を参照。
結論先行:分岐点はリソース分離であり、「もう 1 台買う」ことではない。
-
4 つのハードシグナル、1 つでも該当なら分割
ビルド ≥50 回/日、並列 ≥3 本、Gateway がエンドユーザー向け、または週内 swap Critical ≥3 回——同一ホストで Nice をいじり続けるのは先延ばし。
≥50 回/日
-
最小二ノード:ビルド機 + Gateway 専用機
ビルドノードは 24GB+ と大容量 SSD;Gateway ノードは 16GB で常駐 ~500MB 十分。2 台の M4 を Tailscale で内網接続し、公開は必要な入口だけ。
16GB Gateway 専用
-
移行の核心:状態と DNS、再インストールではない
設定ディレクトリを rsync、token 再利用、Tailscale MagicDNS で切替。ビルド機は止めず、Gateway はブルー/グリーン 15 分以内。
ブルー/グリーン ≤15分
1. なぜ同一ホスト構成が「突然足りなくなる」のか
多くのチームの最初のクラウド Mac は 3 役:xcodebuild、OpenClaw Gateway 18789、たまに VNC でキーチェーン操作。初期負荷が低いと 16GB は余裕に見える。ブランチ増加、UI テスト並列、Channels が社内試験から本番サービスへ——メモリ曲線が「のこぎり」から「台地」へ。ビルド終了後 10 分も compressed ページが 8GB 超、Gateway の heap が swap に押し出される。
同一ホスト最適化(並列低下、Nice 引き上げ、キャッシュ掃除)はピーク重複確率を下げるだけで、ピーク自体は消せない。時間軸で 2 つのピークがほぼ必ず重なる——例:APAC 昼のビルド、北米夜の IM ピーク——なら、パラメータ調整ではなくスケジューラへの賭けになる。Apple Silicon のユニファイドメモリに独立 VRAM バッファはなく、Xcode と Node.js Gateway は同一物理プールを奪い合う。memory_pressure が Warn に入ると、ユーザーには遅延ジッターが体感され、CI にはタイムアウトと署名ステップ失敗が散発する。
分割の本質は「スケール神話」ではなく、圧縮不能なピークを分離すること:ビルドピークはビルドノードだけに、Gateway 常駐メモリは xcsbuildd に永遠に奪われない。GitHub Actions セルフホスト macOS Runner をクラウド Mac に載せる 記事の「専用 Runner にデスクトップを混ぜない」と同じ論理で、ここでは Gateway と CI を分ける。
2. 3 種のトポロジ分類:同一ホスト、二ノード、三ノード
先に分類してから選定——いきなり「2 台追加」は避ける:
- T0 同一ホスト共存:1 台の M4 で Xcode Server / セルフホスト Runner + Gateway。日次ビルド <30、Gateway 社内のみ、偶発遅延許容向け。
- T1 二ノード(本稿の主役):ノード A が CI 専任(24GB+、大 SSD);ノード B が Gateway 専任(16GB で可)。Tailscale または DC 内網で Channels / Dashboard は B、ビルドトリガーは A。
- T2 三ノード:T1 に「署名/アップロード専用機」または「並列テスト専用機」を追加。日次ビルド >150、TestFlight アップロードとコンパイルのハード分離が必要なときのみ;多くの中小チームは T1 で十分。
非対称結論を再掲:問題は M4 の性能不足ではなく、SLA の異なる 2 サービスを同一メモリプールに縛ったこと。 Gateway は 99.9% の安定応答;CI はスループット。同一ホストは 1 つの SLA で 2 目標を妥協している。
3. 同一ホスト vs 二ノード vs スペックアップ:比較の仕方
| 観点 | 同一ホスト調整継続 Nice / 並列低下 | 単機 32GB へ メモリ増、トポロジ不変 | 二ノードへ分割 CI + Gateway 分機 |
|---|---|---|---|
| 根本原因 | ピーク重複の緩和 | メモリ上限の引き上げ | 2 種 SLA の分離 |
| Gateway 遅延 | ビルドに連動してジッター | 改善するが消えない | ビルド中も <100ms 維持可 |
| 月額(概算) | 最低 | 中(アップグレード差額) | 中(+16GB 小型機) |
| 運用複雑度 | 低 | 低 | 中(+1 台、Tailscale) |
| 向く段階 | 日次ビルド <30 | 並列 2 本、社内 Gateway | ≥50 回/日または対外 Gateway |
| シグナル | 閾値 | 観測方法 | 誤検知の除外 |
|---|---|---|---|
| ビルド頻度 | ≥50 回/日 | CI ログ / Runner カウント | 手動ローカル Archive は除外 |
| 並列本数 | ≥3 本フルビルド | xcodebuild -jobs とキュー深度 | 軽量 lint は 1 本に数えない |
| Gateway 利用者 | エンドユーザー / 7×24 Channels | プロダクト SLA 要件 | 純社内 webhook は猶予可 |
| メモリ圧力 | 週内 Critical ≥3 回 | memory_pressure ログ | 一回性リークは先に調査 |
| ディスク競合 | 月間成果物 >50GB かつ I/O wait 高 | df -h + iostat | archive 掃除後に再判定 |
4. シナリオ別の選び方:意思決定マトリクス
- 2 人チーム、日次ビルド <20、Gateway 自用のみ:T0 のまま、同一ホスト launchd チューニング で十分。
- APAC でビルドトリガー、Gateway が IM ユーザー向け:即 T1。Follow-the-sun ではピークがほぼ必ず重なり、同一ホストは負ける。
- GitHub Actions クラウド Runner あり、Gateway 立ち上げ直後:Runner は A、Gateway は B;Runner 機に 18789 を載せない。
- TestFlight アップロードとコンパイルがディスクを奪い合う:まず T1;まだ足りなければ T2 で署名/upload 専用機。TestFlight 専記事を参照し、本稿と混同しない。
- 予算が 1 台のみ:Gateway SLA を優先、ビルドは夜間バッチか並列低下——妥協であり長期アーキテクチャではない。
5. 推奨スタック(重ね合わせ可)
- 最小二ノードスタック:Hashvps カナダ M4 24GB(A、CI)+ M4 16GB(B、Gateway)+ Tailscale tailnet +
openclaw doctor週次。T0 から上げる大多数のチーム向け。 - CI ハイブリッドスタック:A にセルフホスト Runner で GitHub Actions;B に Gateway + Channels。編成は GitHub、実行面は macOS、Gateway はビルドピークを背負わない。2027 macOS ビルド市場の変化 の「環境主権」と同方向。
- 可観測スタック:B に Gateway 遅延プローブ常駐(
curl5 秒間隔)+ A のビルド終了 hook でmemory_pressureスナップショット;2 週間のデータで分割 ROI を経営層に示せる。
6. 移行前のよくある誤解
- ビルドを止めてから Gateway 移行:ビルド停止コストが高い;Gateway をブルー/グリーン、ビルドは継続。
- 新機を再インストールのみで rsync しない:
~/.openclaw、token、Channels ペアリング喪失で全員再ログイン。 - 公網で 18789 を 2 IP 同時公開:切替期は Tailscale または内網 DNS;2 Gateway が同時に Channels を受けない。
- ビルド機で Gateway ディレクトリを早く削除:ロールバック窓では旧機 Gateway プロセスを残し、DNS だけ切替。
- 時刻と証明書の軽視:2 台の NTP ずれ >30s で token 検証が散発失敗;移行日は先に
sudo sntp -sS time.apple.com。
7. 実装手順:7 ステップで二ノード移行
以下 Runbook の前提:旧機 mac-ci-01 で CI+Gateway 同居;新規 mac-gw-02(16GB)を Gateway 専用に購入。Tailscale 済み(OpenClaw 日常運用 Runbook 参照)。
ステップ 1:ベースラインスナップショット(移行日前日)
旧機で Gateway 遅延 P50/P95、ビルド回数、memory_pressure 分布を記録し分割後と比較。openclaw status と Channels 一覧のスクリーンショットを保存。
# 延迟采样 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
ステップ 2:新機初期化と Tailscale 参加
mac-gw-02 で macOS 更新、Homebrew、Node、Tailscale;mac-ci-01 との相互 ping <5ms を確認。新機で xcodebuild は走らせない。
ステップ 3:rsync で 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/
ステップ 4:新機で Gateway 起動とローカル検収
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
ステップ 5:切替——Tailscale MagicDNS またはリバースプロキシ
チームが使う Gateway ホスト名(例 gateway.tailnet-abc.ts.net)を新機へ;モバイルと Channels 設定を新 MagicDNS 名へ。切替中は旧機で Gateway を再起動しない。
ステップ 6:ビルド機の負荷軽減
Channels と Dashboard が新機で正常なら、旧機から Gateway launchd をアンロードし CI 用メモリを解放。並列コンパイルを 5–6(24GB 機)に戻せる。
# 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
ステップ 7:48 時間観察とロールバック手順
旧機の .openclaw バックアップと plist.bak を 7 日保持。新機 Gateway P95 >200ms または Channels 切断なら DNS を旧機へ戻し launchctl load——CI キューは触らない。新 Gateway セットアップは OpenClaw リモート Mac 導入 と照合。
8. FAQ
Q1. 16GB 1 台だけのとき、「論理ロール」分割で 2 台目を買わずに済む?
物理分割の代替にはならない。 時間帯ずらし(夜間ビルド、昼 Gateway)は可能だが、Follow-the-sun や 7×24 Channels では再びピーク衝突する。論理分割の価値は「Gateway を移したら遅延が戻るか」を検証し、調達に 2 台目の必要性を示すこと。
Q2. 二ノードに Tailscale は必須?
必須ではないが強く推奨。 同一クラウド内網、WireGuard 自前、SSH トンネルも可;Tailscale の強みは MagicDNS、ACL、低運用。Hashvps カナダ M4 2 台が同リージョンなら内網 RTT は通常 <2ms、Gateway から A 機のビルド webhook を叩くのに十分。
Q3. 二ノードは単機 32GB よりどれくらい高い?
プラン次第だが、しばしば「24GB ビルド機 + 16GB 小型機」vs「単機 32GB」に近い。 重要なのは絶対差額ではなく Gateway SLA の証明可能性——対外サービスチームはダウンタイムで計算し、月額だけ見ない。
Q4. GitHub Actions ホスト Runner があれば Mac 自前は不要?
環境主権次第。 ホスト macOS は分課金で波峰向き;固定日次ビルド >50 で鍵と DerivedData を握るならクラウド Mac 自前。Gateway は独立ノード推奨、Runner ホストかどうかは無関係。
Q5. 移行失敗時の最速ロールバックは?
DNS を戻す + 旧機で launchctl load。 ロールバック日に大規模ビルドは走らせない;先に Gateway health と 1 本の Channel メッセージを確認してから CI を開放。状態ディレクトリは新指標が安定するまで二重保持。
9. まとめ
Mac M4 CI の分割は失敗ではなく、同一ホスト最適化が成功したあとの自然な次段階——ビルドと Gateway を同時に必要と証明したが、物理的にはもう 1 枚のメモリに押し込むべきではない。4 つのハードシグナル、T1 二ノードで SLA 分離、7 ステップ Runbook でブルー/グリーン——分岐点はリソース分離であり、台数ではない。
「Gateway がまた重いがビルドは止められない」窓にいるなら、Nice をいじり続けるより 16GB Gateway 専用機を足す方が根治に近い。ビルドは 24GB クラウドで走らせ、Agent とユーザーは別機で安定した 18789——2026 年の小チームが負担できる「準本番」トポロジだ。
二ノード分割は、2 台のクラウド Mac mini から
Xcode CI と OpenClaw Gateway を分機すると、ビルド機は 24GB と大 SSD で DerivedData を背負い、Gateway 機は 16GB で 7×24 低遅延稼働で足りる。Hashvps カナダ M4 ベアメタル、専用 IPv4、SSH/VNC 即利用、同リージョン 2 台で低遅延組網——T1 二ノードの出発点として、単機 32GB 強化より swap ジッターを分離しやすい。
同一ホストから二ノードへの移行を計画中なら、 Hashvps クラウド Mac mini M4 はコスパの良い 2 台目の起点—— プランを見る 、Gateway と CI をそれぞれの居場所へ。