564 lines
34 KiB
Markdown
564 lines
34 KiB
Markdown
# arch-07. Контракт наблюдаемости
|
||
|
||
> Канонический контракт telemetry для всех application VM и private SigNoz.
|
||
> Реализация на конкретной VM — в [`module-09-observability-vm1.md`](../VM1_app/documentation/module-09-observability-vm1.md) и [`module-09-observability-vm2.md`](../VM2_services/documentation/module-09-observability-vm2.md).
|
||
> Имена сущностей — [`arch-00-glossary.md`](arch-00-glossary.md). Границы системы — [`arch-01-system-architecture.md`](arch-01-system-architecture.md). HTTP — [`arch-02-api-contracts.md`](arch-02-api-contracts.md). Compose — [`arch-03-docker-compose-blueprint.md`](arch-03-docker-compose-blueprint.md). Env — [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md). Процесс — [`arch-05-agent-development-process.md`](arch-05-agent-development-process.md). Host security — [`arch-06-service-hosting-security.md`](arch-06-service-hosting-security.md).
|
||
|
||
## Назначение
|
||
|
||
Документ фиксирует то, что **должно совпасть между ВМ1 и ВМ2**: схема сигналов, корреляция, redaction, sampling, Collector pipeline и удаленный backend. Локальные `service.name`, scrape targets, дашборды и алерты конкретной машины в этом файле не детализируются.
|
||
|
||
Этот контракт **нельзя** независимо кастомизировать в репозитории VM. Изменение JSON-полей, labels, redaction, sampling или `service.namespace` сначала вносится сюда.
|
||
|
||
## 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
|
||
|
||
Каждый root Compose (ВМ1 и ВМ2) обязан содержать локальный `otel-collector`. Prometheus/Grafana/Loki/Tempo не обязаны размещаться на application VM.
|
||
|
||
Операбельный вариант:
|
||
|
||
1. приложения на каждой VM экспортируют OTLP gRPC только в свой local `otel-collector:4317`;
|
||
2. Collector отправляет telemetry в self-hosted SigNoz на отдельной VM `192.168.0.5:4317`;
|
||
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. Для текущего SigNoz plaintext OTLP разрешён только внутри доверенной приватной сети; UI — через SSH jump host.
|
||
|
||
ВМ2 никогда не использует Docker hostname collector ВМ1. Каждый collector имеет собственный `otel-queue`.
|
||
|
||
### 2.2. Самостоятельно размещаемая опция
|
||
|
||
Опциональный Compose profile `observability-local` может включать Prometheus, Grafana, Loki и Tempo. Он **не включается по умолчанию на малой 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. Нужен ли profile на ВМ1 — TBD профильной спецификации ВМ1.
|
||
|
||
## 3. Архитектура Collector
|
||
|
||
### 3.1. Компоненты
|
||
|
||
```text
|
||
backend services ─OTLP gRPC/HTTP─┐
|
||
nginx/Redis/Keycloak exporters ──┼─> otel-collector
|
||
Docker JSON stdout ─filelog───────┘ ├─ OTLP remote backend (SigNoz)
|
||
├─ 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, exporters и сервисных `/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`: endpoint из secret env/mount; для SigNoz в доверенной private network — plaintext `192.168.0.5:4317`; для любого иного remote — TLS verify;
|
||
- `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}"
|
||
# true только для утверждённого plaintext SigNoz внутри private network;
|
||
# для любого другого remote — false и проверка CA.
|
||
tls: {insecure: ${env:OTEL_REMOTE_TLS_INSECURE}}
|
||
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. Для текущего SigNoz `tls.insecure` и auth header задаются профильной спецификацией VM согласно O1/O-TBD1. Scrape targets кроме самого Collector добавляет спецификация VM.
|
||
|
||
## 4. Resource attributes и корреляция
|
||
|
||
Обязательные resource attributes:
|
||
|
||
- `service.name` — только из реестра ниже;
|
||
- `service.namespace=han-chat`;
|
||
- `service.version=<release-or-git-sha>`;
|
||
- `deployment.environment=production-like|production`;
|
||
- `host.name`/`service.instance.id` без публичного IP.
|
||
|
||
Для logs действуют те же resource attributes, что для traces/metrics.
|
||
`trace_id` (32 hex) и `span_id` (16 hex) добавляются только из активного
|
||
OpenTelemetry span. SigNoz связывает log со span по этой паре. Host, Redis,
|
||
Docker и systemd события, у которых span отсутствует, коррелируются по
|
||
`host.name`, `service.name`, времени и безопасным request/business IDs;
|
||
синтетические trace/span IDs для них запрещены.
|
||
|
||
На application VM используются два непересекающихся канала:
|
||
|
||
- Python application logs отправляются OTLP Logs SDK в Compose Collector и
|
||
одновременно остаются в JSON stdout как локальный аварийный buffer;
|
||
- platform logs (nginx, Keycloak, Redis, Docker и выбранные systemd units)
|
||
собирает отдельный host agent без доступа к Docker socket.
|
||
|
||
Один источник не может одновременно экспортироваться SDK и host `filelog`.
|
||
Allow-list источников и self-exclusion Collector обязательны, чтобы исключить
|
||
дубли и feedback loop.
|
||
|
||
Реестр `service.name`:
|
||
|
||
| Имя | Владелец |
|
||
|---|---|
|
||
| `nginx` | nginx каждой VM; различать экземпляры `host.name` / `service.instance.id` |
|
||
| `api-backend` | ВМ1 |
|
||
| `delivery-worker` | ВМ1 |
|
||
| `safety-recovery-worker` | ВМ1 |
|
||
| `cleanup-worker` | ВМ1 |
|
||
| `notification-expire-worker` | ВМ1 |
|
||
| `notification-draft-cleanup-worker` | ВМ1 |
|
||
| `bitrix-local-app` | ВМ1 |
|
||
| `keycloak` | ВМ1 |
|
||
| `sms-service` | ВМ1, callback/internal API |
|
||
| `sms-worker` | ВМ1, отправка в Direct |
|
||
| `message-safety` | ВМ2 |
|
||
| `bitrix-sync` | ВМ2 |
|
||
| `redis` | Redis каждой VM; различать экземпляры |
|
||
| `otel-collector` | collector каждой VM |
|
||
| `otel-host-collector` | host telemetry agent ВМ1 |
|
||
|
||
Новое имя добавляется только сюда, затем в спецификацию владельца.
|
||
|
||
Обязательные поля 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 upstream; при edge instrumentation создаёт server span;
|
||
- caller создаёт child spans для своих зависимостей (PostgreSQL, Redis, S3, Safety, Open Lines, JWKS, CRM);
|
||
- 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
|
||
|
||
Детальный список инструментов конкретной VM — в профильной спецификации. Ниже правила, общие для всех сервисов.
|
||
|
||
### 8.1. FastAPI и workers
|
||
|
||
- 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. 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 создаёт upstream.
|
||
|
||
### 8.5. 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. Метрики: запрет high-cardinality
|
||
|
||
Никакие UUID/user/session/dialog/task/message id не labels. Они допустимы только в sampled logs/traces при принятой retention. Allow-list labels; route template вместо raw path; status class/known code; dependency enum.
|
||
|
||
Карта бизнес-метрик принадлежит спецификации VM-владельца сервиса. Сквозные SLI ниже используют сигналы обеих VM.
|
||
|
||
## 10. Сквозные dashboards в SigNoz
|
||
|
||
SigNoz обязан иметь:
|
||
|
||
1. **Executive/SLO**: availability, error budget burn, p50/p95/p99, message delivery, auth, active incidents.
|
||
2. **Business flow**: guest config → OTP → bootstrap → session → dialog → safety → Open Lines → operator reply.
|
||
3. **Collector health**: accepted/sent/refused/dropped, queue, retry, exporter errors, memory/CPU — отдельно по `host.name` ВМ1 и ВМ2.
|
||
|
||
Сервисные дашборды nginx/API/Safety/Keycloak/Redis/sync — в спецификациях VM. Каждая панель содержит 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/ticket alerts, привязанные к конкретному сервису, задаёт спецификация VM. Общие инфраструктурные paging alerts:
|
||
|
||
- multi-window burn: 14.4× за 5m/1h или 6× за 30m/6h;
|
||
- PostgreSQL unavailable/connection saturation >90%;
|
||
- 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.
|
||
|
||
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 — 5–10%;
|
||
- health/ACME success — 0%;
|
||
- tail sampling предпочтителен в Collector, но head sample SDK должен оставлять достаточно данных;
|
||
- sampling decision передаётся W3C.
|
||
|
||
### Metrics
|
||
|
||
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 заполнить системный диск. Telemetry outage/overflow fail-open для business и Safety readiness, но создаёт alert.
|
||
|
||
## 14. Docker Compose collector
|
||
|
||
`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;
|
||
- перед collector запускается idempotent `otel-queue-init`: one-shot без сети
|
||
и secrets, с `user: 0:0`, `cap_drop: ALL` и только `CHOWN/FOWNER`, выставляет
|
||
mount root `10001:10001 0700`; collector зависит от
|
||
`service_completed_successfully`;
|
||
- initial limit: 0.5 CPU/512 MiB, queue disk 5–10 ГБ; уточнить load test;
|
||
- healthcheck extension;
|
||
- restart policy с backoff;
|
||
- приложения имеют bounded non-blocking OTLP exporter queue.
|
||
|
||
Новый named volume считается потенциально `root:root`; основной collector не
|
||
запускается от root и volume не получает `0777`. Ownership init проверяется
|
||
после первого create и повторного recreate.
|
||
|
||
Доступ к Docker socket запрещён. Для container metrics используется безопасный exporter/hostmetrics, а не unrestricted socket mount.
|
||
|
||
## 15. Security
|
||
|
||
- OTLP receiver internal-only; при переходе между hosts — mTLS, кроме явно зафиксированного plaintext SigNoz в private network;
|
||
- remote exporter 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.
|
||
|
||
### Логи содержат секрет/PII
|
||
|
||
1. Ограничить доступ и остановить offending export.
|
||
2. Сохранить только incident metadata, не копировать значение.
|
||
3. Ротировать скомпрометированный secret.
|
||
4. Удалить данные по процедуре backend/provider.
|
||
5. Исправить source redaction + Collector defense; добавить canary test.
|
||
|
||
Разбор высокой latency сообщения — в спецификации VM, которая владеет соответствующим hop.
|
||
|
||
## 17. Общий Definition of Done
|
||
|
||
- Collector config проходит validate и запускается в root Compose каждой VM;
|
||
- OTLP gRPC и HTTP принимают три сигнала;
|
||
- все сервисы имеют правильные resource attributes из реестра §4;
|
||
- request проходит nginx/API/Safety/Open Lines с одним `request_id` и связанным trace;
|
||
- `ux_session_id` есть только где передан и не является label;
|
||
- 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;
|
||
- dashboards и alerts provisioned из versioned files для SigNoz; до полного provisioning это остаётся acceptance/TBD, а не выполненный production DoD.
|
||
|
||
Профильный DoD VM дополняет этот список своими сервисами и не переопределяет контракт.
|
||
|
||
## 18. Допущения, TBD и конфликты
|
||
|
||
### Решения
|
||
|
||
- O1: обязательный архитектурный минимум — local Collector на каждой application VM; выбран 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.
|
||
|
||
Поля JSON, resource attributes, redaction, sampling, `service.namespace` и адрес SigNoz меняются только здесь.
|
||
|
||
### 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.
|
||
|
||
### Обнаруженные архитектурные конфликты
|
||
|
||
1. `arch-03` разрешает stdout/platform exporter как минимум, но production-like расследования и alerts без backend ограничены. Закрыто выбором SigNoz (O1 / O-TBD1); stdout остаётся аварийным buffer.
|
||
2. Test-only режимы Message Safety не входят в production SLO. Production dashboards используют canonical `403`, sticky verdict и фактический `processing_mode`; значение `stub` считается ошибкой cutover. Детали — спецификация ВМ2.
|
||
3. Queue/CRM SLI включаются только после module-07 preflight/cutover и отражают фактическое состояние `bitrix-sync` на ВМ2; синтетический CRM success запрещён. Детали — спецификация ВМ2.
|
||
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. Ссылки
|
||
|
||
- Профильные спецификации: [`module-09-observability-vm1.md`](../VM1_app/documentation/module-09-observability-vm1.md), [`module-09-observability-vm2.md`](../VM2_services/documentation/module-09-observability-vm2.md).
|
||
- VM/Docker logging и firewall: [`arch-06-service-hosting-security.md`](arch-06-service-hosting-security.md) и профильные module-10/runbook.
|
||
- Nginx stdout/access log: [`arch-08-nginx.md`](arch-08-nginx.md) и профильные module-03.
|
||
- HTTP/OTLP registry: [`arch-02-api-contracts.md`](arch-02-api-contracts.md).
|
||
- Compose topology: [`arch-03-docker-compose-blueprint.md`](arch-03-docker-compose-blueprint.md).
|