261 lines
18 KiB
Markdown
261 lines
18 KiB
Markdown
# 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.6` закрепляет frontend `1.2.1` и backend `2.3.0`.
|
||
Доступность образов в Nexus и работа пары на сервере не проверялись перед
|
||
публикацией комплекта.
|
||
|
||
Совместимость OpenAPI frontend `1.2.1` и backend `2.3.0`, а также наличие новых
|
||
миграций не проверялись. Штатный `deploy.sh` выполняет остановку backend,
|
||
backup и `prisma migrate deploy`; существующие данные, `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. Stop backend → копия **всего** data directory с WAL/SHM → migration →
|
||
backend readiness. Только для `--init` пустой базы backup не требуется.
|
||
3. Запуск frontend после готовности backend. Каждый вызов выполняет этот путь,
|
||
включая повтор того же тега и frontend-only update. No-op/change detection
|
||
отсутствуют; приняты короткий простой API и дополнительный backup.
|
||
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. Проверяются полные повторные updates, настройки, ключи,
|
||
ошибки 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, автоматически выбирающий последние образы.
|