11 KiB
module-10-vm1. Runbook развёртывания ВМ1 HAN Chat
Статус: целевой runbook репозитория ВМ1.
Общий контракт (VPC/SG, PG, S3, ролиdeploy, TLS процедура, порядок cutover) —arch-10-deployment.md.
ВМ2 —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. Пример:
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:
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:
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
- seed
otp.phone.*; - schema/role
sms, migrations, seedsms_setting/activeauth_otp; - test env с mock Direct;
- production Direct
TOKEN_1, sender, template, callback credentials; - записать
<IDGTL_STATIC_EGRESS_IP>изsms-worker; - deploy
sms-service/worker и callback route nginx ВМ1,KEYCLOAK_OTP_MOCK_ENABLED=true; - Keycloak expand migration/SPI;
- provider smoke на
<IDGTL_TEST_PHONE>; - только после evidence —
KEYCLOAK_OTP_MOCK_ENABLED=false; - проверить durable order, resend, limits.
Rollback SMS: вернуть Keycloak в mock; не удалять schema/journal. Production cutover запрещён при placeholder или нестабильном egress IP.
9. Keycloak bootstrap
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 шаги 1–3):
- Redis ВМ1,
otel-queue-init, Collector; - API, SMS, Keycloak, local app;
- edge nginx последним; после readiness —
nginx -t -c /tmp/nginx.confи HUP. 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
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 + arch-07. Сквозной X-Request-ID до Safety span — совместно с ВМ2.
14. Legacy gate перед cutover Safety
Перед MESSAGE_SAFETY_URL на ВМ2:
- validator принимает только
https://<private-vm2-name>:8443, требует CA path, запрещает Docker hostname и plaintext; - internal CA root-owned; read-test UID
api-backend, negative посторонний UID; - local
message-safety, Redis DB2 и local rules-version env удалены из Compose/validator; - root-owned stack unit;
deployне вdocker; DOCKER-USERcounters черезconntrack --ctorigdstportпосле Docker restart и reboot;- images digest; rollback по compatible digests;
- ordered startup этого runbook, не legacy
docker compose up -d; 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). 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. - ВМ2:
module-10-deployment-vm2.md. - Указатель:
module-10-deployment-runbook.md.