Files
2026-10-04 00:30:09 +07:00

12 KiB
Raw Permalink Blame History

SSH deployment — Phase 7

./mcpack deploy применяет готовый полный immutable release через SSH/rsync. Если artifact ещё не создан, команда вызывает clean build --release; если уже есть, проверяет его checksums и provenance. Development artifact с git.dirty=true отклоняется, даже если текущий worktree уже чистый.

Подготовка цели

На host нужны Linux x86_64, Node.js 22+, доступный Docker daemon, rsync, GNU tar и zstd. Pinned Compose и небольшой Node agent с закреплённым YAML parser передаются из локального bootstrap/dependencies; системный Docker не устанавливается. У оператора должны быть права на весь runtime и его владельцев; выбранные UID/GID согласовать с владельцем каталога.

В platform/config.yaml выбрать SSH host/alias и отдельный root сборки:

server:
  host: crab@192.168.1.61
  directory: /home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
  memory: 2G
  port: 25565

Host .env готовится отдельно, вне releases, с EULA/UID/GID/memory/port и стабильным MC_PROJECT_NAME. Runtime startup читает именно этот файл; build defaults не перезаписывают host settings. Первое применение требует пустого runtime без старого binding; существующий неопознанный мир не принимается автоматически. У разных сборок разные roots, projects и ports.

SSH host key должен быть известен заранее. Используются BatchMode, StrictHostKeyChecking=yes и keepalive. Ключи/порты задать в SSH config; password-only сессия может использовать заранее аутентифицированный ControlMaster. Пароль не записывается в config, env, artifact или agent.

./mcpack deploy --artifact /absolute/full-release
# Порт/ControlMaster/known_hosts в отдельном SSH config:
./mcpack deploy --artifact /absolute/full-release --ssh-config /absolute/ssh-config
# Node из nvm, которого нет в non-interactive SSH PATH:
./mcpack deploy --artifact /absolute/full-release \
  --ssh-config /absolute/ssh-config \
  --remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node

--config FILE выбирает другой platform config. Timeout берётся из его deploy.healthcheck_timeout; он должен быть больше 10 секунд stability. Первый image/dependency download может требовать 600+ секунд. IPv6 host и другие сложные адреса сейчас задавать через SSH alias.

Выполнение и state

Под SSH удерживается одна .mcpack-runtime.lock на всё получение релиза, backup и применение. Перед передачей проверяются незавершённый deployment, source/binding, target version collision, host settings/права и свободное место. Релиз передаётся rsync в releases/.incoming-*, проверяется на host и публикуется atomic rename. Сбой передачи не останавливает исходный сервер. Agent в .mcpack/agent-UUID/ также проверяется по полной checksum coverage.

Перед применением проверяется каждый replace-managed файл прошлого release. При изменении/missing file или занятом новом managed path возвращается конфликт. Для небольших server-owned text configs выводится content diff, для остальных файлов — expected/actual hash. Неуправляемые файлы не удаляются. Нужно перенести полезные правки в source и построить новую версию; флага force для молчаливого перетирания конфигов нет.

Если тот же release уже активен, выполняется health без restart; успешный повторный deploy возвращает noop. Одинаковая версия с другим manifest/checksums отклоняется до остановки. Если активный release unhealthy, повторный deploy не перезапускает его автоматически.

При смене релиза исходный сервер останавливается, подтверждается остановка, создаётся verified snapshot всего runtime и исходного release. Только после этого apply callback меняет binding, удаляет отсутствующие в новом релизе известные replace-managed файлы и запускает target. Конфликты проверяются повторно непосредственно перед apply. Image применяет pack/server overrides при запуске; runtime-generated/unmanaged данные сохраняются.

После стабильного mc-health/status query обновляются previous-release и active-release, каждый через atomic rename. Deployment state записывается перед каждым этапом и связывает source/target identities, прежние pointers, backup path и факт apply. Пара указателей не является единым filesystem transaction: interrupted committing требует просмотра state перед recovery.

Ошибка и ручное восстановление

Любой failed/incomplete deployment-state.json блокирует следующую операцию. При прерывании SSH во время upload worker сохраняет failed state и освобождает lock; при принудительном убийстве worker/power loss lock может остаться. Не удалять stale lock, пока соответствующий host process действительно жив.

После ошибки до apply source/runtime binding не переключаются. При ошибке backup сервер может остаться остановленным; проверить logs и backup operation. После подтверждения неизменности мира можно запустить исходный release. Failed state нужно сохранить для аудита и явно разрешить только после recovery.

Если applied=false, binding совпадает с active manifest, причина исправлена и исходный сервер проверен health, сохранить failed record под отдельным именем в server root, затем повторить deploy. Для ручного config edit новый clean release должен содержать ровно текущие bytes этого managed файла — такой осознанный перенос разрешается. Любое другое содержимое остаётся конфликтом. Пример архивирования только после перечисленных проверок на host:

mv "$MC_ROOT/deployment-state.json" \
  "$MC_ROOT/deployment-state-resolved-$(date -u +%Y%m%dT%H%M%SZ).json"

Не применять этот способ к applied=true, неизвестному binding или interrupted committing: сначала восстановить/завершить реальное состояние.

После ошибки apply/startup target останавливается, source pointers не продвигаются, binding остаётся на фактически применённом target и snapshot сохраняется. Старый pack автоматически не запускается на изменённом мире. Восстановить snapshot в новый root вместе с сохранённым source release по backups.md, подготовить host settings, удалить старый контейнер, запустить восстановленную копию и проверить health. Не менять binding вручную, чтобы обойти защиту существующего мира.

Для первого неудачного deploy без snapshot использовать state/release/diagnostics: пустой мир можно повторно инициализировать отдельно либо явно завершить запуск того же target. При ошибке committing сначала проверить actual binding, Docker health и оба pointer files. Snapshot recovery реализован в Phase 8. Простое удаление failed state не является recovery.

Текущий проверенный host

На crab@192.168.1.61:2228 подготовлен root /home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike. Release 0.0.5 healthy (обновление публикации порта после Phase 7), UID/GID 1000:1000, memory 2G, MC_PROJECT_NAME=minecraft-lab-neoforge-spike. Offline mode включён.

Clean release собран в отдельном checkout и опубликован локально:

./mcpack deploy \
  --artifact dist/minecraft-lab-neoforge-spike-0.0.5 \
  --ssh-config .tools/phase7-ssh-target/config \
  --remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node

SSH config хранит только соединение/known_hosts/ControlPath. После окончания ControlMaster для password login сначала заново аутентифицировать сессию:

ssh -F .tools/phase7-ssh-target/config minecraft-lab-target true

Host settings теперь содержат MC_BIND_ADDRESS=0.0.0.0; порт 25565 публикуется на всех IPv4-интерфейсах host. В Prism подключаться напрямую к 192.168.1.61:25565, без SSH tunnel. Локальный Phase 0 сервер остаётся отдельным на 127.0.0.1:25565. Default для новых host остаётся 127.0.0.1; LAN bind выбирается явно в host .env. Проверка — network-results.md.

Сохранённый remote agent содержит scripts/server.sh. Для lifecycle на host выбрать этот agent, задать абсолютные MC_ROOT/MC_RELEASE_DIR, указать nvm Node в PATH и выполнить wrapper:

export PATH=/home/crab/.nvm/versions/node/v26.10.0/bin:$PATH
export MC_ROOT=/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
export MC_RELEASE_DIR="$MC_ROOT/releases/$(cat "$MC_ROOT/active-release")"
# Подставить один из проверенных каталогов .mcpack/agent-*:
/absolute/agent/scripts/server.sh health
/absolute/agent/scripts/server.sh logs --tail 100

При failed deployment брать фактически применяемый release из state/binding, а не автоматически active-release. Agents/releases/backups автоматически не удаляются. Свидетельства и границы проверок — phase7-results.md.