Проект разделен на два репозитория
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
# 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 здесь не разворачиваются.
|
||||
|
||||
`<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 МиБ + 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 <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_<digits>` → `ufCrm_<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. 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://<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 и 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).
|
||||
Reference in New Issue
Block a user