Files
han-app/modules/module-09-observability.md
T

577 lines
31 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-09. Наблюдаемость production-like контура
> Статус: целевая спецификация наблюдаемости MVP на одной VM.
> Источники: [`README.md`](README.md), [`arch-00-glossary.md`](arch-00-glossary.md), [`arch-01-system-architecture.md`](arch-01-system-architecture.md), [`arch-02-api-contracts.md`](arch-02-api-contracts.md), [`arch-03-docker-compose-blueprint.md`](arch-03-docker-compose-blueprint.md), [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), [`arch-05-agent-development-process.md`](arch-05-agent-development-process.md), [`module-01-api-backend.md`](module-01-api-backend.md)[`module-08-keycloak.md`](module-08-keycloak.md).
## 1. Цели и границы
Наблюдаемость должна позволять:
- найти пользовательский запрос по `request_id`, `trace_id` или `ux_session_id`;
- восстановить путь «frontend → nginx → API → Safety → S3/Open Lines»;
- измерять доступность, задержку, ошибки и насыщение каждого сервиса;
- обнаруживать backlog, DLQ, circuit open, потерю telemetry и истечение TLS;
- расследовать security/audit события без записи PII и секретов;
- проверять SLO по данным, независимым от бизнес-логов.
Telemetry не является источником бизнес-истины и не влияет на auth, safety verdict или доставку сообщений. Недоступность Collector не должна блокировать запросы. Audit в `han_app` — отдельный durable контур.
## 2. Production-like решение MVP
### 2.1. Обязательный минимум в основном Compose
Архитектура явно требует только `otel-collector`. Поэтому **основной production-like Compose обязан содержать Collector, но не обязан размещать Prometheus/Grafana/Loki/Tempo на той же VM**.
Предпочтительный operable-вариант после выбора backend:
1. приложения экспортируют OTLP gRPC в `otel-collector:4317`;
2. Collector отправляет telemetry в выбранный удалённый управляемый OTLP backend провайдера;
3. JSON stdout остаётся аварийным локальным журналом Docker с rotation;
4. пока удалённый backend не выбран, допустим архитектурный минимум из arch-03: bounded JSON stdout/platform logs и Collector `debug` exporter с sampling в acceptance; такой режим не считается полноценным production-хранением и не закрывает alerting/SLO.
Требуемые возможности удалённого backend: OTLP ingest, поиск traces, PromQL-совместимые или эквивалентные metrics, поиск структурированных logs, alerting, RBAC, retention и TLS.
### 2.2. Самостоятельно размещаемая опция
Опциональный Compose profile `observability-local` может включать:
- Prometheus — scrape метрик Collector/Redis/Keycloak/nginx exporters;
- Grafana — dashboards и alerts;
- Loki — логи;
- Tempo — traces.
Он **не включается по умолчанию на малой VM**: стек требует дополнительной RAM/диска и сам становится объектом backup/monitoring. Для operable local-варианта нужны отдельный volume каждому backend, retention limits, compaction, auth через ops/VPN и отсутствие host ports. Grafana доступна только через отдельный защищённый ops route/VPN, не через публичный `/`.
Рекомендуемый минимум VM при local profile: дополнительно 4 vCPU, 8 ГБ RAM и 100+ ГБ SSD сверх приложения; точный размер — после измерения ingest.
## 3. Архитектура Collector
### 3.1. Компоненты
```text
backend services ─OTLP gRPC/HTTP─┐
nginx/Redis/Keycloak exporters ──┼─> otel-collector
Docker JSON stdout ─filelog───────┘ ├─ OTLP/TLS remote backend
├─ Prometheus endpoint (optional)
└─ debug exporter (acceptance only)
```
Collector запускается одним сервисом MVP. При росте разделяется на agent/gateway: локальный agent принимает и буферизует, remote gateway выполняет policy/export.
### 3.2. Receivers
- `otlp` gRPC `0.0.0.0:4317` — основной internal receiver;
- `otlp` HTTP `0.0.0.0:4318` — совместимость SDK;
- `prometheus` — scrape самого Collector, Keycloak metrics, Redis exporter, nginx exporter и сервисных `/metrics`, если они не идут OTLP;
- `filelog` — только если Docker logging driver предоставляет read-only каталог/volume; парсит JSON stdout без чтения secret-файлов;
- `hostmetrics` — CPU, memory, filesystem, network VM/container host, если Collector получает только необходимые read-only mounts.
Порты `4317`, `4318`, `8888`, `8889` используют `expose`, не `ports`. Receiver доступен только в сети `observability`.
### 3.3. Processors и порядок
Во всех pipelines первым стоит защита памяти, последним — batch:
1. `memory_limiter`: check interval 1s, soft/hard limit относительно container memory;
2. `resource`: нормализует `service.namespace=han-chat`, `deployment.environment`, `service.version`;
3. `attributes`: удаляет/маскирует sensitive attributes;
4. `transform`: нормализует route/status/error semantic conventions;
5. `filter`: исключает health noise, debug events и запрещённые поля;
6. `probabilistic_sampler` или tail sampling для traces;
7. `batch`: bounded batch/timeout;
8. при remote export — `queued_retry`/sending queue и `file_storage` extension.
`memory_limiter` не заменяется Docker OOM limit. При pressure Collector отбрасывает telemetry контролируемо и увеличивает `otelcol_processor_refused_*`.
### 3.4. Exporters
- `otlp/remote`: TLS verify, endpoint и auth header из secret env/mount;
- `prometheus`: optional pull endpoint только internal;
- `debug`: только `APP_ENV=test|acceptance`, verbosity normal; production debug exporter по умолчанию выключен;
- `loki`/`otlphttp` — только если выбран backend и его контракт закреплён.
Секрет exporter-а не должен появляться в rendered config, логах или `/debug/configz`. Config монтируется read-only; secret подставляется env.
### 3.5. Extensions
- `health_check` — internal endpoint, используется Compose;
- `pprof`/`zpages` — только при явном ops profile, internal network;
- `file_storage` — persistent sending queue на volume `otel-queue`;
- `basicauth`/`oauth2client` — если требует remote backend.
### 3.6. Принципиальная конфигурация
```yaml
receivers:
otlp:
protocols:
grpc: {endpoint: 0.0.0.0:4317}
http: {endpoint: 0.0.0.0:4318}
prometheus:
config:
scrape_configs:
- job_name: otel-collector
static_configs: [{targets: ["127.0.0.1:8888"]}]
processors:
memory_limiter:
check_interval: 1s
limit_mib: 384
spike_limit_mib: 96
resource/common:
attributes:
- {key: service.namespace, value: han-chat, action: upsert}
- {key: deployment.environment, value: "${env:APP_ENV}", action: upsert}
attributes/redact:
actions:
- {key: http.request.header.authorization, action: delete}
- {key: http.request.header.cookie, action: delete}
- {key: url.query, action: delete}
- {key: db.statement, action: delete}
filter/noise:
error_mode: ignore
traces:
span:
- 'attributes["http.route"] == "/health/live"'
batch:
send_batch_size: 1024
timeout: 5s
exporters:
otlp/remote:
endpoint: "${env:OTEL_REMOTE_ENDPOINT}"
tls: {insecure: false}
headers: {authorization: "${env:OTEL_REMOTE_AUTH_HEADER}"}
extensions:
health_check: {endpoint: 0.0.0.0:13133}
file_storage: {directory: /var/lib/otelcol/queue}
service:
extensions: [health_check, file_storage]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resource/common, attributes/redact, filter/noise, batch]
exporters: [otlp/remote]
metrics:
receivers: [otlp, prometheus]
processors: [memory_limiter, resource/common, attributes/redact, batch]
exporters: [otlp/remote]
logs:
receivers: [otlp]
processors: [memory_limiter, resource/common, attributes/redact, filter/noise, batch]
exporters: [otlp/remote]
telemetry:
metrics: {address: 0.0.0.0:8888}
```
Конкретная версия schema проверяется командой Collector `validate`; image закрепляется по digest. Значения memory/batch/queue — стартовые, не SLO.
## 4. Resource attributes и корреляция
Обязательные resource attributes:
- `service.name`: `nginx`, `api-backend`, `message-safety`, `bitrix-local-app`, `bitrix-sync`, `keycloak`, `redis`, `otel-collector`;
- `service.namespace=han-chat`;
- `service.version=<release-or-git-sha>`;
- `deployment.environment=production-like|production`;
- `host.name`/`service.instance.id` без публичного IP.
Обязательные поля request-события:
- `request_id`;
- `trace_id`, `span_id`;
- `ux_session_id` — nullable, только когда передан;
- `service.name`;
- `environment` либо canonical `deployment.environment`.
`request_id` формирует/валидирует nginx; сервис возвращает его клиенту и передаёт downstream. `trace_id` берётся из active span. `ux_session_id` не является auth и не должен использоваться как metric label.
## 5. W3C propagation
- принимаются только валидные `traceparent` и опциональный `tracestate`;
- nginx передаёт context в API; при edge instrumentation создаёт server span;
- API создаёт child spans для PostgreSQL, Redis, S3, Safety, Open Lines и JWKS;
- internal calls передают `traceparent`, `tracestate`, `X-Request-ID`;
- `baggage` по умолчанию не принимается от внешнего клиента; если включён, allow-list исключает PII;
- Bitrix24/S3 могут не вернуть context: внешний client span всё равно закрывается результатом;
- async outbox/inbox связывается span link с исходным trace; новый worker trace не притворяется продолжением спустя долгий срок.
Frontend может отправлять валидный `traceparent`, но backend не доверяет его sampling/security атрибутам.
## 6. JSON stdout contract
Одна JSON-запись на строку UTF-8:
```json
{
"timestamp": "2026-07-10T09:00:00.123Z",
"level": "INFO",
"service.name": "api-backend",
"service.version": "git-abcdef0",
"environment": "production-like",
"module": "message_service",
"event": "message.delivery.completed",
"message": "Message delivery completed",
"request_id": "01J...",
"trace_id": "32hex",
"span_id": "16hex",
"ux_session_id": "uuid-or-null",
"route": "/api/v1/dialogs/{dialog_id}/messages",
"method": "POST",
"status_code": 201,
"duration_ms": 742,
"dependency": "bitrix-local-app",
"outcome": "success",
"error_code": null
}
```
Правила:
- `event` — стабильная mnemonic, `message` — безопасное описание;
- route — template, никогда raw URI с id/query;
- stack trace допускается только в internal error log после redaction;
- message text, callback body, SQL values и file content запрещены;
- Docker driver ограничен `50m × 5`, но это buffer, не retention backend;
- multiline stack trace сериализуется полем JSON, не отдельными строками.
## 7. Redaction и data minimization
Удаляются или маскируются:
- `Authorization`, Cookie, Set-Cookie, JWT, OAuth/code/refresh/access tokens;
- raw OTP/mock code, Keycloak admin/client password;
- phone/email/full name, device id, IP по policy (допустим HMAC/truncated);
- message text, filenames с PII, document/file contents;
- DSN/password, Redis URL, S3 keys;
- presigned URL и любая query string;
- Bitrix raw payload/download URL/application token;
- `db.statement` с literals; предпочтительно operation/table, не SQL.
Redaction выполняется в SDK/logger **до stdout**, затем повторяется Collector processor. Collector не может считаться единственной защитой. Автотесты отправляют canary secrets/PII и требуют отсутствие во всех трёх сигналах.
## 8. Instrumentation по компонентам
### 8.1. FastAPI-сервисы
- OpenTelemetry ASGI/FastAPI server spans с route template;
- HTTPX client spans с sanitized host/method/status;
- SQLAlchemy/asyncpg spans без параметров и raw statement;
- Redis instrumentation с command name и DB index, без key/value;
- boto/S3 spans: operation/bucket logical name, без object key/query;
- background workers: span на claim/process/finalize, links на origin;
- исключить `/health/live` из traces; readiness оставить в metrics и sampled logs.
### 8.2. PostgreSQL
Собираются pool wait/checked-out, transaction duration, error class, migrations revision, managed PG provider metrics (CPU, storage, connections, locks, replication/PITR state). `user_id`, SQL text и row data не labels.
### 8.3. Redis
`redis_exporter` подключается отдельным ACL user только на `INFO`, `PING`, безопасные latency/keyspace metrics. Нужны memory ratio, evictions, expirations, blocked/rejected clients, command latency, AOF status/rewrite, Pub/Sub buffers, key count/TTL агрегаты. Keys/values не экспортируются.
### 8.4. Keycloak
Включаются management metrics/JVM/HTTP/DB pool. Custom OTP provider публикует counters send/verify/limit/settings-cache без phone labels. Login events идут в JSON/audit с masked/HMAC destination. Public OIDC synthetic проверяется отдельно.
### 8.5. nginx
JSON access log содержит `request_id`, извлечённый `trace_id`, route class, method, normalized path, status, bytes, request/upstream duration/status, TLS version, cache status. `$request` с query не используется.
Collector `filelog` parser:
- разбирает JSON, timestamp и severity;
- переносит `service.name=nginx`;
- превращает пустые/`-` в null;
- route class нормализует в bounded set;
- отбрасывает ACME/health success noise;
- не парсит raw URI в labels.
Native nginx OTEL module предпочтителен, если image/version закреплены. Без него nginx только передаёт W3C context и коррелирует access log; первый server span создаёт API.
### 8.6. Host/Docker
CPU, load, memory/swap, disk usage/inodes/IO, network, container restarts/OOM, Docker daemon health и clock sync. Container name/version — bounded labels; container id не хранится как долгосрочный high-cardinality label.
## 9. Метрики бизнес-потоков
### API и auth
- `han_http_requests_total{service,route,method,status_class}`;
- `han_http_request_duration_seconds`;
- `han_auth_bootstrap_total{outcome}`;
- `han_ux_session_start_total{reason}`;
- `han_jwks_refresh_total{outcome}`;
- `han_rate_limit_decisions_total{scope,outcome}`.
### Message Safety
- checks/verdicts по `allow|deny|pending|error`;
- poll duration/count buckets, timeout и recovery backlog age;
- stub mode info и terminal `400` отдельно, пока действует test-only контракт;
- cache hit, Redis latency, task expired/not-found.
### Open Lines/Bitrix
- message submitted → delivered end-to-end latency;
- local app outbound result/retry/ambiguous/DLQ;
- inbox depth/oldest age/forward retries/duplicate;
- OAuth time-to-expiry/refresh result;
- connector desired/observed state;
- Bitrix 429, circuit state, setup failure.
### Files/S3
- init/complete/promote/delete;
- quarantine object age/orphans;
- checksum/MIME/size reject;
- presigned download issued;
- S3 dependency latency/error by operation and logical bucket.
### Frontend synthetic
- public config/content;
- OIDC discovery/authorization page;
- WS handshake;
- test-user end-to-end flow в отдельной тестовой identity без реального PII.
Никакие UUID/user/session/dialog/task/message id не labels. Они допустимы только в sampled logs/traces при принятой retention.
## 10. Dashboards
1. **Executive/SLO**: availability, error budget burn, p50/p95/p99, message delivery, auth, active incidents.
2. **nginx edge**: RPS, 4xx/5xx, upstream latency/status, 429, WS, TLS, cache.
3. **api-backend**: routes, DB/Redis pools, JWKS, circuits, outbox/safety backlog, S3.
4. **message-safety**: verdicts, pending/poll, task TTL, Redis, distribution stub outcomes.
5. **bitrix-local-app**: install/OAuth, connector, outbound/inbox/DLQ, API/Bitrix latency.
6. **bitrix-sync**: mode, DB probe, last success/staleness; нельзя показывать CRM sync как рабочий в stub.
7. **Keycloak**: login/OTP/lockout, sessions/tokens, provider settings cache, JVM/DB.
8. **Redis**: memory/evictions/AOF/latency/clients/keyspace.
9. **PostgreSQL/S3**: provider metrics, storage, connections, backup/PITR, object errors.
10. **Business flow**: guest config → OTP → bootstrap → session → dialog → safety → Open Lines → operator reply.
11. **Collector health**: accepted/sent/refused/dropped, queue, retry, exporter errors, memory/CPU.
Каждая панель содержит release annotation, environment filter и links trace→logs по `trace_id`.
## 11. SLI, SLO и alerts
Значения — начальная production-like политика до load/product review:
| SLI | Initial SLO, 30 дней |
|---|---|
| HTTPS edge availability | 99.9% |
| public config/content successful requests | 99.9% |
| protected read API successful requests | 99.5% |
| Keycloak login flow availability | 99.5% |
| text message accepted и доставлен в Open Lines | 99.0% |
| operator inbox applied без permanent loss | 99.5% |
| p95 protected read API | < 750 ms |
| p95 text send без внешнего rate limit | < 5 s |
| telemetry Collector ingest availability | 99.0%, не входит в business availability |
Файловый send измеряется отдельно: p95 не должен превышать configured safety poll budget; user-cancel, safety deny, 4xx validation и edge abuse 429 не считаются server failure. 503/504 и unexpected 5xx считаются.
### Paging alerts
- multi-window burn: 14.4× за 5m/1h или 6× за 30m/6h;
- edge/API 5xx >5% 5 минут;
- text delivery failure >5% 10 минут;
- oldest outbox/inbox/safety task >5 минут либо DLQ >0;
- Keycloak login failures infrastructure class >10% 5 минут;
- PostgreSQL unavailable/connection saturation >90%;
- Redis unavailable, AOF error или sustained evictions;
- Collector exporter queue >80%, dropped/refused telemetry >0 sustained;
- TLS expiry <14 дней warning, <7 дней page;
- disk >85% warning, >92% page; OOM/restart loop;
- managed PG backup/PITR failure.
### Ticket/warning alerts
- p95 regression 20% release-over-release;
- settings/JWKS cache stale;
- bitrix-sync stub probe stale >150s;
- OAuth expires <24h без успешного refresh;
- quarantine orphan growth;
- cardinality/ingest growth >2× baseline.
Alert содержит service, environment, symptom, dashboard, runbook, release и безопасный query; не содержит PII.
## 12. Sampling, cardinality и retention
### Traces
- errors/5xx, circuit, timeout, DLQ, safety final deny и slow requests — 100%;
- обычные успешные requests — 510%;
- health/ACME success — 0%;
- tail sampling предпочтителен в Collector, но head sample SDK должен оставлять достаточно данных;
- sampling decision передаётся W3C.
### Metrics
Allow-list labels; route template вместо raw path; status class/known code; dependency enum. Cardinality budget: целевой <10 000 active series на MVP environment. CI проверяет запрещённые labels.
### Retention initial
- metrics: 30 дней high resolution, 13 месяцев downsampled при доступности backend;
- traces: 7 дней, errors 14 дней;
- technical logs: 14 дней, security/auth logs 30 дней;
- audit `han_app`: 365 дней **только как временное допущение до legal policy**;
- raw Bitrix callback не хранится в telemetry;
- local Docker logs: не более 250 МБ/container и 5 файлов.
Legal retention/erasure имеет приоритет; изменение требует обновления policy и backup lifecycle.
## 13. Collector health и отказоустойчивость
Контролируются:
- `/health` extension;
- process CPU/RSS/restarts;
- accepted/refused/sent/failed spans, points, records;
- batch send size/latency;
- exporter queue capacity/size, enqueue failures, retry age;
- file storage usage/corruption;
- scrape failures;
- config reload/validation.
При remote outage queue хранится на `otel-queue` с bounded size/age. При заполнении отбрасываются сначала low-priority success traces/logs; приложение продолжает работу. Нельзя позволять queue заполнить системный диск.
## 14. Docker Compose
`otel-collector`:
- pinned contrib image;
- networks: только `observability`, а для scrape internal targets — минимально необходимая `backend`;
- `expose`: 4317, 4318, 13133, 8888/8889;
- без host ports;
- config read-only, `otel-queue` volume rw;
- non-root, read-only rootfs, tmpfs `/tmp`, drop capabilities, no-new-privileges;
- initial limit: 0.5 CPU/512 MiB, queue disk 510 ГБ; уточнить load test;
- healthcheck extension;
- restart policy с backoff;
- приложения имеют bounded non-blocking OTLP exporter queue.
Доступ к Docker socket запрещён. Для container metrics используется безопасный exporter/hostmetrics, а не unrestricted socket mount.
## 15. Security
- OTLP receiver internal-only; при переходе между hosts — mTLS;
- remote exporter только TLS verify, credentials least privilege;
- Grafana/Prometheus/Loki/Tempo не публичны;
- RBAC: viewer/operator/admin; audit доступа к logs/traces;
- dashboards не показывают PII;
- config/secret permissions 0400/0600;
- dependency/image scan и SBOM;
- защита от log injection: JSON encoding, control chars, bounded field lengths;
- telemetry input не исполняет expressions из пользовательских значений;
- регулярная secret-canary проверка и incident deletion procedure.
## 16. Runbooks
### Collector not-ready
1. `docker compose ps otel-collector` и bounded logs.
2. Проверить config validation, memory/OOM, queue volume.
3. Проверить DNS/TLS/auth remote exporter.
4. Не рестартовать бесконечно при полной queue; сначала освободить/расширить безопасно.
5. Бизнес-сервисы оставить работающими; подтвердить local JSON logs.
6. После восстановления проверить drain и gap.
### Telemetry отсутствует у одного сервиса
1. Проверить `service.name`, endpoint/protocol и сеть `observability`.
2. Проверить SDK queue/drop counters и clock.
3. Отправить synthetic request с `X-Request-ID`.
4. Найти его в stdout, Collector accepted и backend.
5. Проверить sampling/filter/redaction rules.
### Remote backend outage
1. Подтвердить exporter errors, а не application outage.
2. Оценить queue fill rate/time-to-full.
3. Ограничить debug exporter; не включать verbose.
4. При длительном outage увеличить sampling только через reviewed config.
5. После восстановления подтвердить drain и создать incident note о потере данных.
### Cardinality/ingest spike
1. Найти новое metric/log attribute по release annotation.
2. Отключить offending instrument/filter в Collector.
3. Проверить raw path/id/user/session labels.
4. Rollback instrumentation при риске стоимости/доступности.
5. Добавить CI regression test.
### Высокая latency сообщения
1. Открыть trace по request id.
2. Разделить API, Safety poll, S3, local app, Bitrix.
3. Проверить circuit, queue age, DB pool и Redis.
4. Не повторять ambiguous message без исходного idempotency key.
5. Следовать runbook зависимого модуля.
### Логи содержат секрет/PII
1. Ограничить доступ и остановить offending export.
2. Сохранить только incident metadata, не копировать значение.
3. Ротировать скомпрометированный secret.
4. Удалить данные по процедуре backend/provider.
5. Исправить source redaction + Collector defense; добавить canary test.
## 17. Проверки и Definition of Done
- Collector config проходит validate и запускается в едином Compose;
- OTLP gRPC и HTTP принимают три сигнала;
- все сервисы имеют правильные resource attributes;
- request проходит nginx/API/Safety/Open Lines с одним `request_id` и связанным trace;
- `ux_session_id` есть только где передан и не является label;
- FastAPI/HTTPX/PG/Redis/S3 workers instrumented;
- nginx JSON parsing и trace correlation проверены;
- Redis/Keycloak/host/Collector metrics доступны;
- dashboards и alerts provisioned из versioned files для выбранного telemetry backend; до его выбора это остаётся acceptance/TBD, а не выполненный production DoD;
- remote outage, queue full, Collector restart и backend recovery rehearsed;
- local profile, если включён, имеет volumes/retention/auth и не публикует порты;
- secret/PII canary отсутствует в logs/traces/metrics;
- cardinality и sampling tests проходят;
- SLO queries воспроизводимы и исключения документированы;
- runbooks связаны с alerts.
## 18. Допущения, TBD и конфликты
### Решения
- O1: обязательный архитектурный минимум — Collector; выбран self-hosted SigNoz
на отдельной VM `192.168.0.5`, доступный по приватному OTLP gRPC.
- O2: Prometheus/Grafana/Loki/Tempo — отдельный operable profile, не скрытая обязательная нагрузка основной VM.
- O3: JSON stdout — аварийный локальный buffer; audit App DB — durable.
- O4: telemetry fail-open для business path, но потеря telemetry alertится.
- O5: ID/PII не labels; source redaction обязательна до Collector.
### TBD
- O-TBD1 закрыт: self-hosted SigNoz, `192.168.0.5:4317`, plaintext только
внутри доверенной приватной сети; UI через SSH jump host.
- O-TBD2: утвердить SLO/RPS/error-budget с product owner.
- O-TBD3: legal retention/erasure и допустимость IP/user-agent.
- O-TBD4: точные sampling и resource limits после load test.
- O-TBD5: поддерживаемый nginx OTEL module и Keycloak native tracing по pinned versions.
- O-TBD6: нужен ли local observability profile в первой VM.
### Обнаруженные архитектурные конфликты
1. `arch-03` разрешает stdout/platform exporter как минимум, но production-like расследования и alerts без backend ограничены. Здесь remote OTLP backend рекомендован, но не объявлен выбранным: провайдер остаётся TBD.
2. `module-05` использует test-only terminal `400` и non-sticky verdict вместо canonical `403`/sticky production verdict. Dashboards обязаны маркировать сервис `stub`; production SLO Safety на нём недостоверен.
3. `module-07` — только DB connectivity stub, тогда как arch-01/02 описывают полноценную CRM sync. Dashboard не должен показывать queue/CRM SLI, которых нет.
4. Retention, RPO/RTO и production SLO открыты в module-01/04/06/08; значения этого документа являются initial ops policy, не закрывают legal/product TBD.
5. Observability env (`OTEL_REMOTE_*`, service names, sampling/queue limits)
добавлены в arch-04 и `.env.example`; sampling/queue limits уточняются после
load test.
## 19. Ссылки на прототип и исходные документы
- VM/Docker logging и firewall: [`../../HAN_chat/deploy/setup-vm-han-chat.sh`](../../HAN_chat/deploy/setup-vm-han-chat.sh).
- Прототипный nginx stdout/access log: [`../../HAN_chat/bitrix-local-app/deploy/nginx/nginx.conf`](../../HAN_chat/bitrix-local-app/deploy/nginx/nginx.conf).
- Архитектурный observability contract: [`arch-02-api-contracts.md`](arch-02-api-contracts.md).
- Compose topology: [`arch-03-docker-compose-blueprint.md`](arch-03-docker-compose-blueprint.md).