Files
han-app/VM1_app/documentation/module-10-deployment-vm1.md
T

214 lines
11 KiB
Markdown
Raw 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.
# module-10-vm1. Runbook развёртывания ВМ1 HAN Chat
> Статус: целевой runbook репозитория ВМ1.
> Общий контракт (VPC/SG, PG, S3, роли `deploy`, TLS процедура, порядок cutover) — [`arch-10-deployment.md`](../../architectory/arch-10-deployment.md).
> ВМ2 — [`module-10-deployment-vm2.md`](../../VM2_services/documentation/module-10-deployment-vm2.md). Не переносить команды ВМ2 и не шарить Compose/secrets.
## 1. Границы
ВМ1 владеет edge nginx `80/443`, `api-backend`, Keycloak, SMS, `bitrix-local-app`, Redis DB0/DB1, Collector. После Safety cutover local Safety/Redis DB2 отсутствуют; `MESSAGE_SAFETY_URL` — private HTTPS ВМ2.
`<BACKEND_ROOT>` / `<BACKEND_REPO_URL>` — репозиторий ВМ1. Host ACME — `<PUBLIC_HOST>`.
## 2. Sizing ВМ1
Final sizing — D-TBD2. Disk после pull/build ≥30% free. `PUBLIC_DOCKER_PORTS=80,443`.
## 3. Hardening
По arch-10 §5 / arch-06. Пример:
```bash
sudo DEPLOY_USER=deploy \
DEPLOY_DIR=/opt/han-chat \
SSH_PORT=<SSH_PORT> \
SWAP_SIZE_GB=4 \
PUBLIC_DOCKER_PORTS=80,443 \
./deploy/setup-vm-han-chat.sh
```
Gate 2 — arch-10. Break-glass вне VM.
## 4. Release layout и `.env` ВМ1
Checkout exact SHA в `/opt/han-chat/backend`. Структура: root Compose, `nginx`, `keycloak`, `redis`, `observability`, frontend artifact.
Обязательные группы секретов/config ВМ1:
- `APP_ENV`, release, log level;
- private PG host/port/database и TLS CA; runtime DSN в secret backend;
- Redis ACL URLs DB0/DB1 только в secret backend; после cutover DB2 нет;
- public web/API/auth URLs;
- Keycloak realm/audience/hostname/bootstrap/provider secrets;
- SMS DB URL, парные Keycloak↔SMS tokens, Direct `TOKEN_1`, callback credentials;
- paired service tokens arch-02;
- Bitrix local app client/application/encryption secrets (не CRM sync webhook ВМ2);
- S3 API credentials (не Safety read-only key ВМ2);
- OTEL exporter secrets;
- nginx/TLS/rate limits;
- frontend public build values;
- после cutover: `MESSAGE_SAFETY_URL=https://<private-vm2-name>:8443` и `MESSAGE_SAFETY_CA_HOST_PATH`.
Пары: `BITRIX_LOCAL_APP_INTERNAL_TOKEN == BITRIX_INTERNAL_API_TOKEN`, `BITRIX_API_FORWARD_TOKEN == BITRIX_API_INBOX_TOKEN`, `KEYCLOAK_SMS_SERVICE_TOKEN == SMS_SERVICE_TOKEN`.
`FRONTEND_DEV_PROXY_ENABLED=false`. Safety timeout согласован с nginx ВМ1. Validation — arch-10 §8; `validate-env` в этом репозитории.
## 5. Images и frontend
Pull или build без production secrets. Frontend:
```bash
cd <FRONTEND_PROJECT_PATH>
npm ci
npm run test
npx expo export --platform web
```
Artifact в versioned `frontend-static`. Build env — только public URL/realm/client id. Secret scanner: нет service tokens/mock OTP/S3 keys.
Image/frontend gate: images по digest; static без secrets; nginx image с request-id/TLS; disk >30% free.
## 6. Root Compose ВМ1
Сервисы: edge `nginx`, `api-backend`, `keycloak`, `sms-service`, `sms-worker`, `bitrix-local-app`, Redis DB0/DB1, local `otel-collector`. После cutover — без `message-safety` и Redis DB2.
Networks: `public`, `backend`, `egress` (`sms-worker`; Keycloak входит только при `KEYCLOAK_YANDEX_CAPTCHA_ENABLED=true` и только с destination allow-list SmartCaptcha API), `observability`. `sms-service` без egress при отдельном worker.
Volumes: `redis-data`, ACME, `frontend-static`, `otel-queue` + `otel-queue-init`. Единственные published mappings — nginx 80/443.
Compose gate — arch-10 применительно к этому Compose.
## 7. TLS ВМ1
Arch-10 §9 / arch-08, `-d <PUBLIC_HOST> --cert-name <PUBLIC_HOST>`. Private `8443` на ВМ1 нет.
## 8. Миграции и seed ВМ1
Preflight/upgrade:
```bash
cd <BACKEND_ROOT>
docker compose run --rm api-backend alembic current
docker compose run --rm bitrix-local-app alembic current
# PITR marker, затем:
docker compose run --rm api-backend alembic upgrade head
docker compose run --rm bitrix-local-app alembic upgrade head
```
Shared `han_app.sync_queue` мигрирует api-backend **до** sync cutover на ВМ2, но grants `bitrix_sync_user` — после обеих migrations (см. runbook ВМ2). Seed `app_settings` idempotent из этого репозитория.
SMS schema/migrations выполняет `sms-service` на ВМ1.
### Controlled rollout real SMS
1. seed `otp.phone.*`;
2. schema/role `sms`, migrations, seed `sms_setting`/active `auth_otp`;
3. test env с mock Direct;
4. production Direct `TOKEN_1`, sender, template, callback credentials;
5. записать `<IDGTL_STATIC_EGRESS_IP>` из `sms-worker`;
6. deploy `sms-service`/worker и callback route nginx ВМ1, `KEYCLOAK_OTP_MOCK_ENABLED=true`;
7. Keycloak expand migration/SPI;
8. provider smoke на `<IDGTL_TEST_PHONE>`;
9. только после evidence — `KEYCLOAK_OTP_MOCK_ENABLED=false`;
10. проверить durable order, resend, limits.
Rollback SMS: вернуть Keycloak в mock; не удалять schema/journal. Production cutover запрещён при placeholder или нестабильном egress IP.
## 9. Keycloak bootstrap
```bash
cd <BACKEND_ROOT>
docker compose up -d keycloak
```
Bootstrap admin только на первый запуск, затем MFA named admin и удаление bootstrap. Realm: public client PKCE S256, issuer `https://<PUBLIC_HOST>/auth/realms/han-chat`, без `--import-realm` на живой production без diff. Keycloak gate — discovery/JWKS HTTPS, OTP fail-closed, settings bridge.
## 10. Ordered startup ВМ1
После готовности ВМ2 (arch-10 §10 шаги 13):
1. Redis ВМ1, `otel-queue-init`, Collector;
2. API, SMS, Keycloak, local app;
3. edge nginx последним; после readiness — `nginx -t -c /tmp/nginx.conf` и HUP.
4. `MESSAGE_SAFETY_URL` переключается на ВМ2 **только** после cutover gates runbook ВМ2 и legacy gate §14.
Не использовать host ports для health curl. Expected: Redis `PONG`; Keycloak ready; Collector health; API core DB/Redis/JWKS/settings ready. S3/Safety/Open Lines могут быть `degraded` без снятия read API из readiness, но send path при недоступном Safety остаётся fail-closed. Local app до install может быть `portal_not_installed`.
## 11. Bitrix24 local app и Open Lines
Install/handler/placement URL на `https://<PUBLIC_HOST>/bitrix/...`. Canonical internal path `/internal/openlines/v1/*`, не prototype `/bitrix-internal/*`. Open Lines gate: connector line 8, outbound once, operator reply, duplicate callback безопасен.
CRM webhook robots **не** настраиваются на ВМ1.
## 12. Public smoke ВМ1
```bash
curl -I http://<PUBLIC_HOST>/
curl -fsS https://<PUBLIC_HOST>/api/v1/public/app-config
curl -fsS https://<PUBLIC_HOST>/api/v1/public/content
curl -fsS https://<PUBLIC_HOST>/auth/realms/han-chat/.well-known/openid-configuration
curl -i https://<PUBLIC_HOST>/internal/safety/v2/messages/check
openssl s_client -connect <PUBLIC_HOST>:443 -servername <PUBLIC_HOST>
```
Expected: 308; public 200; discovery 200; internal 404; valid cert.
Далее: guest content; 401 без JWT; consent → OTP → PKCE; bootstrap без phone body; `ux_session_id`; silent refresh; logout; wrong OTP. Real SMS mode — по §8.
Safety E2E **со стороны caller** (правила stub до v2 cutover): `ф``422 message_blocked`; allow path; timeout 503/504 без duplicate. Статус Safety `stub` не заменяет production AV.
Files: presigned PUT quarantine, promote/deny, owner-only download, нет URL в logs. Realtime/ownership/idempotency/429 — module-01.
Сквозной first-send до Open Lines требует готовую ВМ2; system end-to-end gate закрывается по arch-10 после обоих runbook.
## 13. Observability ВМ1
[`module-09-observability-vm1.md`](module-09-observability-vm1.md) + arch-07. Сквозной `X-Request-ID` до Safety span — совместно с ВМ2.
## 14. Legacy gate перед cutover Safety
Перед `MESSAGE_SAFETY_URL` на ВМ2:
1. validator принимает только `https://<private-vm2-name>:8443`, требует CA path, запрещает Docker hostname и plaintext;
2. internal CA root-owned; read-test UID `api-backend`, negative посторонний UID;
3. local `message-safety`, Redis DB2 и local rules-version env удалены из Compose/validator;
4. root-owned stack unit; `deploy` не в `docker`;
5. `DOCKER-USER` counters через `conntrack --ctorigdstport` после Docker restart и reboot;
6. images digest; rollback по compatible digests;
7. ordered startup этого runbook, не legacy `docker compose up -d`;
8. `han-secrets` и firewall oneshot явно перезапущены; TLS renew success — пустой stderr.
Ни старый single-VM guide, ни успешный stub Compose не являются evidence. Rollback caller — предыдущий immutable release ВМ1. Уже созданные v2 tasks не down-migrate.
## 15. Rollback, ops, incidents ВМ1
Rollback application-only: previous digests, без Alembic downgrade; при SMS incident — mock OTP, сохранить journal. Redis restore — clean instance, прогрев idempotency из PG ([`module-04-redis-vm1.md`](module-04-redis-vm1.md)). Keycloak restore — проверить issuer/JWKS/PKCE/OTP.
Routine: health, PG/TLS/disk/OTEL/Redis, Keycloak signing, Bitrix connector desired/observed.
Incident triage — arch-10 команды в `<BACKEND_ROOT>` ВМ1. Типовое: API 503 (DB/Redis/JWKS/Safety circuit); send timeout — не новый idempotency key; Redis loss — polling.
Потеря ВМ1: provision в той же VPC, restore secrets из vault, existing PG/S3, TLS `<PUBLIC_HOST>`, Bitrix local app/callbacks verify. Не пересоздавать ВМ2.
## 16. Definition of Done ВМ1
Дополнительно к arch-10 §12:
- Compose/nginx/Redis/Collector ВМ1 прошли профильные compose/nginx gates;
- Keycloak realm/provider/PKCE/OTP готов;
- SMS либо mock с accepted risk, либо real mode после §8;
- Bitrix connector line 8 проверен;
- auth/text/file/realtime E2E caller-side зелёный;
- observability ВМ1 + redaction;
- после cutover — legacy gate §14 закрыт.
## 17. TBD ВМ1
D-TBD1, D-TBD2 (VM1 sizing/SLO), D-TBD8 CLI, D-TBD9 Keycloak admin VPN.
## 18. Ссылки
- Контракт: [`arch-10-deployment.md`](../../architectory/arch-10-deployment.md).
- ВМ2: [`module-10-deployment-vm2.md`](../../VM2_services/documentation/module-10-deployment-vm2.md).
- Указатель: [`module-10-deployment-runbook.md`](module-10-deployment-runbook.md).