# module-10-vm2. Runbook развёртывания ВМ2 Processing > Статус: целевой runbook репозитория ВМ2. > Общий контракт (VPC/SG, PG, S3, роли `deploy`, TLS процедура, порядок cutover) — [`arch-10-deployment.md`](../../architectory/arch-10-deployment.md). > ВМ1 — [`module-10-deployment-vm1.md`](../../VM1_app/documentation/module-10-deployment-vm1.md). Не переносить команды ВМ1 и не шарить Compose/IAM/secrets. ## 1. Границы ВМ2 владеет nginx public `80/443` (только exact CRM webhook) и private `8443` (Message Safety), `message-safety` API/worker, `clamd`/`freshclam`, `bitrix-sync`, Redis Safety, Collector. Guest API, Keycloak, SPA и SMS callback здесь не разворачиваются. `` / `` — репозиторий ВМ2. Public ACME host — ``. Private DNS `processing.internal` не публикуется. ## 2. Sizing и load gates Начальный sizing без local Grafana: 4 vCPU, 8 ГБ RAM, 80 ГБ SSD, 4 ГБ swap; Redis 512 МиБ; Collector 512 МиБ + 5–10 ГБ queue; свободный диск ≥30%. До real traffic — load test module-05 §15.4: - sustained 10 text checks/s: p95 ≤2 с, p99 ≤5 с; - sustained 2 file checks/s на 5 worker slots: среднее processing ≤2.5 с, p95 ≤60 с, public wait ≤300 с; - 100 pending принимаются; 101-й file POST — retryable `503` без новой task; - RPS overflow — `429 + Retry-After`; - long Safety poll не блокирует WS/read API ВМ1; - если gate не пройден — увеличить slots/CPU/clamd scan lanes; production traffic не открывать. Monthly availability SLO Safety в MVP не задаётся. Scale-out/ВМ3: sustained CPU/RAM >70%, queue age >30 с, провал performance gates, contention `bitrix-sync` или независимый release cadence. Workers масштабируются первыми по queue depth, `clamd` — scan lanes. ## 3. Hardening и egress Arch-10 §5 / arch-06. Public Docker ports — `80,443`. Private `8443` не internet SG. Default-deny egress. Bootstrap window для registry/OS, затем закрыть. Оставить: S3, approved Bitrix portal (`bitrix-sync`), signature CDN (`freshclam`), DNS/NTP, SigNoz `4317`, PG. Постоянный open egress запрещён. Отдельный IAM principal Selectel: только VM2 secret names. ## 4. Release layout и `.env` ВМ2 Checkout exact SHA. Структура: root Compose, `nginx`, Safety, `bitrix-sync`, Redis Safety, `observability`. Frontend-static нет. Секреты ВМ2 (не в `.env` ВМ1): - PG DSN роли Safety/`bitrix_sync` + CA; - `MESSAGE_SAFETY_REDIS_URL`; - Safety mode file `/etc/han-chat/message-safety-mode.env` (не repository `.env`); - S3 **read-only quarantine** credentials; - internal TLS PEM `8443`; - Bitrix sync webhook tokens, portal credentials, `BITRIX_WEBHOOK_ALLOWED_CIDRS`; - OTEL exporter secrets. `BITRIX_SYNC_ENABLED=false` до §9. Paired tokens local app ВМ1 здесь не живут. ## 5. Images Один immutable image API+worker Safety, отдельные containers. MVP: 1 API + 1 worker с 5 file-worker slots. Build без production secrets. Frontend на ВМ2 не собирается. ## 6. Root Compose ВМ2 Сервисы: public/private nginx, `message-safety-api`, `message-safety-worker`, `clamd`, `freshclam`, `bitrix-sync`, Redis Safety, local `otel-collector`. Networks: `public` (только nginx webhook/ACME), `backend`, `egress` (freshclam, bitrix-sync, Safety worker → S3/PG/DNS, collector → SigNoz), `observability`. Safety API/clamd/Redis без общего internet egress. Volumes: Redis Safety data (rebuildable), ClamAV signatures, ACME public, `otel-queue` + init. Frontend-static нет. Published: nginx 80/443; `8443` только SG ВМ1/ops. Compose gate — arch-10 применительно к этому Compose. ## 7. TLS ВМ2 Public: arch-10 §9, `-d `. HTTP webhook paths — без redirect с query token ([`module-03-nginx-vm2.md`](module-03-nginx-vm2.md)). Private `8443`: сертификат internal CA, SAN = private DNS. Не Let's Encrypt. Caller ВМ1 проверяет CA bind. ## 8. Миграции Safety Stub не создаёт PG tables. Production v2: schema `message_safety`, lease/fencing smoke. Первая migration создаёт `config_versions` и seed version 1; readiness без ровно одной valid active version не открывается. Config-only rollout — отдельный root-owned job под config-admin role: immutable draft, JSON Schema, artifacts, approvals, транзакционный activate. Runtime — только `SELECT` к config. Rollback не реактивирует retired row: клон в новую monotonic version. ## 9. Controlled rollout `bitrix-sync` До `BITRIX_SYNC_ENABLED=true`: 1. custom Contact fields и env names; universal CRM `UF_CRM_` → `ufCrm_`; 2. smart process конфликтов и `bitrix_sync.settings`; 3. входящий webhook техпользователя, права module-07 §13; 4. два HTTP-webhook на `https:///bitrix/sync/webhook/...?token=...`; query/body не в журналах; local app handler не создавать; 5. expand migrations `han_app` (делает ВМ1/api-backend) затем `bitrix_sync`; GRANT и negative tests; 6. `cutover_watermark`; отменить pending до него с `initial_full_sync_cutover`; 7. не создавать backfill; 8. image с sync disabled: `/health/live`, `sync_disabled`, smoke Bitrix methods; 9. открыть на nginx ВМ2 только два exact routes для `BITRIX_WEBHOOK_ALLOWED_CIDRS`; valid/invalid source IP; нет запросов на ВМ1; 10. включить sync; canary post-watermark; нет CRM ID в App DB; 11. canary window: queue age, 429, DLQ, webhook lag, reconciliation, source-IP rejects. Rollback: закрыть webhook или `503`; `BITRIX_SYNC_ENABLED=false`; ВМ1 не изменяется; mapping не удалять; schema не downgrade. ### Изменение source IP Битрикс24 Сигнал: всплеск Contact из reconciliation + `webhook_rejected_total{reason="source_ip"}`. 1. Сопоставить bounded-retention журнал rejects; query/body не извлекать. 2. Подтвердить адрес как инфраструктуру Битрикс24; query token сам по себе недостаточен. 3. Минимальный CIDR в version-controlled allow-list, review, `nginx -t` через deployment unit. 4. Safe reload; приём webhook без query/body в logs. 5. Rejects прекратились, lag нормализовался. 6. Ошибочное расширение — rollback allow-list. Автодобавление IP запрещено. ## 10. Ordered startup ВМ2 1. Redis Safety, `otel-queue-init`, Collector; 2. `clamd`/`freshclam`, Safety API/worker, `bitrix-sync`; 3. nginx последним: оба TLS, exact webhook, capability health, signature age, negative ingress/egress. До enablement `bitrix-sync` — `sync_disabled`. Safety v2 capability `text|links|files|worker`. Redis Safety не core gate readiness. ## 11. Public smoke ВМ2 ```bash curl -I http:/// curl -i https:///internal/sync/v1/status curl -i https:///bitrix/sync/webhook/contact openssl s_client -connect :443 -servername ``` Expected: HTTP redirect/ACME policy; internal 404; GET webhook 405/404; отдельный cert. POST webhook — отдельный form-urlencoded test allowed/denied IP без query token в history. Private `8443` проверяется с ВМ1/ops, не из интернета. ## 12. Observability ВМ2 [`module-09-observability-vm2.md`](module-09-observability-vm2.md) + arch-07. Paging: MOCK, Safety config, sync DLQ, queue age. ## 13. Cutover, rollback, reprovision Cutover gates: private TLS chain/SAN; Safety v2 PG migration и lease/fencing smoke; capability `text|links|files|worker`; S3 Gate 4 (Safety read-only); performance §2; egress negative tests. Approvals: Safety Service, Rule Pack, Security, Product, Operations. Только после них ВМ1 переключает `MESSAGE_SAFETY_URL`. При потере ВМ2 fail-open запрещён. Reprovision из immutable image; отдельный IAM; Redis пустой; gates повторяются. RTO ≤4 ч; restore rehearsal ≥2 раза в год. Rollback ВМ2 не требует изменения nginx ВМ1. Caller rollback — runbook ВМ1. ## 14. Emergency MOCK и Freshclam `deploy` без root login: ```bash sudo /usr/local/sbin/han-message-safety-mode mock --text-free true --file-free true sudo /usr/local/sbin/han-message-safety-mode mock --text-free true --file-free false sudo /usr/local/sbin/han-message-safety-mode mock --text-free false --file-free true sudo /usr/local/sbin/han-message-safety-mode mock --text-free false --file-free false sudo /usr/local/sbin/han-message-safety-mode standard ``` Helper `root:root 0755`; sudoers только этот executable. Config `root:han-message-safety 0640`, GID `10001`. Нет auto-expiry; incident не закрывать без `standard` и canary. File в MOCK — `scan_status=bypassed`, не `clean`. `deploy` не пишет Compose/Docker. `freshclam`: controlled egress только к signature CDN. Seed `max_signature_age_hours=240`, schema max `720`. Stale/failed update выключает только `files` и alert. Новая база — integrity/EICAR, atomic activate; regression — предыдущая валидная база. ## 15. Rollback, ops, incidents ВМ2 Redis Safety restore — empty/clean, lazy cache ([`module-04-redis-vm2.md`](module-04-redis-vm2.md)). Routine: signature age, Safety queue, sync DLQ/webhook lag, Collector, disk, egress still deny. Incident: Safety timeout — checkpoint, не новый task id; Bitrix down — circuit/DLQ, webhook `503`; MOCK page — вернуть standard; cert processing host и private CA. Потеря ВМ2: новая VM в VPC, hardening, private DNS, secrets из VM2 IAM, empty Redis, Safety migrations, nginx webhook+8443, **не** менять public routes ВМ1 до готовности; затем повторить cutover gates. ## 16. Definition of Done ВМ2 Дополнительно к arch-10 §12: - Compose/nginx/Redis Safety/Collector прошли профильные compose/nginx gates; - private `8443` fail-closed до cutover, затем только источники ВМ1; - webhook allow-list и preflight `BITRIX_SYNC_ENABLED` согласованы; - capability/load gates module-05; - MOCK helper и Freshclam rehearsal; - observability ВМ2 + redaction (нет query/body/file content); - cutover §13 не объявлен выполненным только документацией. ## 17. TBD ВМ2 D-TBD5 Safety v2; D-TBD6 bitrix-sync cutover; performance gates уже в §2. ## 18. Ссылки - Контракт: [`arch-10-deployment.md`](../../architectory/arch-10-deployment.md). - ВМ1: [`module-10-deployment-vm1.md`](../../VM1_app/documentation/module-10-deployment-vm1.md). - Указатель: [`module-10-deployment-runbook.md`](module-10-deployment-runbook.md). - Safety / sync / nginx: [`module-05-message-safety.md`](module-05-message-safety.md), [`module-07-bitrix-sync.md`](module-07-bitrix-sync.md), [`module-03-nginx-vm2.md`](module-03-nginx-vm2.md).