判断マトリクス · 協働・運用 · 2026

共有リモートMac:Nomad 式軽量キュー vs cron 扇出

2026年4月10日 Meshmac 読了 約7分

小チームが1台または少数台の共有リモートMacで CI と手元スクリプトを混ぜるとき、cron で複数行を扇出するか、Nomad 型(宣言的な並行・再起動・履歴)に寄せるかで、初動遅延先奪の説明責任が変わります。macOS は Linux ではないので制御面を別ホストに置く構成は普通です。第二ノード以降のキュー同期はデプロイとタスクキュー同期、座席と予約の運用はキュー・ロック FAQflock 実装はビルド待ち行列 FAQと併読してください。

シナリオの境界

cron 扇出は、(1) エントリを単一の運用アカウントが管理し、(2) 各ジョブが短く冪等、(3) クリティカル区間を flock壁時計上限で囲み、(4) 分単位のジッターを許容できるときに妥当です。複数スカッドが投入し、CI と人間が優先度・公平性を争う段階では、cron だけでは「誰が CPU を取っているか」が属人化します。

Nomad 型(HashiCorp Nomad そのものでも、同じ概念のオーケストレータでも可)は、「アーカイブはホストあたり1本」「SwiftPM 解決は2並列まで」のように制約キーごとの最大並列を宣言し、再起動・再配置を HCL 等に残せる点が価値です。Mac は実行端末、スケジューラは退避しやすい OSに置くのが現実的です。境界の線引きはシンプルで、同一の可変ディレクトリをドキュメント化されたロック領域なしに触れる経路が2本あるなら、すでに「キュー+ロック」の設計対象です。

キューモデル対照表

深夜のオンコールがcrontab を12個読まずに状態を復元できるかが、運用コントラクトです。

観点 cron 扇出+ラッパ Nomad 型の軽量キュー
初動遅延 周期に依存(多くは60秒)。周期短縮はヘルド増。 キュー駆動なら p50 は数十秒〜1分台も現実的。
並行制御 手作り:flock/Redis カウンタ等、ドリフトしやすい。 宣言的:count・spread・affinity で上限が一箇所。
先奪・隔離 自前シグナルなければ暗黙知に依存。 再起動・再スケジュール・優先クラスと割当ログ。
状態・観測性 ログ分散、相関 ID が揃いにくい。 ジョブ履歴、joballoc ラベルでメトリクス化しやすい。
典型故障 静かな重複+キャッシュ破損。 制約モデル誤りによる飢餓(HCL で修正可能)。

実行可能な設定要点(コピー用)

cron:単一レーン・非ブロッキング flock 拒否ではなく待ちを使い分け、壁時計は timeout で必ず殺す。

# crontab 断片(buildlane ユーザのみ)
*/1 * * * * buildlane /usr/bin/flock -n /var/run/meshmac/build.lane.lock \
  timeout 45m /usr/local/bin/run-ci-dequeue.sh >>/var/log/meshmac/dequeue.log 2>&1

dequeue 先頭で待ち深度を検査:Redis リストや SQLite キュー長が 20 を超えたら exit 77 でページ(クライアントを吊り下げない)。

Nomad:物理アーカイブ1本に対応する periodic には prohibit_overlap = true。実処理は run-ci-dequeue.sh 内で Mac へ SSH/Runner API。

job "meshmac-mac-build" {
  datacenters = ["dc1"]
  type        = "batch"
  periodic {
    cron             = "*/2 * * * *"
    prohibit_overlap = true
  }
  group "lane" {
    count = 1
    task "dequeue" {
      driver = "exec"
      config { command = "/usr/local/bin/run-ci-dequeue.sh" }
      resources { cpu = 500 memory = 512 }
      restart {
        attempts = 3
        interval = "30m"
        delay    = "30s"
        mode     = "delay"
      }
    }
  }
}

権限とロック

スケジューラは並べるだけで、macOS の権限境界は別問題です。非対話ビルドは buildlane 等の専用アカウントに寄せ、コード署名はそのキーチェーンだけに閉じます。root の LaunchDaemon からは sudo -u buildlane で Xcode を起動してください。

  • ロック粒度:Pods/npm キャッシュ/シミュレータ枠など共有資源ごと.lock。ディスク全体を1ロックにするのは意図的直列化のときだけ。
  • メタデータ:ロック旁に job_idcommitrunner_name の JSON を置き、オンコールが「まだ誰の所有か」を読めるようにする。
  • 先奪後の掃除:割当 kill 時は trap で部分 .xcarchive とロック解放。スケジューラが再試行してもディスクが孤児で埋まらないようにする。

ビルド衝突の検収チェックリスト

  • 同一チェックアウトでドライラン2本が書き込み競合なし(または flock 区間120秒以内)。
  • CI と手動が同じ dequeue 入口のみ通る(バイパス cron なし)。
  • ロック待ちの中央値が社内 SLA 内、p95 > 15分ならアラート。
  • 実行中ジョブ kill 後にゾンビ flockが残らない(PID 確認と解除手順が Runbook にある)。
  • 夜間アーカイブ5連続後も空き容量 > 15%。満たせなければレーン分離またはノード追加。

タイムアウトと退避パラメータ

以下は出発点です。p95 ビルド時間の実測で上書きしてください。

パラメータ cron+シェル基準 Nomad 型基準
ジョブ壁時計 timeout 45m 軽量、重いアーカイブは 90m kill_timeout を同水準に。Xcode で 30s 既定は避ける。
ロック待ち パッケージマネージャは flock -w 180、TO 時はメトリクス付きで失敗。 待ちはワーカー側。再起動は delay = "30s" 未満でビジーループにしない。
再試行退避 指数:30s→60s→120s 上限、係数 0.8–1.2 のジッター。 reschedulemax_delay = "5m"delay_function = "exponential"
待ち深度上限 レーンあたり pending 20、拒否は HTTP/exit で意味のあるコードに。 投入側+キューで強制(Nomad は SLA を推測しない)。

bash 5+ のジッター例:sleep $((30 + RANDOM % 30))。各分岐で attemptnext_backoff_sreason をログに残し、ダッシュボード検索を単調に保ちます。

FAQ

Mac 本体に Nomad が無いとダメ?
いいえ。制御面を Linux に置き Mac を実行端末にするのが一般的です。ブランドより宣言的な重複禁止と履歴が目的です。
cron の方が簡単なのに、なぜキューが爆発する?
導入時の簡単さと、定常運用の簡単さは一致しません。扇出に中央の深度上限がないと隠れバックログが増え、周期を詰めると同じロックへ群がります。
flock だけで Nomad は不要?
直列化には有効ですが、優先度・履歴・複数ホスト間の整合は別レイヤです。第二台目を足すならスケジューラまたは単一の dequeue デーモンに寄せた方が説明が楽です。

cron は単一レーンの合理的ハックになり得ますが、深度・タイムアウト・ロック領域を本番コード同様に管理する前提です。Nomad 型は制約キーと再起動ポリシーをチームの実態に合わせるほど効きます。どちらでも macOS 権限と flock ドメインは省略できません。p95 待ちが検収を超えるなら、無理に並列を上げず多ノード化や共有ビルド専用リソースの追加を検討してください。トップでプールの前提を確認し、購入・プランはログイン不要で比較できます。接続手順はヘルプセンターを参照してください。

キュー · 共有ビルド

衝突を増やす前にノードを増やす

アーカイブ用とインタラクティブ用で Mac を分け、キュー方針を正直に保てます。プラン比較(ログイン不要)ヘルプブログ一覧

購入・プランへ