81c959b7e2
Закрыло за сутки: bundle 7→40 справочников, Альтум pull verified, Tempo+Loki+Grafana+SLO burn rate alerts (Google SRE multi-window), UI scope grouping + accents, INTERNAL словари visibility (rolesClaim), CNPG 1.24→1.25.4 + spec.probes, CI helm --atomic + auto-rollback, config-server startupProbe, 75→109 unit тестов. Готовы к prod (manifests + runbooks): multi-instance Postgres HA, external S3 backup, k6 benchmark setup. Block — DNS/cert prod, S3 provider creds, real ЦУОД data. Прогресс: 21/33 (v0.5) → 27/39 требований (v0.9). 1-2 недели до v1. v0.5 файл оставлен как историческая запись.
20 KiB
20 KiB
Состояние разработки НСИ (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.probesAPI → выставили 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-safecountActiveGroupedByDictionary). - 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 fixed —
pnpm tsc --noEmitзелёный (5 ошибок: unused imports, missing prop type,$schemafield).
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 runbookdocs/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-benchKeycloak 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
- Multi-instance Postgres failover. Single-node CNPG без replica → SPOF. Манифесты + runbook готовы (см. item 37). Apply при prod cutover.
- CNPG backup в external S3. MinIO в кластере = same failure domain. Внешний S3 (Selectel/Yandex) — runbook готов (item 26). RTO < 30m, RPO < 5m по WAL archive interval.
- Audience валидация JWT. Сейчас не enforced — принимаем токены от любого клиента в realm
nstart. Допускает scope-эскалацию. На prod либо включить audience-check, либо мапитьaud=ordinisчерез Keycloak Audience Mapper. - Outbox poll-interval 1 сек. При 100+ событиях/сек → 86k+ запросов к Postgres/день. Возможно нужен batch tuning или reactive listener вместо poll.
2.2. Архитектурные открытые вопросы
- Realtime invalidation для consumer'ов. TTL 5 мин кэша достаточен для v1, но push через Kafka topic
ordinis.cuod.events.*уже emit'ится — consumer'ам осталось subscribe. - Геоданные. PostGIS включён, но bitemporal-выборка по геометрии не реализована. Нужно подтверждение от ЦУОД что геопоиск нужен в v1 или v2.
- Multi-tenancy. Anchor
bundle: cuod. Второй клиент → второй bundle, своя тройка Kafka топиков. Шаринг общих справочников между bundle (напримерspacecraftдля ЦУОД и геопортала) не определён архитектурно. - Object storage / большие справочники. Если справочник вырастет до 100k+ записей, JSONB в одной таблице может стать узким. Партиционирование
dictionary_recordsпоdictionary_id— потенциальное решение, требует оценки.
2.3. Operational (выявлено в этой сессии)
- 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. - 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 одновременно. - kube-apiserver TLS handshake timeouts под нагрузкой (load avg 50+). Транзиентно блокирует kubectl. Mitigation: подождать 10-30s, retry. Долгосрочно — multi-node cluster.
- Vault-agent-init backoff. ~3 минут при transient Vault unavailability — слишком долго. Тuning либо переход на CSI Driver.
- JDK 25 + Mockito inline несовместим. Перешли на subclass mock-maker. При апгрейде до Java 26 проверить.
- CI path-filter. Был костыль. Сейчас все docker jobs запускаются всегда (~30s extra на layer-cache). Можно вернуться к smart-filter когда GitLab variable interpolation в trigger jobs будет надёжной.
2.4. Compliance / правовые
- Audit retention policy.
audit_logпишется бессрочно. По регламенту ЦУОД нужно определить срок хранения (5/10 лет) + автоматическую архивацию. Блокер: legal deadline. - Personal data handling. В audit_log пишется
user_id(Keycloak sub) + IP. ФЗ-152 — нужна оценка / DPA если в realm будут не-сотрудники. - Электронная подпись изменений. Если потребуется юридически значимая фиксация (КЭП оператора при 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.