Files
docs/architecture/deployment.md
T
ksv741 0ee54ca952 docs: record minimal runtime coordination
Document accepted cross-tab reauthentication, on-demand candles,
native query/logging ownership and the complete deployment path.

Preserve historical ADR context and link amendments through ADR 018.
Related backend change: WealthLedger/backend@d698ed9
2026-10-03 21:32:35 +03:00

14 KiB
Raw Permalink Blame History

Контейнерная поставка

Поставка для одного Docker host, одного backend и краткого простоя. Топология и ADR 015 объясняют границы. Production-комплект находится в отдельном репозитории deploy. Его README — единственная пошаговая инструкция установки, обновления и recovery; ADR 016 объясняет выбор релиза комплекта. Development-команды ниже выполняются из workspace с checkout frontend/ и backend/.

Предпосылки

  • Docker Engine с Compose plugin не ниже 2.24.4 (!override в development), доступ к registry и достаточно свободного диска для non-root UID 1000. Production images 0.2.3 опубликованы для Linux amd64. Другую архитектуру проверяют отдельно; multi-platform images не обещаны.
  • Nexus Docker hosted repository docker-hosted, доступный через nexus.personal.ksv741.ru/docker-hosted/. Docker Bearer Token Realm включён; reverse proxy передаёт исходные host и HTTPS scheme. Адрес token realm в WWW-Authenticate также должен начинаться с https://. Deployment policy запрещает redeploy существующих release tags. Runner label — ubuntu-latest, runner должен иметь Docker CLI, buildx и daemon. Наличие label само по себе не доказывает Docker capability.
  • В Actions каждого component repo нужны NEXUS_USERNAME и NEXUS_PASSWORD пользователя wealthledger-ci. Эта же учётная запись используется сервером для загрузки образов. Для публикации нужны browse/read/add/edit на docker-hosted; фактически выданный набор прав настраивается в Nexus. Built-in workflow token не заменяет эту настройку.
  • На сервере Caddy уже завершает TLS и передаёт весь домен на 127.0.0.1:8080 (или выбранный frontend port). Отдельного /api upstream нет. DNS, TLS, firewall и provisioning Caddy выполняются вне этого runbook.

Серверный login и доступ к images проверяются на целевом сервере. Локальный smoke не доказывает серверные registry permissions.

Development

Из workspace с исходниками обоих компонентов:

docker version
docker compose version
docker compose --env-file .env.development.example \
  -f compose.yaml -f compose.development.yaml config --quiet
docker compose --env-file .env.development.example \
  -f compose.yaml -f compose.development.yaml up -d --build --wait --wait-timeout 180

Откройте http://localhost:5173. Vite отдаёт HMR и проксирует /api/ без rewrite; Nest работает в watch mode. Монтируются только source/public, node_modules и generated files остаются в container filesystem. .env с хоста не копируется. После изменения manifest, lockfile, tsconfig или Vite config пересоберите соответствующий image. up --build обновляет Linux dependencies без stale named volume для node_modules.

Для другого порта скопируйте .env.development.example в игнорируемый .env.development и согласованно поменяйте FRONTEND_PORT/FRONTEND_ORIGIN. Пример содержит только вымышленные local secrets; server secrets сюда не входят. Поля image/data directory нужны для интерполяции base Compose, но полностью заменяются development override. В итоговом config нет production data path.

Project wealthledger-dev хранит SQLite в development-data. Обычные restart и down сохраняют его. Не применяйте down -v к данным, которые хотите сохранить. Не используйте production project name для development.

Публикация компонентных images

Штатный release process каждого repo создаёт защищённый vX.Y.Z после проверенного release commit. image.yml проверяет tag против package/lock version, затем выполняет npm ci, npm run verify; backend дополнительно выполняет npm run test:e2e. У каждого image OCI version/revision labels.

Images публикуются как nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:X.Y.Z и .../backend:X.Y.Z. latest не создаётся. Workflow отказывается перезаписывать существующие release tags; защита release tags и запрет параллельных publishers обязательны. Если одна публикация уже состоялась, не удаляйте tag для повторного запуска: проверьте опубликованный image и завершите проверку существующего artifact отдельно.

Выберите опубликованные version tags, выполните pull и сравните OCI revision с release commit. Выполните smoke с disposable data/config до первого server rollout. В release.env комплекта хранится пара полных image references, например nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:0.2.3. Сохраните versions/revisions в deployment record. Release tags не должны переназначаться другим images.

Перенос существующих images из Gitea

До переключения CI и server references проверьте HTTPS token realm и настройте пользователя wealthledger-ci. В Actions добавьте NEXUS_USERNAME/NEXUS_PASSWORD; прежние REGISTRY_USERNAME/REGISTRY_TOKEN сохраните для отката старого workflow. Пароли вводятся интерактивно или через --password-stdin, не через аргументы.

Скопируйте каждый опубликованный release без пересборки. Например, для 0.2.0 с установленным Skopeo и доступом чтения Gitea/записи Nexus:

skopeo login git.personal.ksv741.ru
skopeo login nexus.personal.ksv741.ru
for component in frontend backend; do
  skopeo copy --all --preserve-digests \
    "docker://git.personal.ksv741.ru/wealthledger/$component:0.2.0" \
    "docker://nexus.personal.ksv741.ru/docker-hosted/wealthledger/$component:0.2.0" || exit 1
done

--all переносит весь image index, включая платформы и вложенные attestation manifests; --preserve-digests прерывает перенос при изменении digest. До копирования проверьте наличие destination tag: если он уже существует, сравните исходный и целевой index digest и вложенные manifests. Совпадающий artifact повторно не публикуйте; несовпадение требует расследования, не удаления или перезаписи tag. Частичный перенос не считается завершённым.

После копирования сравните digests через skopeo inspect --raw и SHA-256, выполните docker pull обоих images пользователем wealthledger-ci на сервере приложения, проверьте OCI revision и container smoke на disposable data. Только после этого меняйте production image references и запускайте обычную последовательность обновления ниже. Старые Gitea packages и references сохраните: откат registry возвращает прежние адреса тех же artifacts, не восстанавливает SQLite и не меняет encryption keys. Не переносите Git release tags и не запускайте заново старый tag workflow для копирования существующего image.

Первая установка

На подготовленном сервере оператор клонирует deploy, настраивает внешний HTTPS и registry login, размещает server env, сертификат T-Bank и data directory вне checkout. Для новой пустой базы команда вызывается с --init. Для действующей установки сохраняются прежние данные и ключи, а первый переход выполняется как полное обновление с backup. Отсутствующая БД без --init — ошибка. Точные команды и разрешения каталогов приведены в README комплекта.

Git-тег deploy закрепляет пару images через release.env. Версия комплекта не обязана совпадать с версиями компонентов. Публикация image не выпускает комплект и не обновляет сервер автоматически. Команда получает выбранный тег, загружает images до простоя и использует весь snapshot этого commit.

Backup при остановленном backend

Сначала останавливаются писатели, затем копируется весь каталог данных, включая возможные WAL/SHM. Только успешная копия допускает миграции. Локальные backups находятся рядом с data directory, вне checkout, и автоматически не удаляются. Незавершённая копия помечается .partial- и не подходит для restore. Ключ шифрования хранится отдельно. Локальная копия не защищает от потери диска.

Единый цикл обновления

Каждое обновление, включая повтор того же тега и изменение только frontend, выполняет stop → backup → migration → readiness. Сравнение hashes контейнеров и конфигурации не выполняется. Приняты короткий простой API и дополнительный backup; во время простоя возможен 502, вкладкам может потребоваться reload. Ошибка migration блокирует старт, ошибка готовности останавливает новый backend; ошибка frontend не останавливает исправный backend. Успех фиксируется только после проверок запуска обоих компонентов.

Одна file lock защищает установку от параллельных команд. Маркер незавершённого обновления блокирует повтор до разбора оператором. Он содержит этап, версии и путь к backup, но не секреты и не копию данных. После обновления нужен smoke через HTTPS: healthcheck сам по себе не доказывает работу Secure refresh-cookie.

Откат image

Откат версии и восстановление базы — разные операции. Прежний backend можно запускать только после проверки совместимости с текущей схемой. Автоматического rollback нет; migration service при ручном image rollback не запускается. Порядок ручных Compose-команд и проверки recovery находится в README комплекта.

Explicit restore

Оператор выбирает проверенную копию и явно принимает потерю всех более новых записей. Сначала восстановление проверяется на isolated данных с прежним ключом и совместимым image. На целевой установке останавливаются писатели, текущий каталог сохраняется под новым именем, затем восстанавливается выбранная копия. Backend пересоздаётся, чтобы заново подключить заменённый bind-каталог: простой stop/start может сохранить mount прежних данных. Проверяются целостность БД, auth/cards и расшифровка подключения. Только после подтверждённого recovery можно снять маркер незавершённой операции.