snapshot restore из kreknin:daily-2026-05-25 за 46 сек (epz=820604, products=105922, artmone=2621, counts == source). RCA: 5 индексов (включая system .tasks) удалены через ES API DELETE _all за 1 сек на 2026-05-26 10:21 UTC — 1ч 11мин после создания bookva-es в cutover-prep. Каноничный endpoint elasticsearch.kzntsv.site попал под команду которая предназначалась bookva-es:9200 (internal-only, без traefik route). Caller identity unrecoverable: ES audit = X-Pack платный, traefik accessLog был выключен, Portainer CE без audit. Preventive controls applied + verified: 1. ES env action.destructive_requires_name=true (stack 33) — DELETE _all и wildcard теперь 400 BadRequest; by-name DELETE работает (нужно для reindex). Pattern удаления что случился физически невозможен. 2. Traefik JSON accessLog в /letsencrypt/access.log — будущие DELETE оставят forensic след с IP/user/method/path. Wiki concept: .wiki/concepts/es-destructive-delete-incident-2026-05-26.md с recovery runbook + preventive controls + cross-refs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.5 KiB
restore-elasticsearch-indices-books-vds
Goal
Восстановить 3 ES индекса (epz, products, artmone) на canonical endpoint elasticsearch.kzntsv.site (books VDS, Portainer stack 33). Сейчас target ES пустой → весь поиск в books-app (slovo) сломан (404 index_not_found_exception для epz и products).
Симптомы (зафиксировано 2026-05-28 ~15:50 MSK)
bookva.kzntsv.site/api/products/search?q=...→ 500no such index [products].bookva.kzntsv.site/api/epz/search?q=...→ 500no such index [epz].books-api+books-webлоги (stderr) — десяткиResponseError: index_not_found_exceptionза час.- User-facing impact: страница
/epz/searchи поиск товаров в slovo UI не работают.
Closure note (2026-05-28 ~20:10 MSK)
🟢 Восстановлено за 46 сек через snapshot restore + два preventive фикса в один заход.
Restore
POST /_snapshot/kreknin/daily-2026-05-25/_restore?wait_for_completion=true
{"indices":"epz,products,artmone","include_global_state":false,"include_aliases":true,"index_settings":{"number_of_replicas":0}}
- artmone: 2621 docs ✓ (source: 2621)
- epz: 820604 docs ✓ (source: 820604)
- products: 105922 docs ✓ (source: 105922)
- cluster: green
- 3 consumers (books-api/task-runner/job-scheduler) рестартованы — 0
index_not_found_exceptionв логах за 5 мин - public smoke
bookva.kzntsv.site/api/{epz,products}/search→ 401 от auth middleware (поиск-pipeline жив, до wipe было 500)
Ключевая удача: в исходной задаче было записано «pre-migration-2026-05-25 содержит только read_me placeholder — не годится для восстановления» — это правда, но это был другой snapshot (он был до reindex'а). А daily-2026-05-25 (сделан кроном books-vds-backup-daily-kreknin в 09:51 UTC через 2ч после reindex'а) содержал full corpus — [read_me, products, epz, artmone, .tasks], 25 sec duration, 5 шардов SUCCESS. Snapshot pipeline (стояший с 25.05) автоматически создал safety net.
Root cause (forensics из ES container log)
Container elasticsearch restart_count=0, created=2026-05-25T07:51:38 — никогда не рестартовал, volume bind не задет, индексы существовали и были удалены через ES API:
2026-05-26 10:21:43.526 UTC [read_me/IfM2V1pTQk-...] deleting index
2026-05-26 10:21:44.075 UTC [artmone/xQdFWkafSKy-...] deleting index
2026-05-26 10:21:44.257 UTC [epz/AV8inT4YS-e-...] deleting index
2026-05-26 10:21:44.450 UTC [.tasks/EbutxIzdTPSq-...] deleting index
2026-05-26 10:21:44.576 UTC [products/6VRXsuP5QV-...] deleting index
5 индексов (включая system .tasks) удалены за 1 секунду = DELETE _all / DELETE * / Kibana DevTools «delete index».
Timeline:
- 2026-05-25 09:51 UTC — daily snapshot SUCCESS [read_me, products, epz, artmone, .tasks] (full data)
- между ~10:00 и 26.05 03:02 UTC — Wipe #1 (snapshot 26.05 содержит только [read_me])
- 2026-05-26 05:49–06:44 UTC — re-reindex (artmone+epz+products, full counts восстановлены)
- 2026-05-26 09:10:18 UTC —
bookva-escontainer created (cutover-prep Step 2) - 2026-05-26 10:21:43 UTC — Wipe #2 (1ч 11мин после создания bookva-es)
bookva-es использует named volume bookva-es-data, БЕЗ traefik labels (internal-only). Не задел canonical через mount или routing. Best guess: оператор в момент cutover-prep хотел очистить новый bookva-es:9200, но команда ушла на elasticsearch.kzntsv.site (canonical, slovo). Bookva-es internal-only → DELETE с воркстейшна туда не дойти; canonical через traefik basicAuth → доходит.
Caller identity unrecoverable: ES audit log = X-Pack платная фича (нет на free 7.10). Traefik accessLog был отключён (нет секции accessLog: в traefik.yml). Portainer audit = CE без enterprise. Только timestamp + DELETE fact в ES log.
Preventive fixes (applied 2026-05-28 ~20:10 MSK)
Fix #1: action.destructive_requires_name=true на stack 33 (env var добавлен через Portainer PUT API). Verified:
DELETE /_all→ 400 «Wildcard expressions or all indices are not allowed»DELETE /ep*→ 400 sameDELETE /probe-canary(by exact name) → 200 (легитимные операции работают)
Pattern удаления который случился (5 индексов в 1 сек) теперь физически невозможен.
Fix #2: traefik accessLog в JSON формат, /letsencrypt/access.log (bind-mounted, persistent). Backup traefik.yml.bak-pre-accesslog-2026-05-28 рядом. Verified entries содержат RequestMethod, RequestHost, RequestPath, ClientHost, ClientUsername, DownstreamStatus, TLSCipher. Будущие DELETE запросы оставят след — даже если кто-то снова попадёт не на тот endpoint, retrospective forensics возможен.
Artifacts
.scratch/stack33-put-2026-05-28.json— Portainer PUT payload (новый env + сохранёнreindex.remote.whitelist)..wiki/concepts/es-destructive-delete-incident-2026-05-26.md— incident concept (написан в эту сессию).- ES container env post-fix:
reindex.remote.whitelist=elasticold.kzntsv.site:443+action.destructive_requires_name=true. - Traefik static config:
accessLog: { filePath: /letsencrypt/access.log, format: json, bufferingSize: 100 }.
Follow-ups (не блокеры)
- Logrotate на
access.log— при ~1 req/sec будет ~30–50 MB/day. Disk free 63G, ressedimption через 1–2 года максимум — но добавить logrotate стоит. Отдельная micro-task. - Переезд access.log из
/letsencrypt/в отдельный bind (/usr/docker/traefik/logs/) — косметика, текущее место persistent и работает. - Pass entry update — комментарий в
books-vds/full-envустарел: «NOT Portainer-managed (legacy SSH-compose): elasticsearch, mongo, minio, books-db…». Migration 25.05 (books-vds-stacks-to-portainer) уже мигрировала их. Поправить при следующем pass-edit.
Текущее состояние ES endpoint'ов
Source (Windows host, rollback — не выключен): https://elasticold.kzntsv.site/
index health docs.count store.size
artmone yellow 2621 1.4mb
epz yellow 820604 699.7mb
products yellow 105922 26.6mb
Target (books VDS, Portainer stack 33, canonical): https://elasticsearch.kzntsv.site/ — restored 2026-05-28, counts == source, green.
Branch
n/a (admin ops)
Status
closed 2026-05-28