Files
mdm-ordinis/docs-internal/status/2026-05-06-state.md
T
Zimin A.N. bcfb07f547 docs: split integrator guide (Diplodoc) vs internal docs + add build-docs CI
Чёткое разделение audiences:
- docs/ — consumer-facing only, Diplodoc-built. Public contract для
  интеграторов (Альтум, BI, third-party).
- docs-internal/ — operator runbooks, status snapshots, legacy tech pages.
  НЕ в Diplodoc build, НЕ публикуются.

Изменения:
- Move docs/{ops,status,tech} → docs-internal/{ops,status,tech}
- docs/toc.yaml: убраны секции Operations, Архитектура (legacy), Status
- docs/index.md: убран tab "оператор / on-call", focus на integrators
- docs/README.md: rewritten — scope только integrator guide, editorial
  guidelines (no leak internal architecture, stable API contract)
- .yfm: убран явный ignore status/* (теперь не нужно — папка переехала)
- docs-internal/README.md: index для внутренней документации с rationale
  разделения

Scrubbing implementation details из Diplodoc страниц:
- events.md: убраны Strimzi/cluster.142 references, OutboxPoller детали,
  internal alert names (OrdinisOutboxLagHigh, OrdinisOutboxDLQNonEmpty),
  metric names (nsi_outbox_*). Заменены на consumer-observable contract.
- webhooks.md: убраны metric names (nsi_webhook_ssrf_rejected_total),
  alert table (OrdinisWebhookHighFailureRate и т.д.). Retry policy
  переписана на user-side perspective.
- errors.md: убран alert name OrdinisReadApiErrorBudgetBurnFast, Tempo
  endpoint URL, internal connection details.
- x-references.md: OrphanReferenceScanner → general "Ordinis периодически
  детектирует". Убраны metric/alert names. Cascade roadmap без link на
  internal design doc.
- auth.md: убран file path ScopeContext.java.
- bundle-cuod.md: убран Maven module path.
- caching.md: cascade design link заменён на text reference.

CI:
- .gitlab-ci.yml: новый job build-docs (stage: build) — Node 20 alpine,
  pnpm install --frozen-lockfile + pnpm build, artifact docs/_dist на
  неделю. Triggers: auto на docs/**, либо manual web. Готов к follow-up
  pages-deploy job когда host решён.
- Новый pattern .docs-changes для rules.

Build verified clean: 13 HTML pages, 6.9M, no broken links/refs.
2026-05-07 23:27:03 +03:00

20 KiB
Raw Blame History

Состояние разработки НСИ (Ordinis ЦУОД ОДХ)

Версия: v0.9 (anchor v1 — close-out этап, prod readiness в работе) Дата: 2026-05-06 Owner: zimin.an@nstart.space Окружения: staging (192.168.100.161 / cluster.265, STABLE), prod (192.168.100.142 / cluster.142, 🟡 hibernated) Предыдущий snapshot: 2026-05-05-state.md (v0.5)


0. Что изменилось с v0.5

Крупные блоки за последние сутки:

Stability:

  • CNPG operator 1.24.1 → 1.25.4 (server-side apply через manifest, не helm). Дало spec.probes API → выставили liveness 15s/30s × 5 + stopDelay 60s. Закрыло хронические cascade failures когда single-node постгрес умирал под нагрузкой от parallel rolling deploys.
  • Config-server startupProbe + bumped probe timeoutSeconds (5 → 10s). Spring Boot startup до 134с под нагрузкой больше не убивается liveness'ом.
  • CI helm armored: --atomic --cleanup-on-fail --wait --timeout 8m + pre-step авто-rollback застрявших pending-* releases. Сегодня помогло trigger восстановиться от vault-injector-induced cascade без ручного helm rollback.
  • Migrate Job hardened: hook-delete-policy включает hook-failed, activeDeadlineSeconds: 600, ttl 1h. Job в ImagePullBackOff больше не держит CPU request 4 часа подряд (incident 2026-05-05).

Observability:

  • Spring Boot percentiles-histogram на http.server.requests с custom SLO buckets (5ms..5s). Без них histogram_quantile() в Grafana был пустой.
  • 2 Grafana дашборда (sidecar auto-discovery via label grafana_dashboard=1):
    • ordinis-overview — request rate, error rate, latency p50/p95/p99, up статусы
    • ordinis-slo — error budget burn rate (5m/1h/6h/24h), 30d budget remaining
  • SLO burn rate alerts (Google SRE Workbook multi-window/multi-burn-rate):
    • read-api 99.9% (43m бюджет/30d): BurnFast 14.4× × 5m+1h, BurnSlow 6× × 30m+6h, BurnTicket 3× × 2h+1d
    • writer 99.5% (3.6h/30d): аналогично, чуть свободнее
  • ServiceMonitor label release: monitoring — без него kube-prometheus-stack игнорирует SM. Добавили в helper, плюс PodMonitor для CNPG.
  • Tempo retention 48h → 168h (7d), pool tuning, global_overrides — для full-week investigation traces.
  • Loki compactor с retention_enabled=true, ingestion limits — спасает PVC от bloat.

UI:

  • Scope-based grouping в списке справочников: секции PUBLIC → INTERNAL → RESTRICTED + цветные accent-полосы слева у карточек (emerald/amber/rose). Search фильтрует across all sections.
  • Scope accent на detail page: top stripe 4px цветом scope + colored chip в breadcrumb рядом с back-link.
  • Поиск + счётчик активных записей на странице списка справочников (через useDeferredValue + N+1-safe countActiveGroupedByDictionary).
  • INTERNAL словари visibility fix: ORDINIS_AUTH_ROLES_CLAIM теперь читает resource_access.ordinis.roles (Keycloak client-роли), не realm_access.roles (default). Маппинг ordinis:client:internal → INTERNAL уже работал в ScopeContext.
  • revisionHistoryLimit=3 на всех Deployment в чарте — было 49 stale ReplicaSets.
  • Pre-existing TS errors fixedpnpm tsc --noEmit зелёный (5 ошибок: unused imports, missing prop type, $schema field).

Data:

  • Bundle 7 → 40 справочников (cuod v1.2.0). Закрыло раздел item 28 из v0.5.
  • Альтум pull verified — внешний integration работает (item 22 → ).

Production prep (планирование, не deployed):

  • cluster-prod.yaml (3-instance HA с synchronous replication, 90d retention, podAntiAffinity required)
  • scheduled-backup-prod.yaml (daily 02:30 UTC + Weekly Sunday 04:00 UTC)
  • docs/runbooks/postgres-multi-instance-deploy.md — sister runbook
  • docs/runbooks/cnpg-backup-s3-prod.md — Selectel/Yandex S3 setup, IAM policy, ExternalSecret, Prometheus alerts (BackupFailed + WalArchiveStale)
  • k8s/benchmark/k6-read-api.js + k6-job.yaml + README.md — Pareto 80/20 (records.list/dictionaries.list/record.byKey), JWT через ordinis-bench Keycloak SA, smoke 50 RPS → ramp 1k RPS → sustain 5m. Не запускался ещё.

1. Перечень требований

1.1. Реализовано (production-ready на staging)

# Требование Статус Где зафиксировано
1 Каталог НСИ ЦУОД ОДХ — единый источник истины bundle cuod v1.2.0 (40 справочников)
2 Bitemporal модель (валидное-время + транзакционное-время) dictionary_records + valid_from/valid_to + Hibernate Envers
3 JSON Schema валидация записей (Draft-07) ordinis-validation (networknt)
4 i18n поля (RU + EN) с patternProperties x-localized: true extension
5 Уникальные поля (бизнес-ключ + произвольные) x-unique extension + native query enforcement
6 Авто-derive businessKey из data поля x-id-source extension
7 История версий записи + diff /records/{key}/history + UI drawer
8 Schema versioning + breaking-change detection diffSchemas + Save-block + auto major/minor/patch
9 Аудит-лог (кто, когда, что, IP, trace_id) audit_log + AuditLogger + UI /audit + CSV export
10 Kafka outbox (at-least-once гарантия) OutboxPoller + DLQ cap (1000 attempts)
11 Idempotency для мутирующих запросов Idempotency-Key header + sha-256 hash
12 Multi-scope доступ (PUBLIC / INTERNAL / RESTRICTED) DataScope + JWT roles + Kafka per-scope topics
13 Keycloak OIDC (issuer-uri auto-discovery) ordinis-auth + JwtDecoders.fromIssuerLocation + resource_access.ordinis.roles
14 Visual schema builder + CRUD UI (scope-grouped, search, counts) admin-ui (React 19 + TanStack + @nstart/ui)
15 SSO для admin-ui (PKCE Public client) react-oidc-context
16 Bundle import (idempotent seed) CuodBundleImporter startup runner
17 Swagger UI / OpenAPI 3 spec springdoc на /swagger-ui.html
18 40 справочников + 51+ запись на staging bundle v1.2.0 (закрытие item 28 из v0.5)
19 Friendly error responses (409/400/404 вместо 500) GlobalExceptionHandler
20 Метрики Prometheus + actuator/health + percentiles-histogram ordinis_outbox_*, ordinis_kafka_*, http_server_requests_seconds_bucket
21 Тесты 24 backend + 51 frontend + 34 новых = 109 unit-тестов, все зелёные
22 Real consumer integration (Альтум) Pull verified 2026-05-05/06, БFF (FastAPI) подключился
25 Distributed tracing (Tempo) на staging OpenTelemetry → Tempo OTLP gRPC, 7d retention
27 SLO + alerts (Prometheus) 4 burn rate alerts (Google SRE multi-window), 2 Grafana dashboards
34 CI auto-rollback при failure helm --atomic --cleanup-on-fail + pre-step pending-* recovery
35 CNPG operator 1.25.4 (spec.probes API) стабильность под нагрузкой, нет cascade failures
36 Loki compactor + retention enforcement PVC bloat защита

1.2. В работе / required для v1 closure

# Требование Статус Прогресс с v0.5 / Блокер
23 DNS A-record + Let's Encrypt cert на prod 🔴 DNS не проброшен на prod IP, prod hibernated. Нет изменений.
24 ORDINIS_AUTH_REQUIRED=true на prod 🔴 Зависит от готовности SSO admin-ui на prod + Альтум.
26 CNPG backup в S3 + DR rehearsal 🟡 ready Манифесты + runbook готовы (cluster-prod.yaml, scheduled-backup-prod.yaml, cnpg-backup-s3-prod.md). Не запущено: ждёт provider creds (Selectel default) + Vault path.
28 30+ справочников ЦУОД ОДХ (full coverage) Закрыто — bundle 1.2.0 содержит 40.
29 Operator runbook 🟡 70% Добавлены postgres-multi-instance-deploy.md, cnpg-backup-s3-prod.md, k8s/benchmark/README.md. Остаётся: рестарт Kafka StrimziPodSet, разбор DLQ, рестарт outbox-loop.
30 e2e API тесты (testcontainers) 🔴 Только unit. Нет изменений.
31 Component тесты frontend (RTL) 🔴 Только pure-logic. Нет изменений.
32 Bundle code-splitting (~660KB gzip) 🔴 echarts/lottie/@nstart/ui тянут единый chunk. Нет изменений.
33 Vault auto-unseal 🟢 deferred Сознательно skipped по решению owner — приоритет prod-deploy. Mitigation: manual unseal runbook.
37 Multi-instance Postgres prod (HA failover) 🟡 ready cluster-prod.yaml + postgres-multi-instance-deploy.md готовы. Не applied (ждёт prod stand-up).
38 Performance benchmark (1k RPS, p99 < 1s) 🟡 ready k6 setup в k8s/benchmark/. Не запускался — нужно: создать ordinis-bench Keycloak SA → Secret → ConfigMap → kubectl apply -f k6-job.yaml.
39 AlertmanagerConfig per-channel webhooks 🔴 Сейчас 1 webhook (#AltumAlerts) на всё. Нужны URLs для разделения critical / warning / info.

1.3. v2 (governance / data quality / federation)

Не блокирует prod stand-up. Roadmap ~2-3 квартала, без изменений с v0.5:

  • Approval workflow (draft → review → publish), ownership trees, RBAC per-dictionary
  • Lineage / dependency graph
  • Geo first-class (карта ввода/превью, bbox/polygon query)
  • Cross-dictionary FK с cascade rules
  • Data quality rules engine (cross-field constraints, ML-anomaly detection)
  • Federation MDM-кластеров (master/replica)
  • Schema registry (Apicurio/Confluent) для Kafka
  • Webhooks для не-Kafka consumer'ов

2. Нерешённые технические вопросы

2.1. Критические для prod

  1. Multi-instance Postgres failover. Single-node CNPG без replica → SPOF. Манифесты + runbook готовы (см. item 37). Apply при prod cutover.
  2. CNPG backup в external S3. MinIO в кластере = same failure domain. Внешний S3 (Selectel/Yandex) — runbook готов (item 26). RTO < 30m, RPO < 5m по WAL archive interval.
  3. Audience валидация JWT. Сейчас не enforced — принимаем токены от любого клиента в realm nstart. Допускает scope-эскалацию. На prod либо включить audience-check, либо мапить aud=ordinis через Keycloak Audience Mapper.
  4. Outbox poll-interval 1 сек. При 100+ событиях/сек → 86k+ запросов к Postgres/день. Возможно нужен batch tuning или reactive listener вместо poll.

2.2. Архитектурные открытые вопросы

  1. Realtime invalidation для consumer'ов. TTL 5 мин кэша достаточен для v1, но push через Kafka topic ordinis.cuod.events.* уже emit'ится — consumer'ам осталось subscribe.
  2. Геоданные. PostGIS включён, но bitemporal-выборка по геометрии не реализована. Нужно подтверждение от ЦУОД что геопоиск нужен в v1 или v2.
  3. Multi-tenancy. Anchor bundle: cuod. Второй клиент → второй bundle, своя тройка Kafka топиков. Шаринг общих справочников между bundle (например spacecraft для ЦУОД и геопортала) не определён архитектурно.
  4. Object storage / большие справочники. Если справочник вырастет до 100k+ записей, JSONB в одной таблице может стать узким. Партиционирование dictionary_records по dictionary_id — потенциальное решение, требует оценки.

2.3. Operational (выявлено в этой сессии)

  1. Vault Agent Injector flaky под нагрузкой. Restarted 68 раз за месяц, last 4m26s ago. Когда injector down — pods создаются БЕЗ vault-agent sidecar → app пишет cannot open /vault/secrets/postgres → CrashLoop. Воркэраунд: kubectl delete pod для recreate; долгосрочно — увеличить request resources injector + readinessProbe.
  2. CNPG metrics exporter slow queries. Под нагрузкой parallel deploy pg_stat_replication парсится 9-22 секунды → scrape timeout (default 10s) → up=0 для postgres. Mitigation: dashboard panel переключён на kube_pod_status_ready (стабильно). Корень — нагрузка на single-node под docker builds + Spring Boot startups + cnpg exporter одновременно.
  3. kube-apiserver TLS handshake timeouts под нагрузкой (load avg 50+). Транзиентно блокирует kubectl. Mitigation: подождать 10-30s, retry. Долгосрочно — multi-node cluster.
  4. Vault-agent-init backoff. ~3 минут при transient Vault unavailability — слишком долго. Тuning либо переход на CSI Driver.
  5. JDK 25 + Mockito inline несовместим. Перешли на subclass mock-maker. При апгрейде до Java 26 проверить.
  6. CI path-filter. Был костыль. Сейчас все docker jobs запускаются всегда (~30s extra на layer-cache). Можно вернуться к smart-filter когда GitLab variable interpolation в trigger jobs будет надёжной.

2.4. Compliance / правовые

  1. Audit retention policy. audit_log пишется бессрочно. По регламенту ЦУОД нужно определить срок хранения (5/10 лет) + автоматическую архивацию. Блокер: legal deadline.
  2. Personal data handling. В audit_log пишется user_id (Keycloak sub) + IP. ФЗ-152 — нужна оценка / DPA если в realm будут не-сотрудники.
  3. Электронная подпись изменений. Если потребуется юридически значимая фиксация (КЭП оператора при close записи) — отдельный модуль, не реализован.

3. Наличие технической документации

3.1. Создана и актуальна

Документ Назначение Расположение
API integration guide Гайд для интеграторов docs/tech/api-integration.md
Альтум integration ТЗ Согласованный план «Заказ съёмки» docs/integration/altum-imaging-order.md
OpenAPI 3 spec (Swagger) Машиночитаемая спецификация https://ordinis.k8s.265.nstart.cloud/swagger-ui.html + /v3/api-docs
Bundle manifest + schemas JSON Schema каждого справочника ordinis-cuod-bundle/src/main/resources/bundles/cuod/
Postgres multi-instance deploy 🆕 Runbook апгрейда staging single → prod 3-instance HA docs/runbooks/postgres-multi-instance-deploy.md
CNPG backup → external S3 🆕 Selectel/Yandex провижининг, IAM policy, ExternalSecret, Prometheus alerts docs/runbooks/cnpg-backup-s3-prod.md
k6 benchmark 🆕 Pareto 80/20 setup, preflight, regression tracking k8s/benchmark/README.md
CI/Helm values Параметризация per-env ordinis-infra/charts/ordinis/{values,values-staging,values-prod}.yaml

3.2. Отсутствует / требует написания

Документ Приоритет Прогресс
Operator runbook (full) 🔴 high 70%: добавлены postgres-multi-instance + cnpg-backup. Остались: Kafka StrimziPodSet рестарт, DLQ-разбор, outbox-loop рестарт, vault unseal, поднятие hibernated prod.
DR plan 🔴 high → 🟡 partial Backup runbook готов с RTO/RPO targets. Quarter restore drill в operations calendar — не настроен.
Архитектурный обзор (HLA) 🟡 medium Single-pager не написан.
User docs / справка оператора 🟡 medium Не написана.
Threat model + security review 🟡 medium OWASP-чеклист не пройден.
CHANGELOG.md 🟢 low git log + commit messages.

Резюме

Прогресс v0.5 → v0.9:

  • 27 из 39 требований v1 закрыто (было 21/33). Прирост: items 18 (40 справочников), 22 (Альтум), 25 (tracing), 27 (SLO/alerts), 28 (full coverage), 35 (CNPG), 36 (Loki) + 3 новых item (CI auto-rollback, multi-instance ready, perf benchmark ready).
  • Архитектурный фундамент остаётся прочным, добавлены защиты от cascade failures.
  • Главный риск: prod stand-up зависит от DNS/cert (item 23) + multi-instance Postgres apply (item 37) + S3 creds (item 26). Все имеют готовые runbooks.
  • Документация: prod-deploy artifacts готовы (postgres HA + S3 backup + k6 benchmark). Остаётся operator runbook completion + threat model.

Прогноз до prod-ready (v1): ~1-2 недели при текущем темпе. Блокеры — прежде всего внешние (DNS, S3 provider creds, ЦУОД real data). Inside-team задачи — 3-5 дней работы на code-splitting + e2e tests + AlertmanagerConfig.

Что сегодня закрыло прошлые риски:

  • Хроническая нестабильность под нагрузкой → CNPG 1.25 + config-server probes + CI atomic.
  • Observability vacuum → 4 burn rate alerts + 2 dashboards + 7d Tempo.
  • INTERNAL словари невидимые → rolesClaim fix.
  • Stuck helm releases → CI pre-step recovery + migrate Job hook-failed.