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

11 KiB
Raw Blame History

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

  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

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

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:

  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). 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. Ссылки