Files
han-app/VM2_services/documentation/module-10-deployment-vm2.md
T

193 lines
13 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-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)
> и её [fresh production runbook](../../VM1_app/codebase/backend/deployment/RUNBOOK.production.ru.md).
> Не переносить команды ВМ1 и не шарить Compose/IAM/secrets.
## 1. Границы
ВМ2 владеет nginx public `80/443` (только exact CRM webhook) и private `8443` (Message Safety), `message-safety` API/worker, `bitrix-sync`, Redis Safety и Collector в Compose. KESL 12.4 standalone и root-owned fail-closed broker работают на host VM2 вне Compose; broker слушает `/run/han-kesl/scan.sock`. Guest API, Keycloak, SPA и SMS callback здесь не разворачиваются.
`<BACKEND_ROOT>` / `<BACKEND_REPO_URL>` — репозиторий ВМ2. Public ACME host — `<PROCESSING_PUBLIC_HOST>`. Private DNS `processing.internal` не публикуется.
## 2. Sizing и load gates
Начальный sizing без local Grafana: 4 vCPU, 8 ГБ RAM, 80 ГБ SSD, 4 ГБ swap; Redis 512 МиБ; Collector 512 МиБ + 510 ГБ 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/broker concurrency в пределах подтверждённой KESL capacity; 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; предел задаёт проверенная throughput host KESL/broker.
## 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`), DNS/NTP, SigNoz `4317`, PG; для host KESL — только approved update sources по операторскому KESL runbook. Постоянный 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
Сервисы Compose: public/private nginx, `message-safety-api`, `message-safety-worker`, `bitrix-sync`, Redis Safety, local `otel-collector`. `clamd`/`freshclam` удалены.
Networks: `public` (только nginx webhook/ACME), `backend`, `egress` (`bitrix-sync`, Safety worker → S3/PG/DNS, collector → SigNoz), `observability`. Safety API/Redis без общего internet egress. Worker получает только read/write bind Unix socket `/run/han-kesl/scan.sock`, а не доступ к host command execution.
Volumes: Redis Safety data (rebuildable), ACME public, `otel-queue` + init. KESL database/runtime не являются Compose volumes. Frontend-static нет. Published: nginx 80/443; `8443` только SG ВМ1/ops.
Compose gate — arch-10 применительно к этому Compose.
## 7. TLS ВМ2
Public: arch-10 §9, `-d <PROCESSING_PUBLIC_HOST>`. 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_<latin letters or digits>``ufCrm_<latin letters or digits>`;
2. smart process конфликтов и `bitrix_sync.settings`;
3. входящий webhook техпользователя, права module-07 §13;
4. два HTTP-webhook на `https://<PROCESSING_PUBLIC_HOST>/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. Выполнить operator KESL runbook: проверить KESL 12.4 standalone, database date и ежечасное обновление.
2. `enable/start` broker socket/service; проверить status и owner/group/mode `/run/han-kesl/scan.sock`.
3. Redis Safety, `otel-queue-init`, Collector.
4. Safety API/worker и `bitrix-sync`; files остаётся fail-closed до успешной проверки broker.
5. nginx последним: оба TLS, exact webhook, capability health, KESL database 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://<PROCESSING_PUBLIC_HOST>/
curl -i https://<PROCESSING_PUBLIC_HOST>/internal/sync/v1/status
curl -i https://<PROCESSING_PUBLIC_HOST>/bitrix/sync/webhook/contact
openssl s_client -connect <PROCESSING_PUBLIC_HOST>:443 -servername <PROCESSING_PUBLIC_HOST>
```
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 и KESL
`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.
KESL обновляет базы на host ежечасно по отдельному operator KESL runbook. Seed `max_signature_age_hours=240`, schema max `720`. Stale/failed update выключает только `files` и создаёт alert; scanner error не становится allow. Broker является custom integration: формат/exit semantics `kesl-control --scan-file --action Inform` и throughput подтверждаются target-VM gates.
## 15. Rollback, ops, incidents ВМ2
Redis Safety restore — empty/clean, lazy cache ([`module-04-redis-vm2.md`](module-04-redis-vm2.md)). Routine: KESL version/database date и hourly update, broker/socket status, 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 и KESL/broker 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).
- Исполняемый fresh production runbook ВМ1:
[`RUNBOOK.production.ru.md`](../../VM1_app/codebase/backend/deployment/RUNBOOK.production.ru.md).
- Исполняемый runbook ВМ2:
[`RUNBOOK.ru.md`](../codebase/services/deployment/RUNBOOK.ru.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).