12 KiB
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.