Files
deploy/README.md
T

262 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WealthLedger deploy
Установка и обновление одного Linux-сервера с Docker Compose и SQLite:
```bash
./deploy.sh vX.Y.Z
```
`vX.Y.Z` — Git-тег **этого репозитория**. Его [release.env](release.env)
закрепляет полные адреса frontend и backend. Версии компонентов могут различаться.
Скрипт получает тег из `origin` и выполняет код и Compose выбранного commit;
изменения рабочего checkout не подменяют содержимое релиза.
Образы загружаются из `nexus.personal.ksv741.ru/docker-hosted/wealthledger/`.
Комплект `v0.1.2` закрепляет frontend `0.4.0` и backend `1.0.0`;
образы опубликованы для `linux/amd64`. Не создавайте тег комплекта до проверки
пары по разделу выпуска.
В `v0.1.2` компоненты обновляются вместе: инвестиционные API используют новый
формат `data/meta`. Backend требует миграцию `20260924170000_persist_tbank_data`;
штатный `deploy.sh` применяет её после остановки backend и успешного backup.
Существующие данные, `JWT_SECRET` и `TOKEN_ENCRYPTION_KEY` сохраняются.
## Подготовка сервера
Выполняется один раз. Автоматическая установка Docker, настройка DNS, HTTPS,
firewall и registry в скрипт не входят.
1. Установите Git, Bash, Docker Engine с Compose plugin (2.24.4 или новее),
`flock` из util-linux и стандартные `tar`, `cp`, `mktemp`. Используется
локальный Docker daemon через Unix socket, не удалённый context.
2. Настройте внешний HTTPS reverse proxy: весь домен направляется на
`127.0.0.1:8080`. Отдельный upstream `/api` не нужен. Порт можно поменять
через `FRONTEND_PORT`; `FRONTEND_ORIGIN` должен совпадать с HTTPS origin.
3. Выберите одного оператора с доступом к Docker, Git origin, ключам и данным.
Скрипту нужны права читать всю БД и создавать копии. Процесс backend работает
с UID/GID `1000:1000`; при другом UID оператора настройте доступ заранее.
Запускайте команды из одной и той же учётной записи, чтобы Git и Docker
использовали подготовленные SSH-ключи и registry credentials.
4. Войдите в Nexus, вводя пароль интерактивно, без записи в shell history:
```bash
docker login nexus.personal.ksv741.ru --username wealthledger-ci
```
5. Разместите checkout отдельно от постоянных ресурсов. Пример:
```text
/opt/wealthledger/
├── deploy/ # этот Git checkout
│ ├── compose.yaml
│ ├── deploy.sh
│ ├── release.env # версии frontend и backend
│ ├── .env.example
│ └── README.md
├── production.env # секреты и настройки, chmod 600
├── certs/tbank-ca.pem # CA для подключения к T-Bank, не HTTPS сайта
├── data/wealthledger.db
├── backups/ # согласованные копии, без автоочистки
└── .deploy/ # lock, current и при сбое pending
```
```bash
git clone ssh://git@git.personal.ksv741.ru:2222/WealthLedger/deploy.git /opt/wealthledger/deploy
cd /opt/wealthledger/deploy
install -m 600 .env.example ../production.env
```
Последнюю команду выполняйте только при отсутствии `production.env`:
существующий файл с ключами не перезаписывайте.
6. Заполните [настройки](.env.example) защищённым редактором. `DATA_DIRECTORY`
и `TBANK_CA_CERT_FILE` — существующие абсолютные пути, не внутри checkout.
`TBANK_CA_CERT_PATH` — путь внутри контейнера; сертификат монтируется read-only.
Каталог данных должен быть доступен для записи UID 1000, сертификат — для
чтения. Не отключайте проверку TLS вместо установки корректного CA.
7. Проверьте доступ к образам, свободное место под полную копию data и существующую
схему HTTPS. Сохраните ключ шифрования отдельно от локальных backups.
Не храните рабочие секреты в Git, не выполняйте `source production.env` и не
печатайте полный `docker compose config` с реальными ключами.
## Подключение существующей установки
Сохраните её Compose и текущие image references до перехода. Заполните
`production.env` **прежними** `JWT_SECRET` и `TOKEN_ENCRYPTION_KEY` и укажите
действующий data directory и сертификат. Не копируйте один `.db` из работающего
приложения и не создавайте новые ключи: старые подключения должны расшифровываться.
Проект Compose должен называться `wealthledger`, сервисы — `backend`, `frontend`
и `migration`, база — `wealthledger.db`. Оператор должен исключить другие
процессы записи в этот каталог; старый и новый способы запуска параллельно
не используются. Скрипт откажется менять data mount или действующие ключи.
```bash
cd /opt/wealthledger/deploy
./deploy.sh vX.Y.Z
```
Первый переход всегда выполняет полный цикл с backup, поскольку ещё нет
подтверждённого baseline комплекта. Данные не переносятся и не переименовываются.
`--init` для действующей установки не применяется.
## Новая установка
Только для нового пустого data directory подготовьте UID/GID `1000:1000`,
permissions `0700` и новые ключи: JWT secret не короче 32 символов и отдельный
32-byte encryption key в base64. Например, `openssl rand -hex 32` и
`openssl rand -base64 32` в защищённом терминале; значения перенесите в env.
Предварительно установите сертификат T-Bank.
```bash
./deploy.sh vX.Y.Z --init
```
Отсутствие файла БД без `--init` считается ошибкой, а не разрешением создать
пустую базу. `--init` отклоняется при наличии прежних контейнеров, успешной
установки или непустого data directory. Для первой пустой базы backup не нужен.
Если env расположен иначе, используйте `--env-file /absolute/path/production.env`.
Backups и `.deploy` всегда находятся рядом с фактическим data directory,
поэтому другой env-файл не создаёт отдельную блокировку для той же базы.
## Обновление
После публикации нового тега комплекта достаточно той же команды:
```bash
./deploy.sh vX.Y.Z
```
1. Проверка тега, настроек, данных и CA; загрузка образов и проверка прав
runtime user. Ошибка до остановки сохраняет работающие сервисы.
2. Сравнение образов и конфигурации с реальными контейнерами. При исправной
неизменной установке — no-op. Если изменился только frontend, backend
продолжает работать, backup и migration не запускаются.
3. Если изменился backend или его конфигурация: stop → копия **всего** data
directory с WAL/SHM → migration → backend readiness. Неизменный frontend
не пересоздаётся; если меняется и он, запускается после готовности backend.
4. Успех записывается в `.deploy/current` только после проверки запуска.
Краткий простой допустим. После обновления проверьте через HTTPS deep link,
login/refresh, существующие карты и чтение зашифрованного подключения.
Проверка сертификата/прав запускает кратковременный контейнер с Node validation,
не второй API. Контейнерная готовность не заменяет browser smoke.
## Ошибка или прерывание
Скрипт возвращает ненулевой код, этап ошибки, предыдущие images и расположение
готовой копии. Он не печатает raw application logs или секреты. После начала
изменений `.deploy/pending` сохраняет безопасные сведения для восстановления;
следующая команда остановится, пока оператор не разберёт инцидент.
- Ошибка backup: миграции не запускались, backend остановлен. Скрытые каталоги
`backups/.partial-*` — незавершённые копии, использовать их для restore нельзя.
- Ошибка migration: новый backend не запускается. Не применяйте `migrate reset`
или автоматический `resolve`; сначала выясните состояние схемы.
- Ошибка готовности backend: неисправный backend останавливается. Старый image
автоматически не запускается — он может быть несовместим с новой схемой.
- Ошибка frontend: исправный backend остаётся работающим, БД не восстанавливается.
- После жёсткого прерывания migration может ещё работать: перед любым копированием
или восстановлением проверьте контейнеры и остановите писателей.
Посмотрите `docker ps -a` и `.deploy/pending`. Для диагностики соответствующего
контейнера используйте локально `docker logs <container>`; перед передачей логов
убедитесь, что в них нет чувствительных данных.
## Ручной откат и восстановление
`deploy.sh` не является автоматическим rollback: выбор старого тега сам по себе
не доказывает совместимость со схемой. Сначала проверьте план на отдельной копии
данных с прежним ключом, другим Compose project и отдельным портом.
Подготовьте снимок выбранного **совместимого** тега для ручных Compose-команд:
```bash
RECOVERY_BUNDLE=$(mktemp -d)
git archive vCOMPATIBLE.VERSION.HERE | tar -x -C "$RECOVERY_BUNDLE"
unset FRONTEND_IMAGE BACKEND_IMAGE COMPOSE_FILE COMPOSE_PROJECT_NAME COMPOSE_PROFILES
COMPOSE=(docker compose --project-name wealthledger \
--env-file /opt/wealthledger/production.env \
--env-file "$RECOVERY_BUNDLE/release.env" -f "$RECOVERY_BUNDLE/compose.yaml")
```
Замените демонстрационный тег настоящим. Используйте Bash и реальные абсолютные
пути. Для image rollback без изменения данных:
```bash
"${COMPOSE[@]}" stop backend migration
"${COMPOSE[@]}" pull backend
"${COMPOSE[@]}" up -d --no-deps --wait --wait-timeout 120 backend
```
Для frontend аналогично выполняются `pull frontend` и `up ... frontend`.
Migration при image rollback намеренно не запускается.
**Restore — отдельное решение с потерей всех записей после выбранного backup.**
После проверки целостности выбранной копии и наличия её ключа, при остановленных
писателях сохраните текущие данные под новым именем, не удаляя их:
```bash
"${COMPOSE[@]}" stop backend migration
mv /opt/wealthledger/data /opt/wealthledger/data-before-restore-UNIQUE_TIMESTAMP
cp -a /opt/wealthledger/backups/CHOSEN_BACKUP/data /opt/wealthledger/data
"${COMPOSE[@]}" up -d --no-deps --force-recreate --wait --wait-timeout 120 backend
```
Подставьте уникальное ещё не существующее имя и конкретный backup. Убедитесь,
что backend именно пересоздан: после замены bind-каталога простой stop/start
может сохранить mount прежнего каталога и вернуть более новые данные.
Проверьте owner `1000:1000`, `PRAGMA integrity_check`, доступность данных, auth, карты
и расшифровку подключения. Убедитесь, что восстановленный image совместим
со схемой; старые migration-файлы не являются down-migrations.
Только после проверки успешного recovery переместите `.deploy/pending` в файл
инцидента, например `.deploy/recovered-UNIQUE_TIMESTAMP`, и повторите smoke.
Если новый релиз ещё не установлен, текущая запись может оставаться прежней:
следующее обновление сверяется с реальными контейнерами. Файл `lock` не удаляйте:
блокировка освобождается процессом; удаление файла при живом процессе опасно.
## Резервные копии
Скрипт создаёт уникальный каталог и не перезаписывает прежние копии. Готовой
копия становится только после полного успешного `cp -a`; частичные копии
отмечены префиксом `.partial-`. Проверяйте восстановление на отдельной копии.
Очистка только ручная. Контролируйте свободное место; не удаляйте backup,
нужный незавершённому обновлению. Копия на том же диске не защищает от его потери.
Независимое хранение ключа обязательно; внешнее backup-хранилище сюда не входит.
## Выпуск комплекта и проверки
1. Дождитесь публикации компонентов в Nexus. В `release.env` закрепите ровно
`FRONTEND_IMAGE` и `BACKEND_IMAGE` с version tags. Не используйте `latest`.
2. Выполните локальный gate (Python 3 нужен только для tests):
```bash
bash -n deploy.sh tests/deploy.sh
bash tests/deploy.sh
git diff --check
```
Tests используют настоящий Git, backup-файлы и file locks, а Docker заменяют
управляемым double. Проверяются update/no-op, смена настроек, сохранение ключей,
ошибки pull/backup/migration/readiness, конкуренция и незавершённая операция.
Отдельная проверка реального Docker для restore: `bash tests/deploy.sh --docker-restore`.
Она создаёт временный project, заменяет bind-каталог и проверяет пересоздание
контейнера. На arm64 для проверки текущих amd64 images можно использовать
`DOCKER_DEFAULT_PLATFORM=linux/amd64` при наличии Docker emulation.
3. На disposable Linux-установке с настоящими images проверьте `--init`, adoption,
frontend-only/backend-only update, повтор, backup/restore и HTTPS smoke.
Сверьте OCI revision images с release commits компонентов. Для проверки
кандидата используйте временный Git origin/tag, не двигайте опубликованный тег.
4. После review и проверок примите commit в `main`, создайте аннотированный
`vX.Y.Z` именно на проверенном commit и отправьте этот тег. Версия комплекта
самостоятельная: одинаковые номера frontend/backend не требуются.
Выпуск компонента не выпускает комплект и не обновляет сервер автоматически.
Опубликованные Git и Nexus tags не переназначаются. Для исправления нужен
новый релиз комплекта. В репозитории не нужны npm, собственный release framework
или pipeline, автоматически выбирающий последние образы.