Матрица решений · коллаборация и эксплуатация · 2026

Общий удалённый Mac: Nomad-стиль лёгкая очередь vs cron-веер — задержка, вытеснение и чеклист конфликтов сборки

10 апреля 2026 г. Команда Meshmac около 9 минут чтения

Когда малый коллектив смешивает CI и ручные скрипты на одном или паре арендованных Mac, типичная ошибка — «добавим пять строк в crontab каждую минуту». В итоге пересекаются два xcodebuild, два пайплайна пишут в общий кэш CocoaPods, и дежурный не понимает, кто владеет CPU и диском. Путь в духе Nomad (единый словарь: job, allocation, политика перезапуска, spread) дороже в первый месяц, но даёт предсказуемое вытеснение и аудируемую историю. macOS не Linux: нормально вынести плоскость управления на Linux-ВМ и выполнять работу на Mac по SSH или API runner — это всё ещё контраст с хаосом разрозненных cron. Синхронизацию очередей при нескольких узлах сверяйте с единым деплоем и синхронизацией очереди задач OpenClaw MeshMac; социальные сбои очереди и «призрачную» занятость — с FAQ по очереди и блокировкам на общем Mac; практику flock — с FAQ по очереди сборок и flock.

Границы сценариев

Cron-веер уместен, если (а) все записи ведёт одна учётная запись эксплуатации, (б) задания короткие и идемпотентны, (в) каждая строка оборачивает одну критическую секцию в flock с жёстким лимитом по часам, (г) команда согласна с джиттером порядка минуты. Как только несколько «сквадов» кладут работу в общий пул, CI конкурирует с людьми за полосу codesign, а нужны приоритет (релиз впереди ночных скриншотов) и справедливость (ни один cron не голодает из‑за другого), одного cron недостаточно: без единой модели конкуренции знание «кого убивать в три ночи» остаётся в головах.

Nomad-стиль (буквальный HashiCorp Nomad или другой оркестратор с теми же идеями) окупается, когда нужно выразить максимум параллелизма по ключу ограничения: «на хосте только одна полоса подписи», «два разрешения SwiftPM, но один архив», — и когда перезапуск и переназначение должны быть декларативными, а не скопированными из десяти bash-файлов. Mac в этой схеме — исполнитель; кластерный менеджер часто живёт off-box. Это не компромисс из слабости, а разделение: обновляйте control plane там, где это скучно, оставляя Xcode там, куда его поставил Apple.

Жёсткая граница: если две job могут трогать один и тот же изменяемый каталог без описанного домена блокировки, вы уже в зоне «очередь + замки», а не «просто cron». Планируя второй узел, заранее решите, как submitter узнаёт, куда отправить задачу: иначе вы получите два независимых veерa cron и удвоение гонок.

Сравнительная таблица моделей очереди

Столбцы — это контракты эксплуатации: что дежурный может восстановить в 02:00 без чтения дюжины crontab и личных wiki.

Измерение Cron-веер + обёртки Nomad-стиль, лёгкая очередь
Задержка до старта Ограничена периодом cron (часто 60 с); укорочение периода усиливает стадо процессов. Очередь или событие; при тёплых воркерах p50 часто < 1 мин.
Контроль параллелизма Вручную: flock, счётчики, Redis — легко разъехаться. Декларативно: task groups, count, spread, affinity; потолки в одном месте.
Вытеснение / изоляция Нет, пока не построите сигналы сами; «убить длинную job» — фольклор. Политики restart/reschedule, классы приоритета, журнал по allocation.
Состояние и наблюдаемость Логи разбросаны; correlation id редко согласованы. Центральная история job; метки job / alloc.
Нагрузка на сопровождение Дёшево поставить, дорого удерживать при росте команды. Выше входной порог; меньше сюрпризов при пятом инженере.
Типичный отказ Тихое перекрытие + порча кэша. Неверно смоделированные ограничения (starvation) — правится в спеке, не в чате.

Исполняемые конфигурационные фрагменты

Ужесточённый cron: одна полоса, неблокирующий flock -n на занятом замке, стеновые часы через 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

В начале run-ci-dequeue.sh проверьте глубину очереди: если длина списка Redis, SQLite или другого брокера > 20, завершайтесь с кодом 77 и алертом — не оставляйте клиентов висеть без обратной связи.

Периодический batch в стиле Nomad (иллюстративный HCL; control plane отдельно, внутри скрипта — SSH или runner на Mac):

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"
      }
    }
  }
}

prohibit_overlap = true для периодики, отображающейся на одну физическую полосу архива — это аналог правила «никогда не кладите два полных архива в один корень DerivedData». Реальную нагрузку на Xcode оставьте внутри run-ci-dequeue.sh, чтобы политика жила в одном job spec.

Права и блокировки

Планировщик только расставляет задачи; macOS применяет права. Выделите учётку buildlane (или по команде) для неинтерактивных сборок, отделив её от консольных пользователей; доступ к связке ключей подписи — только для этой учётки. LaunchDaemons от root должны вызывать sudo -u buildlane, а не запускать Xcode от root.

  • Гранулярность замков: один файл .lock на общий ресурс (Pods, npm-кэш, слот Simulator), а не один гигантский замок на весь диск, если вы намеренно не сериализуете всё.
  • Метаданные очереди: рядом с lock храните JSON с job_id, commit, runner_name, чтобы дежурный видел, кто должен ещё работать.
  • Изоляция после вытеснения: при убийстве allocation обеспечьте в trap удаление частичных .xcarchive и снятие замков — планировщик повторит попытку, диск не должен зарастать сиротами.

Чеклист приёмки конфликтов сборки

  • Два одновременных dry-run на одном пути checkout дают нет пересечения записей (или пересечение только внутри секции с flock ≤ 120 с).
  • CI и ручные скрипты проходят только через один и тот же вход dequeue — нет «тайного» cron, обходящего очередь.
  • Медиана ожидания замка укладывается в опубликованный SLA; алерт при p95 > 15 минут.
  • После kill активной job не остаётся зависшего владельца flock (проверка PID, затем задокументированное снятие lock).
  • После пяти ночных архивов подряд свободно > 15% диска; иначе — разделение полос или дополнительный узел.

Таймауты и параметры отката

Скопируйте в внутренний runbook и подстройте по измеренным p95 длительностям сборки.

Параметр База: cron + shell База: Nomad-стиль
Стеновые часы job timeout 45m лёгкая полоса; 90m тяжёлый архив. Согласуйте kill_timeout с теми же числами; не оставляйте дефолт 30 с для Xcode.
Ожидание замка flock -w 180 для менеджеров пакетов; при таймауте — отказ с метрикой. Ожидание в воркере; перезапуск без busy-loop — минимум delay = "30s".
Откат повторов Экспонента: 30 с, 60 с, 120 с с потолком; множитель джиттера 0,8–1,2. reschedule: max_delay = "5m", delay_function = "exponential".
Потолок ожидающих До 20 pending на полосу; отказ с понятным HTTP-кодом или exit. Дублируйте на submitter и в брокере; Nomad не угадывает ваш SLA.

Пример джиттера в bash 5+: sleep $((30 + RANDOM % 30)) перед повторной попыткой dequeue. Логируйте attempt, next_backoff_s и reason на каждой ветке — запросы к Grafana останутся простыми.

FAQ

Обязательно ли ставить Nomad на сам Mac?
Нет. Частая схема — планировщик на Linux, Mac как удалённый исполнитель. Цель — один декларативный контракт на перекрытие, перезапуск и историю, а не марка бинарника.
Cron «проще» — почему очередь всё равно ломается?
Простота установки не равна простоте эксплуатации. Веер без центрального лимита глубины порождает невидимый бэклог; учащение cron только стягивает больше процессов к тем же замкам.
Где должны жить правила вытеснения?
В слое планировщика для жизненного цикла аллокаций и в runbook для человеческих сессий (VNC, пара программирования). Не полагайтесь на «все знают, что в полдень архив не гоняем».
Чем отличается откат в shell от блока reschedule в Nomad?
Shell-откат — экспонента с джиттером только на идемпотентных шагах. Задержки reschedule в Nomad — между попытками аллокации; держите max_delay меньше половины таймаута, видимого в CI, чтобы пайплайн падал быстро, а не висел до глобального лимита.

Cron-веер — рабочий хак для одной полосы, если глубина, таймауты и домены flock ведутся как прод-код. Nomad-стиль окупается, когда ключи конкуренции, политика restart и журнал должны соответствовать тому, как команда реально делит время Xcode. В обоих случаях права macOS и домены блокировок не опциональны. Если p95 ожидания не проходит чеклист, масштабируйте узлы, а не наращивайте рискованный параллелизм на одном диске: разнесите архив и интерактив, добавьте второй билдер в пуле MeshMac-класса.

Ознакомьтесь с контекстом пула на главной MeshMac; тарифы и оформление без входа в аккаунт — на странице покупки и планов; SSH, VNC и онбординг — в центре помощи. Для сценариев с несколькими узлами и общими ресурсами сборки выберите пакет с мультиузлом или выделенной сборочной ёмкостью раньше, чем снова поднимать параллелизм на одном хосте.

Очередь · мультиузел · общая сборка

Сначала узлы и полосы — потом спор о параллелизме

Второй арендованный Mac под codesign или ночные архивы почти всегда выгоднее третьего одновременного xcodebuild на одном томе. Сравнение планов (без логина) · Помощь · Блог.

Тарифы без входа