docs: document container deployment and recovery #15
@@ -47,6 +47,7 @@ WealthLedger помогает частному инвестору понимат
|
|||||||
| Понять ошибку или старые данные | [Состояния интерфейса](architecture/cross-cutting/ui-resilience.md), [свежесть данных](architecture/cross-cutting/data-freshness.md) |
|
| Понять ошибку или старые данные | [Состояния интерфейса](architecture/cross-cutting/ui-resilience.md), [свежесть данных](architecture/cross-cutting/data-freshness.md) |
|
||||||
| Понять визуальные роли и состояния | [Визуальные принципы](design/overview.md) |
|
| Понять визуальные роли и состояния | [Визуальные принципы](design/overview.md) |
|
||||||
| Узнать причину архитектурного решения | [Журнал ADR](adr/README.md) |
|
| Узнать причину архитектурного решения | [Журнал ADR](adr/README.md) |
|
||||||
|
| Запустить, обновить или восстановить приложение | [Контейнерная поставка](architecture/deployment.md) |
|
||||||
|
|
||||||
## Границы знаний
|
## Границы знаний
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
**Status:** Accepted, baseline decision
|
**Status:** Accepted, baseline decision
|
||||||
|
|
||||||
|
Deployment boundary расширена [ADR 015](015-single-server-sqlite-deployment.md)
|
||||||
|
для одного сервера и одного backend process. Ниже сохранён исходный контекст.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
MVP нужен воспроизводимый локальный доступ к данным без инфраструктуры развёртывания. Доменные таблицы и миграции на baseline отсутствуют.
|
MVP нужен воспроизводимый локальный доступ к данным без инфраструктуры развёртывания. Доменные таблицы и миграции на baseline отсутствуют.
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 015. SQLite в ограниченном single-server deployment
|
||||||
|
|
||||||
|
**Status:** Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 001](001-prisma-sqlite-local-mvp.md) ограничивал SQLite локальным MVP.
|
||||||
|
Контейнерная поставка требует постоянного хранилища на одном сервере,
|
||||||
|
но не требует высокой доступности или нескольких экземпляров API.
|
||||||
|
Пользователи, сессии, зашифрованные подключения и банковская история уже
|
||||||
|
хранятся в SQLite. Кэши, limiter и generation подключений локальны процессу.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Разрешён один backend process на одном Docker host с SQLite в постоянном
|
||||||
|
host directory. Development использует отдельный named volume. SQLite
|
||||||
|
остаётся embedded library без сетевого порта. Это расширяет deployment
|
||||||
|
boundary ADR 001; его исторический контекст сохраняется.
|
||||||
|
|
||||||
|
Prisma migrations выполняются отдельным одноразовым service из того же image,
|
||||||
|
что и backend. Перед изменением схемы backend останавливается, всё data
|
||||||
|
directory копируется вместе с возможными WAL/SHM files. Ключ шифрования
|
||||||
|
сохраняется отдельно. Неуспешная migration блокирует запуск новой версии.
|
||||||
|
|
||||||
|
Откат image требует совместимости с текущей схемой и не восстанавливает
|
||||||
|
старую базу. Restore требует отдельного решения о потере данных после backup.
|
||||||
|
Frontend и backend имеют независимые версии; production фиксирует version tags.
|
||||||
|
Краткий простой допустим.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
PostgreSQL потребовал бы миграции данных и отдельного database lifecycle;
|
||||||
|
текущие требования этого не оправдывают. Kubernetes, replicas и blue-green
|
||||||
|
не устраняют ограничения process-local состояния и не входят в решение.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Сервер не обещает HA или zero-downtime. Один backend instance — обязательное
|
||||||
|
ограничение, а не рекомендуемая настройка масштабирования. Нужны контроль
|
||||||
|
свободного места, согласованные backups и проверяемое восстановление.
|
||||||
|
|
||||||
|
Решение пересматривается при необходимости нескольких replicas, другом
|
||||||
|
availability target, заметном write contention или потребности в отдельном
|
||||||
|
database service. Добавление реплик до такого решения недопустимо.
|
||||||
|
|
||||||
|
Топология описана в [архитектуре](../architecture/containers.md#контейнерная-поставка),
|
||||||
|
операции — в [runbook](../architecture/deployment.md).
|
||||||
@@ -19,3 +19,4 @@
|
|||||||
- [009 — Concurrent refresh rotation policy](009-concurrent-refresh-rotation-policy.md)
|
- [009 — Concurrent refresh rotation policy](009-concurrent-refresh-rotation-policy.md)
|
||||||
- [013 — НКР как первый источник кредитных рейтингов облигаций](013-nkr-bond-credit-ratings.md)
|
- [013 — НКР как первый источник кредитных рейтингов облигаций](013-nkr-bond-credit-ratings.md)
|
||||||
- [014 — Актуальная OpenAPI-схема как вход frontend](014-trusted-openapi-input.md)
|
- [014 — Актуальная OpenAPI-схема как вход frontend](014-trusted-openapi-input.md)
|
||||||
|
- [015 — SQLite в ограниченном single-server deployment](015-single-server-sqlite-deployment.md)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
| Как обрабатываются ошибки и ожидание? | [HTTP API и ошибки](http-api.md), [состояния интерфейса](cross-cutting/ui-resilience.md) |
|
| Как обрабатываются ошибки и ожидание? | [HTTP API и ошибки](http-api.md), [состояния интерфейса](cross-cutting/ui-resilience.md) |
|
||||||
| Как связать ошибку с обработкой запроса? | [Наблюдаемость](http-api.md#наблюдаемость) |
|
| Как связать ошибку с обработкой запроса? | [Наблюдаемость](http-api.md#наблюдаемость) |
|
||||||
| Как устроено подтверждённое локальное окружение? | [Container](containers.md#представление-container), [качество и ограничения](containers.md#качество-и-ограничения) |
|
| Как устроено подтверждённое локальное окружение? | [Container](containers.md#представление-container), [качество и ограничения](containers.md#качество-и-ограничения) |
|
||||||
|
| Как запустить, обновить или восстановить приложение? | [Развёртывание](deployment.md) |
|
||||||
| Что означают предметные термины? | [Глоссарий](../glossary.md) |
|
| Что означают предметные термины? | [Глоссарий](../glossary.md) |
|
||||||
|
|
||||||
Эта карта покрывает вопросы архитектуры там, где о них есть подтверждённое
|
Эта карта покрывает вопросы архитектуры там, где о них есть подтверждённое
|
||||||
|
|||||||
@@ -27,11 +27,50 @@ flowchart LR
|
|||||||
F -->|"HTTPS; изображения без авторизации"| C
|
F -->|"HTTPS; изображения без авторизации"| C
|
||||||
```
|
```
|
||||||
|
|
||||||
SPA выполняется в браузере пользователя. Backend — отдельное локальное
|
SPA выполняется в браузере пользователя. Backend — отдельное
|
||||||
приложение; SQLite используется внутри его процесса через библиотеку и файл
|
приложение; SQLite используется внутри его процесса через библиотеку и файл
|
||||||
базы. Между API и SQLite нет сетевого database server. Граница системы на
|
базы. Между API и SQLite нет сетевого database server. Граница системы на
|
||||||
схеме показывает принадлежность частей, а не их размещение на одном узле.
|
схеме показывает принадлежность частей, а не их размещение на одном узле.
|
||||||
|
|
||||||
|
## Контейнерная поставка
|
||||||
|
|
||||||
|
Deployment view для одного сервера. Caddy принадлежит серверу; Compose
|
||||||
|
содержит два постоянных application containers и одноразовую migration.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Browser["Браузер · React SPA"] -->|HTTPS| Caddy["Серверный Caddy · TLS"]
|
||||||
|
subgraph Host["Один Docker host"]
|
||||||
|
Caddy -->|"HTTP · 127.0.0.1:8080"| Frontend["frontend · nginx · SPA + /api/"]
|
||||||
|
Frontend -->|"HTTP /api/v1 · internal network"| Backend["backend · NestJS · один process"]
|
||||||
|
Migration["migration · Prisma · one-shot"] -->|"До старта backend"| Data[("Host directory · SQLite + WAL")]
|
||||||
|
Backend -->|"Чтение и запись /data"| Data
|
||||||
|
Secrets["Restricted environment · ключи вне images"] --> Backend
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Только frontend публикует host port, привязанный к loopback. Backend не
|
||||||
|
публикует порт, но имеет исходящий доступ к внешним API. nginx сохраняет
|
||||||
|
`/api/` prefix, forwarding headers и request ID; Docker DNS разрешается
|
||||||
|
повторно после backend recreation. HTML требует revalidation, hashed assets
|
||||||
|
имеют immutable cache. Caddy не содержит отдельного маршрута к API.
|
||||||
|
|
||||||
|
Development заменяет nginx на Vite с HMR, backend — на Nest watch, а host data
|
||||||
|
path — на отдельный named volume. Browser origin остаётся единым. Linux
|
||||||
|
node_modules не монтируются с хоста. Production SPA собирается с relative API
|
||||||
|
base; production cookies остаются Secure, публичный origin использует HTTPS.
|
||||||
|
|
||||||
|
Компоненты имеют независимые images и версии. Production фиксирует image
|
||||||
|
version tags; migration использует тот же backend image. Успех migration предшествует
|
||||||
|
backend healthcheck на существующем auth route; frontend ждёт healthy backend.
|
||||||
|
Runtime работает non-root, SQLite сохраняется вне image, ключ шифрования и JWT
|
||||||
|
secret поступают из restricted environment и сохраняются между обновлениями.
|
||||||
|
|
||||||
|
Ограничение SQLite расширено в [ADR 015](../adr/015-single-server-sqlite-deployment.md).
|
||||||
|
[Runbook](deployment.md) описывает установку, independent
|
||||||
|
updates, backup, rollback и explicit restore. Он также перечисляет внешние
|
||||||
|
prerequisites: локальный smoke не подтверждает registry/server rollout.
|
||||||
|
|
||||||
## Ответственность частей
|
## Ответственность частей
|
||||||
|
|
||||||
| Часть | Ответственность |
|
| Часть | Ответственность |
|
||||||
@@ -106,10 +145,10 @@ credentials не являются сохраняемым представлен
|
|||||||
|
|
||||||
## Сохранение и восстановление данных
|
## Сохранение и восстановление данных
|
||||||
|
|
||||||
Резервная копия SQLite должна быть согласованной: для работающей базы
|
Поддерживаемая процедура останавливает backend и копирует весь data directory
|
||||||
используется SQLite backup API с учётом WAL. Несогласованное копирование
|
с возможными WAL/SHM files до migration. Копирование отдельных файлов
|
||||||
файлов базы и WAL не сохраняет целостный снимок. Восстановление проверяется
|
работающей базы не сохраняет целостный снимок; для live backup потребовался бы
|
||||||
на отдельной копии. Ключ шифрования подключения хранится отдельно от базы;
|
SQLite backup API. Восстановление проверяется на отдельной копии. Ключ шифрования подключения хранится отдельно от базы;
|
||||||
его потеря делает сохранённый T-Bank token нечитаемым.
|
его потеря делает сохранённый T-Bank token нечитаемым.
|
||||||
|
|
||||||
Откат приложения требует совместимости предыдущей версии с сохранённой
|
Откат приложения требует совместимости предыдущей версии с сохранённой
|
||||||
@@ -150,12 +189,12 @@ access token и вращаемый refresh token. [Политика query-кэш
|
|||||||
отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
|
отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
|
||||||
и [HTTP API](http-api.md#error-contract).
|
и [HTTP API](http-api.md#error-contract).
|
||||||
|
|
||||||
Подтверждённая топология — локальная SPA и локальный API с файлом SQLite.
|
Поддерживается локальный Compose и ограниченная single-server topology с SQLite.
|
||||||
Origin браузера, адрес API и cookie policy должны быть согласованы.
|
Origin браузера, адрес API и cookie policy должны быть согласованы.
|
||||||
`localhost` и `127.0.0.1` задают разные browser-границы для cookie и CORS.
|
`localhost` и `127.0.0.1` задают разные browser-границы для cookie и CORS.
|
||||||
Локальный HTTP поддерживается в development-режиме backend; в остальных
|
Локальный HTTP поддерживается в development-режиме backend; в остальных
|
||||||
режимах refresh-cookie имеет Secure flag и требует защищённого соединения.
|
режимах refresh-cookie имеет Secure flag и требует защищённого соединения.
|
||||||
Наличие локального запуска не задаёт production-топологию или гарантии SLA.
|
Single-server deployment не задаёт гарантий HA или zero-downtime.
|
||||||
|
|
||||||
Кэши и лимитер T-Bank действуют на экземпляр backend. Ротация ключа шифрования
|
Кэши и лимитер T-Bank действуют на экземпляр backend. Ротация ключа шифрования
|
||||||
подключений и согласование изменений подключения между распределёнными
|
подключений и согласование изменений подключения между распределёнными
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
# Контейнерная поставка
|
||||||
|
|
||||||
|
Runbook для одного Docker host, одного backend и краткого простоя.
|
||||||
|
[Топология](containers.md#контейнерная-поставка) и
|
||||||
|
[ADR 015](../adr/015-single-server-sqlite-deployment.md) объясняют границы.
|
||||||
|
Команды выполняются из checkout `WealthLedger/main`, содержащего `compose.yaml`.
|
||||||
|
Development дополнительно требует checkout `frontend/` и `backend/`.
|
||||||
|
|
||||||
|
## Предпосылки
|
||||||
|
|
||||||
|
- Docker Engine с Compose plugin **не ниже 2.24.4** (`!override` в development),
|
||||||
|
доступ к registry и достаточно свободного диска для non-root UID 1000.
|
||||||
|
Локально проверены Engine 29.8.0, Compose 5.5.1, Linux arm64.
|
||||||
|
Другую архитектуру проверяют отдельно; multi-platform images не обещаны.
|
||||||
|
- Gitea Container Registry; проверяемая версия сервера — 1.27.3.
|
||||||
|
Runner label — `ubuntu-latest`, runner должен иметь Docker CLI, buildx и daemon.
|
||||||
|
Наличие label само по себе не доказывает Docker capability.
|
||||||
|
- В Actions каждого component repo нужны `REGISTRY_USERNAME` и
|
||||||
|
`REGISTRY_TOKEN` отдельной identity с package write permission. Server
|
||||||
|
использует другую identity только с package read permission. Built-in
|
||||||
|
workflow token не заменяет эту настройку.
|
||||||
|
- На сервере Caddy уже завершает TLS и передаёт весь домен на
|
||||||
|
`127.0.0.1:8080` (или выбранный frontend port). Отдельного `/api` upstream нет.
|
||||||
|
DNS, TLS, firewall и provisioning Caddy выполняются вне этого runbook.
|
||||||
|
|
||||||
|
Реальная публикация и server pull-only login требуют отдельной проверки
|
||||||
|
на вашей инфраструктуре. Локальные images не доказывают registry permissions.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Из workspace с исходниками обоих компонентов:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 публикуются как `git.personal.ksv741.ru/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. В production env хранится пара полных image references, например
|
||||||
|
`git.personal.ksv741.ru/wealthledger/frontend:0.1.0`. Сохраните versions/revisions
|
||||||
|
в deployment record. Release tags не должны переназначаться другим images.
|
||||||
|
|
||||||
|
## Первая установка
|
||||||
|
|
||||||
|
Создайте restricted environment file и data directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -d -m 0700 /srv/wealthledger
|
||||||
|
sudo install -d -m 0700 -o 1000 -g 1000 /srv/wealthledger/data
|
||||||
|
sudo install -m 0600 .env.compose.example /srv/wealthledger/production.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Дальнейшие команды выполняет оператор, имеющий доступ к environment file,
|
||||||
|
backup directory и Docker. Заполните env локальным защищённым редактором:
|
||||||
|
полные ссылки `FRONTEND_IMAGE` и `BACKEND_IMAGE` с опубликованными version tags,
|
||||||
|
публичный HTTPS origin и абсолютный data path. Версии в примере — образец
|
||||||
|
формата; перед запуском выберите реально опубликованные releases.
|
||||||
|
Пустые required значения блокируют config; некорректные secrets блокируют backend.
|
||||||
|
Не выводите полный rendered config с настоящими secrets в журналы.
|
||||||
|
|
||||||
|
Для новой базы создайте JWT secret длиной хотя бы 32 символа и отдельный
|
||||||
|
32-byte encryption key в base64 (`openssl rand -hex 32` и
|
||||||
|
`openssl rand -base64 32` в защищённом терминале). Сохраните ключ отдельно
|
||||||
|
от data backup. Для существующей установки перенесите прежние JWT secret
|
||||||
|
и encryption key, а согласованную копию **всего** data directory — при
|
||||||
|
остановленном прежнем backend. Файл должен называться `wealthledger.db`;
|
||||||
|
если переименовываете, согласуйте также WAL/SHM companion names до старта.
|
||||||
|
Не запускайте старый и новый backend на одной базе одновременно.
|
||||||
|
|
||||||
|
Войдите в registry server identity без помещения token в командную строку:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker login git.personal.ksv741.ru --username SERVER_PULL_USER
|
||||||
|
export COMPOSE_FILE=compose.yaml
|
||||||
|
export COMPOSE_PROJECT_NAME=wealthledger
|
||||||
|
export COMPOSE_ENV_FILES=/srv/wealthledger/production.env
|
||||||
|
docker compose config --quiet
|
||||||
|
docker compose pull frontend backend migration
|
||||||
|
docker compose up -d --wait --wait-timeout 120
|
||||||
|
docker compose ps -a
|
||||||
|
```
|
||||||
|
|
||||||
|
Migration и backend используют один image и один data mount.
|
||||||
|
Migration должна завершиться с кодом 0; иначе backend не запускается.
|
||||||
|
Readiness принимает 401 от существующего `/api/v1/auth/me` без credentials.
|
||||||
|
Frontend становится healthy после backend. Backend host port отсутствует.
|
||||||
|
Runtime non-root; root filesystem read-only, nginx temp files находятся в tmpfs.
|
||||||
|
|
||||||
|
Проверьте SPA deep link, регистрацию/вход, refresh, существующие карты,
|
||||||
|
чтение старого зашифрованного подключения и один финансовый запрос.
|
||||||
|
Production refresh-cookie имеет Secure flag: проверяйте через HTTPS Caddy;
|
||||||
|
loopback HTTP curl без browser cookie jar не доказывает browser session.
|
||||||
|
При локальном smoke используйте disposable credentials и явную передачу cookie.
|
||||||
|
Направляйте Caddy на новый deployment только после успешной проверки.
|
||||||
|
Прежний runtime удаляйте только после проверки новой установки.
|
||||||
|
|
||||||
|
## Backup при остановленном backend
|
||||||
|
|
||||||
|
До backend update проверьте совместимость новой версии с текущим frontend
|
||||||
|
и схемой, а обратной версии — с новой схемой. При неизвестной совместимости
|
||||||
|
не обещайте image rollback. В выбранном rollout отсутствуют параллельные API.
|
||||||
|
|
||||||
|
Ниже data path соответствует примеру env; при другом path замените его.
|
||||||
|
Сохраните текущую пару image references/revisions и отдельно — ключи.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose stop backend
|
||||||
|
docker compose ps -a backend
|
||||||
|
install -d -m 0700 /srv/wealthledger/backups
|
||||||
|
BACKUP_DIRECTORY="/srv/wealthledger/backups/$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
|
mkdir -m 0700 "$BACKUP_DIRECTORY"
|
||||||
|
cp -a /srv/wealthledger/data "$BACKUP_DIRECTORY/data"
|
||||||
|
```
|
||||||
|
|
||||||
|
Перед копированием убедитесь, что backend остановлен, migration не выполняется
|
||||||
|
и никто другой не пишет в каталог. Успех `cp` обязателен перед продолжением.
|
||||||
|
Не копируйте отдельный `.db` из работающего приложения. Проверяйте backup
|
||||||
|
на отдельной копии: migration status, `PRAGMA integrity_check`, auth, карты
|
||||||
|
и расшифровка тестового подключения исходным ключом. Backup без проверяемого
|
||||||
|
ключа недостаточен для восстановления подключения.
|
||||||
|
|
||||||
|
## Независимое обновление frontend
|
||||||
|
|
||||||
|
После проверки API compatibility замените только `FRONTEND_IMAGE` в env:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull frontend
|
||||||
|
docker compose up -d --no-deps --wait --wait-timeout 120 frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Backend container и SQLite не пересоздаются. Входная точка кратко недоступна.
|
||||||
|
Проверьте deep link, login/refresh и новую OCI revision. Старым browser tabs
|
||||||
|
может понадобиться reload: старые hashed chunks не сохраняются между images.
|
||||||
|
|
||||||
|
## Независимое обновление backend
|
||||||
|
|
||||||
|
Выполните stopped-backend backup выше. Замените только `BACKEND_IMAGE` в env.
|
||||||
|
Последовательность не продолжается после failed migration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull backend migration
|
||||||
|
docker compose up --no-deps --force-recreate --abort-on-container-exit \
|
||||||
|
--exit-code-from migration migration &&
|
||||||
|
docker compose up -d --no-deps --wait --wait-timeout 120 backend
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend не пересоздаётся; nginx повторно разрешает backend DNS. Во время
|
||||||
|
перерыва возможен 502. При failure оставьте backend остановленным, сохраните
|
||||||
|
диагностику migration и выберите совместимый image либо отдельный restore.
|
||||||
|
Не используйте `migrate reset` или автоматический `resolve` как recovery.
|
||||||
|
Проверьте migration status, health, login/refresh, карты и расшифровку подключения.
|
||||||
|
После успешного smoke запишите фактически установленную пару image references.
|
||||||
|
|
||||||
|
## Откат image
|
||||||
|
|
||||||
|
Для frontend верните предыдущий совместимый version tag и повторите frontend update.
|
||||||
|
Для backend сначала остановите процесс и подтвердите совместимость старого
|
||||||
|
image с **текущей** схемой. Верните предыдущий image reference и запустите только backend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull backend
|
||||||
|
docker compose up -d --no-deps --wait --wait-timeout 120 backend
|
||||||
|
```
|
||||||
|
|
||||||
|
Данные и encryption key не заменяются. Migration service здесь намеренно
|
||||||
|
не запускается: старые migrations не являются down-migrations. Прочитайте
|
||||||
|
новые записи, сделанные после backup, и проверьте auth/cards/connection.
|
||||||
|
При несовместимости оставьте приложение остановленным до решения о recovery.
|
||||||
|
|
||||||
|
## Explicit restore
|
||||||
|
|
||||||
|
Это отдельное решение оператора, **не часть image rollback**. Оператор выбирает
|
||||||
|
backup timestamp и явно принимает потерю всех изменений после него, включая
|
||||||
|
сессии, карты и подключения. Сначала воспроизведите restore на isolated project
|
||||||
|
с отдельным data path/port и совместимым image; не используйте production data
|
||||||
|
как fixture. Убедитесь в наличии соответствующего encryption key.
|
||||||
|
|
||||||
|
После отдельного решения остановите backend и убедитесь, что migration не
|
||||||
|
работает. Сохраните нынешний каталог под новым именем, ничего не удаляя:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose stop backend
|
||||||
|
mv /srv/wealthledger/data /srv/wealthledger/data-before-restore-TIMESTAMP
|
||||||
|
cp -a /srv/wealthledger/backups/CHOSEN_TIMESTAMP/data /srv/wealthledger/data
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверьте owner 1000:1000, permissions, выбранный compatible image и исходный
|
||||||
|
ключ. Запустите backend командой rollback с `--no-deps`, затем выполните smoke.
|
||||||
|
Сверьте наличие записей до backup и отсутствие известных записей потерянного
|
||||||
|
интервала. Сохраните прежний каталог для расследования. При необходимости
|
||||||
|
новой migration сначала выполните обычную backup/update последовательность.
|
||||||
Reference in New Issue
Block a user