172 lines
12 KiB
Markdown
172 lines
12 KiB
Markdown
# 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 сборки**:
|
||
|
||
```yaml
|
||
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.
|
||
|
||
```bash
|
||
./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**:
|
||
|
||
```bash
|
||
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](backups.md), подготовить host settings, удалить старый контейнер,
|
||
запустить восстановленную копию и проверить health. Не менять binding вручную,
|
||
чтобы обойти защиту существующего мира.
|
||
|
||
Для первого неудачного deploy без snapshot использовать state/release/diagnostics:
|
||
пустой мир можно повторно инициализировать отдельно либо явно завершить запуск
|
||
того же target. При ошибке `committing` сначала проверить actual binding,
|
||
Docker health и оба pointer files. Snapshot recovery реализован в
|
||
[Phase 8](rollback.md). Простое удаление 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 и опубликован локально:
|
||
|
||
```bash
|
||
./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 сначала заново аутентифицировать сессию:
|
||
|
||
```bash
|
||
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](network-results.md).
|
||
|
||
Сохранённый remote agent содержит `scripts/server.sh`. Для lifecycle на host
|
||
выбрать этот agent, задать абсолютные MC_ROOT/MC_RELEASE_DIR, указать nvm Node
|
||
в PATH и выполнить wrapper:
|
||
|
||
```bash
|
||
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](phase7-results.md).
|