- prod-backup.sh (tier hourly 48ч / daily 30д + uploads), prod-backup-offsite-pull.sh (DR на dev-vm) - prod-restore-verify.sh: restore во временную БД + сверка числа строк, дроп - prod-monitor.sh: api/web/public/БД/диск/5xx → Telegram владельцу (+ --test) - deploy-prod.sh: guard'ы (prod-postgres, .env chmod600, не dev-пароль, quick-login off, SEED_DEMO_DATA=false, issuer), бэкап ДО миграций, только --yes, НЕ в ночных - systemd таймеры (backup-hourly/daily, monitor, offsite-pull); docs/prod-runbook.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
6 KiB
Markdown
65 lines
6 KiB
Markdown
# Рунбук ПРОД admin.food-market.kz (ALL MARKET)
|
||
|
||
Боевой сервер магазина. Прод-vm **192.168.1.190**, compose **`~/food-market/deploy`**, своя БД
|
||
**`food-market-prod-postgres:5437`** (НЕ shared-postgres!). gateway nginx (на хосте, только читать)
|
||
проксирует admin.food-market.kz → :8081 (web). Публичный сайт food-market.kz — НЕ трогаем.
|
||
|
||
## Чек-лист готовности (выполнено 09.10.2026)
|
||
- [x] **Образы — релизные теги** (SHA main, не `test`/`latest`): `API_TAG`/`WEB_TAG` в `.env`.
|
||
- [x] **Секреты прода отдельны от стенда**, в `~/food-market/deploy/.env` **chmod 600**:
|
||
- `POSTGRES_PASSWORD` — сильный (НЕ dev-дефолт). БД своя (5437).
|
||
- `OPENIDDICT_ISSUER=https://admin.food-market.kz/`. OpenIddict PFX-ключи генерируются на
|
||
сервере в томе `food-market-api-data` (очищен перед стартом → свежие прод-ключи, отдельные
|
||
от стенда/dev). `OPENIDDICT_CERT_PASSWORD` пуст (ключи защищены правами ФС).
|
||
- `SEED_SUPERADMIN_PASSWORD` — временный, см. ниже.
|
||
- VAPID (web-push) — автогенерация в БД (PlatformSettings), секрет не нужен.
|
||
- SMTP — НЕ настроен (задаёт владелец; до настройки сброс пароля по email неактивен).
|
||
- [x] **Выключено на проде**: quick-login (`QUICK_LOGIN_ENABLED=false` + code safety-override по
|
||
issuer), стендовые подсказки, демо-сидеры и тест-орги (`SEED_DEMO_DATA=false` → нет «Demo Market»).
|
||
- [x] **SuperAdmin** `admin@food-market.local` — пароль из `SEED_SUPERADMIN_PASSWORD`; **копия
|
||
пароля: `~/.fm-watchdog/prod-superadmin-initial.txt` (chmod 600) на dev-vm**. ⚠ Владелец ОБЯЗАН
|
||
сменить пароль при первом входе (UI → профиль). Рекомендуется также завести персональный аккаунт.
|
||
- [x] Проверено: вход SuperAdmin через https://admin.food-market.kz = HTTP 200; БД пуста
|
||
(orgs=0, products=0, users=1=SuperAdmin, roles=4 системных); web SPA = 200; quick-login = 404.
|
||
|
||
## Деплой прода (ТОЛЬКО вручную)
|
||
```
|
||
~/deploy-prod.sh --yes # взять теги из прод .env, backup → up
|
||
~/deploy-prod.sh <api> <web> --yes --build # пересобрать+push+деплой с новыми тегами
|
||
```
|
||
- Скрипт с guard'ами: проверяет что compose = food-market-prod-postgres (не shared-postgres),
|
||
`.env` chmod 600, НЕ dev-пароль БД, quick-login off, SEED_DEMO_DATA=false, issuer прод.
|
||
- Делает **бэкап БД ДО** pull/миграций. Миграции применяются автоматически при старте api
|
||
(`AppDbContext.Database.Migrate()` → `/health/ready` = Healthy только после миграций).
|
||
- Поднимает `postgres api web` (public НЕ трогает). НЕ в ночных задачах.
|
||
|
||
## Бэкапы и восстановление
|
||
- **Ежечасно** (хранение 48 ч): `food-market-backup-hourly.timer` → `/opt/food-market-data/backups/hourly`.
|
||
- **Ежедневно 03:30** (хранение 30 дн + uploads): `food-market-backup-daily.timer` →
|
||
`…/backups/daily`; `ExecStartPost` — автопроверка восстановления.
|
||
- **Офсайт (другая VM)**: dev-vm таймер `food-market-offsite-pull.timer` (04:00) тянет последний
|
||
daily-дамп на dev-vm `~/fm-prod-backups` (хранение 30 дн).
|
||
- Формат — `pg_dump -Fc` (pg_restore с параллелизмом/выборочно).
|
||
- **Проверка восстановления** (вручную): `~/food-market/deploy/prod-restore-verify.sh` — restore
|
||
последнего дампа во временную БД `food_market_restore_test` + сверка числа строк с боевой +
|
||
дроп временной. Боевую БД не трогает. Проверено 09.10: все ключевые таблицы совпали.
|
||
|
||
## Мониторинг
|
||
- `food-market-prod-monitor.timer` (каждые 5 мин): api `/health/ready`, web, публичный вход, БД
|
||
(`pg_isready`), место на диске (порог 85%), 5xx в логах api за 5 мин. При проблеме — Telegram
|
||
владельцу (бот: `~/.fm-watchdog/telegram-{token,chat}` на prod-vm). Лог: `~/.fm-watchdog/prod-monitor.log`.
|
||
- Тест уведомления: `~/food-market/deploy/prod-monitor.sh --test` (отправлено 09.10 ✓).
|
||
|
||
## Откат (rollback)
|
||
1. Предыдущий образ: в registry по прошлому SHA. `~/deploy-prod.sh <prev-api> <prev-web> --yes`
|
||
(без `--build`) — вернёт прежние теги (бэкап снимется автоматически до переключения).
|
||
2. Данные: восстановить из дампа —
|
||
`docker exec -i food-market-prod-postgres pg_restore -U food_market -d food_market --clean --if-exists < <дамп>`
|
||
(на пустую/битую БД; при живой — сначала остановить api). Дампы: `/opt/food-market-data/backups/{hourly,daily}` или офсайт dev-vm `~/fm-prod-backups`.
|
||
3. Проверить `/health/ready` и вход. Если проблема в миграции — откат образа на прошлый SHA
|
||
(миграции в этом проекте аддитивные; down-миграции не применяем на проде).
|
||
|
||
## НЕ трогать
|
||
shared-postgres (5434) и его базы (sklad/purchase/calcman/makesales); gateway nginx (только чтение);
|
||
стенд (test.admin.food-market.kz); публичный сайт food-market.kz (контейнер public, PUBLIC_TAG=test).
|