# 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. `` / `` — репозиторий ВМ1. Host ACME — ``. ## 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= \ 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://: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 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 --cert-name `. Private `8443` на ВМ1 нет. ## 8. Миграции и seed ВМ1 Preflight/upgrade: ```bash cd 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. записать `` из `sms-worker`; 6. deploy `sms-service`/worker и callback route nginx ВМ1, `KEYCLOAK_OTP_MOCK_ENABLED=true`; 7. Keycloak expand migration/SPI; 8. provider smoke на ``; 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 docker compose up -d keycloak ``` Bootstrap admin только на первый запуск, затем MFA named admin и удаление bootstrap. Realm: public client PKCE S256, issuer `https:///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 шаги 1–3): 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:///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:/// curl -fsS https:///api/v1/public/app-config curl -fsS https:///api/v1/public/content curl -fsS https:///auth/realms/han-chat/.well-known/openid-configuration curl -i https:///internal/safety/v2/messages/check openssl s_client -connect :443 -servername ``` 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://: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 команды в `` ВМ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 ``, 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).