Files
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

139 lines
6.5 KiB
Markdown
Raw Permalink 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.
# Runbook: Vault unseal (staging / prod)
**Когда использовать:** Vault на кластере залип в `Sealed=true` после reboot
ноды или manual seal. Признаки:
- Spring Boot pod'ы (`ordinis-app`, `ordinis-read-api`, `ordinis-projection-writer`)
стоят в `Init:0/1`.
- В логах `vault-agent-init` контейнера: `Vault is sealed`, error 503 на
`auth/kubernetes/login`.
- `kubectl exec -n config vault-0 -- vault status``Sealed: true`.
**Auto-unseal сейчас НЕ настроен** (v1 closure backlog: Transit secret engine
с master Vault либо Yandex Cloud KMS). До миграции — ручная процедура ниже.
---
## 1. Где взять unseal-ключи
Ключи Shamir, threshold **3 из 5**. Хранение:
- **Corporate 1Password** — vault `Ordinis НСИ ЦУОД`:
- `vault-staging-unseal-keys` — для cluster.265 (192.168.100.161)
- `vault-prod-unseal-keys` — для cluster.142 (192.168.100.142)
- **Резерв** (cold storage) — у DevOps lead в KeePass.
Доступ к 1Password vault — у списка инженеров с on-call. При расширении —
добавлять через 1Password admin. Никогда не пересылать ключи в Slack /
Telegram / email.
> ⚠️ **При утере 5 из 5 ключей:** Vault инициализируется заново → ВСЕ
> secrets потеряны (postgres passwords, Kafka SASL, Keycloak signing keys
> в K8s secrets — отдельно). Plan: получить root token + replay `setup.sh` +
> заново положить secrets из 1Password (Postgres backup + Vault: писать
> только в зеркало). Делать только с письменным разрешением tech lead.
## 2. Быстрый unseal (script)
```bash
cd ~/code/ordinis-infra/k8s/vault
# staging
./unseal.sh staging "<KEY_1>" "<KEY_2>" "<KEY_3>"
# prod
./unseal.sh prod "<KEY_1>" "<KEY_2>" "<KEY_3>"
```
Скрипт делает:
1. Pull kubeconfig с ноды (если ещё не скачан).
2. Проверяет `vault status` — если уже unsealed, выходит.
3. Применяет 3 ключа последовательно через `vault operator unseal`.
4. Проверяет `Sealed: false`.
5. Force-deletes Spring Boot pod'ы — vault-agent-init иначе ждёт backoff
до 3+ минут на следующую попытку auth.
После — `kubectl get pods -n ordinis-{env}` показывает `2/2 Running`
через ~2 минуты.
## 3. Ручной unseal (если script недоступен)
```bash
# 1. Получить kubeconfig
ssh root@192.168.100.161 "cat /etc/kubernetes/admin.conf" > /tmp/kc-161
sed -i '' 's|server: https://[^:]*:|server: https://192.168.100.161:|' /tmp/kc-161
export KUBECONFIG=/tmp/kc-161
# 2. Проверить статус
kubectl exec -n config vault-0 -- vault status
# 3. Применить 3 ключа (по одному)
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_1>'
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_2>'
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_3>'
# 4. Убедиться Sealed: false
kubectl exec -n config vault-0 -- vault status
# 5. Перезапустить Spring Boot pod'ы
kubectl delete pod -n ordinis-staging \
-l 'app.kubernetes.io/name in (ordinis-app,ordinis-read-api,ordinis-projection-writer)' \
--grace-period=0 --force
# 6. Проверить
kubectl get pods -n ordinis-staging
```
## 4. Подтверждение что всё работает
```bash
# read-api
curl -sk --resolve ordinis.k8s.265.nstart.cloud:443:192.168.100.161 \
https://ordinis.k8s.265.nstart.cloud/api/v1/spacecraft/records?lang=ru-RU \
| jq 'length'
# → должно вернуть число > 0
# admin
curl -sk -o /dev/null -w "%{http_code}\n" \
--resolve ordinis.k8s.265.nstart.cloud:443:192.168.100.161 \
"https://ordinis.k8s.265.nstart.cloud/api/v1/admin/audit?page=0&size=5"
# → 200
```
## 5. Эскалация / проблемы
| Симптом | Что делать |
|------------------------------------------------------|--|
| `vault status``connection refused` | Pod `vault-0` не Running. `kubectl get pods -n config` + `kubectl describe`. |
| `unseal` принимает ключ но Sealed остаётся true | Проверить count `Unseal Progress` — может нужен 4-й ключ если threshold увеличили. |
| Все 3 ключа отвергает (`invalid key`) | Whitespace / обрезанные символы. Скопировать заново из 1Password (полное поле). |
| Pod'ы Spring Boot не поднимаются после unseal | `kubectl logs <pod> -c vault-agent-init` — могут быть policy/role миграции после изменений. |
| Consul backend (Vault использует Consul) недоступен | `kubectl get pods -n config consul-server-0` + `kubectl logs`. |
При 30+ минут downtime → эскалация tech lead → решение о переносе
prod-нагрузки на запасной кластер либо публичная коммуникация в SLA-канал.
## 6. Связанные документы
- Установка Vault с нуля: `ordinis-infra/k8s/vault/setup.sh`
- Архитектура auth: `ordinis-auth/.../{ScopeContext,SecurityConfig}.java`
- Список секретов в Vault: `secret/ordinis/cuod/` (postgres, kafka, redis)
## 7. Переход к auto-unseal (TODO для v1 closure)
Когда будет master Vault либо Yandex Cloud KMS:
1. Добавить `seal "transit"` либо `seal "yandexcloudkms"` stanza в vault config
ConfigMap (`config/vault-config`).
2. Запустить migration:
```
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_1
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_2
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_3
```
3. После миграции существующие Shamir-keys больше не работают — выдаются
Recovery Keys (5 шт, тот же threshold 3) для emergency rekey/operator-init
операций.
4. Обновить этот runbook: основной случай — auto-unseal, ручной unseal
только при downtime master Vault / KMS.