Evening update к v1.0: - SPOF на staging registry CLOSED (postgis-barman mirrored в repo.nstart.cloud:18.3-3.6.2-2) - Monitoring artifacts ready (k6 prod variant, synthetic smoke + CronJob, 2 alerts) - KafkaTopics READY=False janitorial fix (script ready) - CI build job на 401 known-issue (manual fallback) Section 0.1 добавлена с подробным postmortem image arch incident: 1. arm64 image из mac M-series попал в registry без --platform 2. kubelet cache на node удерживал stale arm64 layer под тем же тегом 3. Pod stuck Terminating 14 мин (CNPG gracePeriod=1800s) Lessons закреплены в docker/postgis-barman/README.md (anti-footgun).
12 KiB
Состояние разработки НСИ (Ordinis ЦУОД ОДХ)
Версия: v1.0.1 (post-cutover hardening: SPOF closed, monitoring stack ready, image arch incident resolved) Дата: 2026-05-07 (evening update) Owner: zimin.an@nstart.space Окружения:
- prod (192.168.100.142 / cluster.142, ✅ LIVE с Альтум traffic, blue/green cutover завершён)
- staging (192.168.100.161 / cluster.265, ✅ STABLE — теперь dev environment, не consumer-facing) Предыдущий snapshot: 2026-05-06-state.md (v0.9, prod hibernated)
0. Что изменилось с v0.9
Production stand-up на cluster.142 (Phase 1-5 done)
Phase 1 — Postgres prod stand-up:
- Discovery: cluster.142 НЕ hibernated (state.md устарел — k8s API v1.33.7 жив, ordinis-prod ns с Helm rev 17 deployed 4 дня назад, всё scaled to 0)
- CNPG cluster
ordinis-postgreshealthy, 50Gi local-path PVC - Custom image
postgis-barman:18.3-3.6.2-1(PostGIS 18 + barman-cli-cloud) для CNPG backups — официальныйcnpg/postgis:18-standard-trixieНЕ имеет barman tools - barmanObjectStore →
s3://altum-backups/ordinis-postgresчерезgeoportal-prod-minio:9000(re-use bucket с prefix isolation) - ScheduledBackup: daily 02:30 UTC + weekly Sun 04:00 UTC
- Initial backup landed, PITR window started
Phase 2 — Vault re-init:
- Original Vault sealed после restart 17h, unseal key потерян
- Re-init с wipe
data-vault-0PVC → init -key-shares=1 -key-threshold=1 - Setup playbook:
kubernetesauth, policyordinis-app, 4 roles (per-SA binding) - 6 secret paths populated из existing k8s secrets (CNPG postgres, Strimzi KafkaUsers, Redis)
- Scripts:
ordinis-infra/scripts/vault/{setup,populate}.sh+ README
Phase 3 — Kafka cluster recovery:
ordinis-kafkaStrimzi cluster CR не было в namespace (только orphaned KafkaUsers + 1 service)- Apply
ordinis-infra/k8s/kafka/cluster-prod.yaml(KafkaNodePool dual + Kafka KRaft + 3 KafkaTopics) - 1 broker, single-instance, SCRAM-SHA-512 auth, 20Gi local-path PVC
- Strimzi operator уже watches ordinis-prod (STRIMZI_NAMESPACE=kafka-test,ordinis-prod)
- Cluster Ready через 90s после apply
Phase 4 — App deploy:
config-server,ordinis-redis-0scaled up (были 0/0)- Helm upgrade
ordinisrev 17 → 22 наimage.tag=main-4252ad93 - Replicas bumped: writer 1→2, readApi 1→2 (HA для consumer-facing)
- 9 pods Running, /actuator/health → 200, /api/v1/dictionaries → 200 + 40 dicts seeded
Phase 5 — Альтум cutover:
- Blue/green phased shift через Альтум config (10% → 50% → 100%)
- staging-as-prod decommissioned для production traffic
- staging cluster.265 теперь чисто dev environment
0.1. Post-cutover hardening (evening 2026-05-07)
Артефакты добавлены:
k8s/benchmark/k6-job-prod.yaml— load test для prod (Service port :80 fix)k8s/benchmark/synthetic-smoke-prod.yaml— one-shot smoke (auth + 3 endpoints, exit code = pass/fail)k8s/benchmark/synthetic-smoke-cronjob-prod.yaml— continuous monitoring (*/30 min)k8s/kafka/topic-orphan-fix.sh— janitorial script для KafkaTopic READY=False- prometheus-rules group
ordinis.synthetic(2 alerts: SmokeFailed/Stale, conditional наprometheusRule.synthetic.enabled) .gitlab-ci.ymljobbuild-postgis-barman(kaniko + REGISTRY_USER/REGISTRY auth, currently 401 — known issue, manual push fallback)
Инцидент: postgis-barman image swap (08:30-22:00 MSK):
- Custom image
:18.3-3.6.2-1запушен вrepo.nstart.cloudс mac M-series без--platform linux/amd64→ arm64 layers попали в registry - Catalog patched на repo.nstart.cloud → CNPG re-roll →
exec format error→ CrashLoopBackOff - Rollback catalog на staging URL → recovered
- Re-push amd64 → но kubelet cache на srvaltum01 удерживал arm64 layer под тем же тегом → retry catalog swap снова exec format error
- Bump rev
:1→:2→ kubelet вынужден pull → cache miss → новый amd64 layer → success - Pod stuck Terminating ~14 мин (CNPG
terminationGracePeriodSeconds: 1800) →kubectl delete pod --force --grace-period=0для recovery acceleration
Lessons learned (закреплены в docker/postgis-barman/README.md):
docker buildx --platform linux/amd64 --pushобязательно когда build с M-series mac- Bump rev tag при cache invalidation needs (тот же тег + новые слои = kubelet skip)
terminationGracePeriodSeconds=1800означает что graceful pod delete может висеть до 30 минут- Verify image arch через registry API (
config.architecture) ДО catalog swap
Production verify (post-incident):
- Cluster phase: "Cluster in healthy state"
- status.image:
repo.nstart.cloud/terravault/postgis-barman:18.3-3.6.2-2 - imageID digest:
sha256:1b4b074d...(verified amd64 manifest) - All 11 pods Running, postgres restartCount=0, lastSuccessfulBackup preserved
SPOF на staging registry — closed. Если registry.k8s.265.nstart.local упадёт, prod не пострадает.
Что закоммичено
ordinis-infra (4 commits сегодня):
cluster-prod.yamlaligned с geoportal pattern (local-path, internal MinIO, single-instance) — было overcomplicated с Longhorn/Yandex S3values-prod.yaml: writer=2, readApi=2, hostordinis.k8s.264.nstart.cloud,selfsigned-cluster-issuerprometheus-rules.yaml: 4 CNPG HA alerts (conditionalcnpgHa.enabledflag, off для single-instance)k8s/kafka/cluster-prod.yaml(NEW): Kafka CR + NodePool + 3 Topics для recoveryscripts/vault/{setup,populate}.sh + README.md(NEW): playbook bootstrap
ordinis (8 commits сегодня — задеплоены в prod):
c7a917dtest(webhook): unit-test WebhookTestPingService — 7 кейсовac93151test(webhook): re-enable WebhookE2ETest через no-op scheduler + manual ticks92507abfeat(audit): URL persistence фильтров + chip-list активных5941207feat(records): bulk close — multi-select toolbar + per-record транзакция640a633test(records): unit-test BulkRecordService — 10 кейсов66a4f13test(geo): e2e auto-derive geometry из data.lat/lon — 6 кейсов4252ad9feat(records): bulk export CSV выбранных + pagination-
- 23 новых теста (7 webhook + 10 bulk + 6 e2e)
1. Перечень требований
1.1. Реализовано (production-ready, LIVE на prod cluster.142)
Та же таблица что v0.9 + новые пункты:
| # | Требование | Статус |
|---|---|---|
| 1-32 | (см. v0.9, всё уже было ready) | ✅ |
| 33 | Production deployment на real cluster (cluster.142) | ✅ NEW |
| 34 | Bulk close записей с multi-select UI | ✅ NEW |
| 35 | Bulk CSV export выбранных записей | ✅ NEW |
| 36 | Audit log URL-persistent filters + chip-list | ✅ NEW |
| 37 | Pagination для records list (50/page client-side) | ✅ NEW |
| 38 | WebhookE2ETest stable на CI (no-op scheduler + manual ticks) | ✅ NEW |
| 39 | Auto-derive POINT geometry из data.lat/lon (3 пути backend + Liquibase backfill 0015) | ✅ |
| 40 | CNPG continuous WAL archive + scheduled backups в external S3 | ✅ NEW |
1.2. В процессе / nice-to-have
- KafkaTopics CR показывают
READY=False. Reason: "Decreasing partitions not supported" —cluster-prod.yamlуказал partitions=3, broker имеет больше (orphan от старого setup). Fix:k8s/kafka/topic-orphan-fix.shannotate'итstrimzi.io/managed=false(broker continues, alerts замолкают). Apply вручную. - Vault SASL passwords могут быть stale если Strimzi user-operator regenerated после Kafka cluster recreate — re-run
scripts/vault/populate.sh ✅ CLOSED 2026-05-07 evening: image mirrored вpostgis-barmanimage SPOF на staging registryrepo.nstart.cloud/terravault/postgis-barman:18.3-3.6.2-2, prod cluster.142 pull'ит оттуда. См. секцию 0.1 ниже про инцидент.- k6 benchmark на prod не прогнан (smoke 50 RPS / ramp 1k / sustain 5m готов в
k8s/benchmark/k6-job-prod.yaml) - Synthetic smoke + alerts ready (
k8s/benchmark/synthetic-smoke-cronjob-prod.yaml+ prometheus-rules groupordinis.synthetic) — apply manually + helm upgrade с values-prod - Real ЦУОД data integration (отдельный epic после prod soak)
1.3. Roadmap (отложено)
- Approval workflow v2 (draft → review → publish) — design doc сохранён в
~/.gstack/projects/claude/zimin-main-design-approval-workflow-v2-20260507-121632.md. Defer'нут до prod LIVE + observed compliance needs. Re-evaluate через 1-2 месяца после Альтум stable. - True HA Postgres (3-instance + Longhorn + external Yandex S3) — отдельный future epic для multi-node cluster, не для cluster.142
- Lineage / dependency graph между dictionaries
- Cross-dictionary FK с cascade rules
- Data quality rules engine
- Federation MDM-кластеров (master/replica)
- Schema registry (Apicurio/Confluent) для Kafka
2. Архитектура (без изменений с v0.9, кроме prod)
┌─────────────────────┐ ┌──────────────────────┐ ┌─────────────┐
│ Альтум (real users) │ ──→│ ordinis.k8s.264 │ ──→│ Postgres │
│ │ │ (cluster.142 prod) │ │ (CNPG 1× │
│ │ │ • read-api ×2 │ │ + WAL S3 │
│ │ │ • app ×2 (writer) │ │ + daily │
│ │ │ • projection-writer │ │ backups) │
│ │ │ • admin-ui ×1 │ │ │
│ │ │ • Kafka 1× + 3 topics │ │
│ │ │ • Redis 1× │ │ │
│ │ │ • Vault 1× │ │ │
│ │ └──────────────────────┘ └─────────────┘
└─────────────────────┘
┌──────────────────────┐
│ staging cluster.265 │
│ (DEV ENVIRONMENT) │
└──────────────────────┘
3. Operational notes
Backup verify: barman-cloud-backup-list --endpoint-url http://geoportal-prod-minio.geoportal-production:9000 --cloud-provider aws-s3 s3://altum-backups/ordinis-postgres ordinis-postgres
Vault sealed после restart: kubectl exec -n vault vault-0 -- vault operator unseal <KEY> (key хранится у operator'а в 1Password / GitLab vars).
Rollback план: DNS TTL 60s — при критичных issues Альтум переключает обратно на staging через config flag.
Email: zimin.an@nstart.space (НЕ gmail)