Files
admin/.wiki/concepts/future-resilient-architecture-goals.md
vitya e2338b6fdd wiki(lint): close all 10 lint issues + delete windows-host backup task
- deleted .tasks/windows-host-fallback-backup-daily.md (MSSQL removed on decommission 2026-06-08)
- recovery-architecture-snapshot.md: marked ИСТОРИЧЕСКАЯ ЗАПИСЬ; removed 3 broken [[wiki-links]] (cms-server-port-leak-fix, cms-admin-assets-root-folder-seed, webconfig-password-xml-escape)
- snolla-recovery-vm.md: marked УДАЛЕНА 2026-06-08
- ruvds-iis-host.md: struck 2 resolved risks (imgproxy SPOF, LE renewal); cross-ref winacme
- future-resilient-architecture-goals.md: dead task link → plain text
- mssql-on-vds.md: frontmatter fix; Backup TODO section replaced with implemented block (Express COPY_ONLY, sqlcmd, bind-mount pattern)
- vds-kzntsv.md: added MSSQL row to software stack table
- log.md: update entries for lint + mssql backup implementation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 08:18:19 +03:00

260 lines
21 KiB
Markdown
Raw 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.
---
title: Future Resilient Architecture — roadmap
type: concept
tags: [planning, architecture, resilience, roadmap]
sources: [../sources/nas-recovery-session-2026-05-18.md]
updated: 2026-05-21
---
# Future Resilient Architecture
Roadmap "выстроить отказоустойчивую архитектуру чтобы инциденты типа 2026-05-18 не повторялись". Документ **живой** — переоценивается после каждого incident'а и major-migration'а.
Структура:
- **§Workshop pass 1 (2026-05-21)** — текущий зафиксированный план, decisions per service. Это **canonical** часть документа.
- **§Pre-workshop placeholder (2026-05-19)** — изначальные наброски без user-confirmation, оставлены для history. Не source of truth.
## Workshop pass 1 — 2026-05-21
Roadmap expanded from placeholder to concrete decisions + impl-tasks. Workshop session с user (vitya) 2026-05-21. Каждое decision ниже = user-confirmed, не unilateral recommendation. Trace разговора зафиксирован в `[resilience-roadmap-design]` close-note (см. `.tasks/STATUS.md`).
### Per-service RTO/RPO matrix
Globальные RTO/RPO разнесены **per service** — cost-of-downtime у разных сервисов разный. Числа = target, не факт. Откалибруются после first incident в новой архитектуре.
| Сервис | RTO (max downtime) | RPO (max data loss) | Rationale |
|---|---|---|---|
| **CMS прод-сайты** (snolla/labtools/pilorama98/tandemmebel/emspb/kupimknigi/maljarka — 8 hosts) | **4 ч** | **1 ч** | клиентский трафик = деньги; больше 4ч → звонки клиентов |
| **CMS DB (MSSQL)** | **4 ч** | **1 ч** | orders + контент; час правок переживёшь, день — нет |
| **CMS content** (IIS files `C:\sites\*` + MinIO) | **4 ч** | **24 ч** | картинки/asset'ы меняются редко |
| **OpenWRT router** | **1 ч** | n/a | сетевой SPOF — без него весь public IP мёртв, tied к CMS |
| **Traefik** (на windows-host) | **4 ч** | n/a | живёт на том же host'е что CMS, RTO одинаковый |
| **Gitea** | **8 ч** | **24 ч** | один пользователь, полдня без push переживёшь |
| **Verdaccio** | **8 ч** | **24 ч** | без деплоев фронтов полдня — ок |
| **Registry** | **24 ч** | **n/a** (rebuild) | "новых наделаю" decision при VDS bootstrap, см. [[../sources/vds-kzntsv-bootstrap-2026-05-20]] §3.3 |
| **Shared DB-park** (PG/Maria/Mongo/Redis на VDS) | **8 ч** | **24 ч** | dev/staging, не прод-трафик |
| **ntfy** | **4 ч** | n/a | алерт-канал; без него слепой но не сломанный |
**Decisions зафиксированы (что отвергнуто):**
- **99.99% uptime / continuous replication / hot-standby для CMS** — отвергнуто как непропорционально дорогое для one-person infra.
- **MSSQL Always-On AG** — отвергнуто (требует Enterprise license), log shipping каждые 60 мин достаточно для RPO 1ч.
- **MSSQL → PostgreSQL миграция** — оставлено как long-term option (упомянуто в §Pre-workshop placeholder), не приоритет — отдельный major project, требует CMS-side rewrite.
**Калибровочные факты:**
- 2026-05-18 incident: RTO факт = 15 ч, RPO факт = 9 дней (Hyper Backup от 2026-05-09).
- Target 4ч / 1ч для CMS = **огромный шаг** от текущей baseline.
- Прогресс 2026-05-20: VDS поднят → infra (gitea/verdaccio/registry/DB-park) уже decoupled с windows-host SPOF. CMS — пока нет.
### CMS backup pipeline — 2-фазный план
**Текущий статус (2026-05-21):** CMS прод (sites + MSSQL + MinIO) на [[../entities/windows-recovery-host]] **не имеет ни одного бэкапа нового состояния** — последний Hyper Backup 2026-05-09 был с мёртвой синки. RPO факт = ∞ (если host сгорит сейчас — теряем всё).
**Фаза 1 — stop-gap (1-2 дня работы, на этой неделе):**
Impl-task: `windows-host-fallback-backup-daily` (🟢 closed 2026-05-24, деком­мишнен 2026-06-11).
- MSSQL: ежедневный `BACKUP DATABASE FULL` → локальный `D:\backup\` → ночной rsync на [[../entities/kreknin-synology]]
- IIS files (`C:\sites\*`): ежедневный rsync → kreknin
- MinIO data dir: ежедневный rsync → kreknin
- Cron через Windows Task Scheduler + SSH-pipe (паттерн как у [[../../.tasks/vds-backup-rsync-kreknin]])
- Достигаемый RPO = 24ч (плохо vs target 1ч, но **лучше чем ∞** и достаточно как мост до Фазы 2)
- **Не строить ничего изощрённого** — это патч на 2-4 недели до миграции MSSQL/MinIO.
**Фаза 2 — proper RPO 1ч после миграции на VDS (1-2 недели работы):**
Impl-task: [[../../.tasks/mssql-minio-migration-to-vds]].
- MSSQL container на [[../entities/vds-kzntsv]] как часть `/opt/stacks/databases/` рядом с pg/maria/mongo/redis
- MinIO контейнер на VDS отдельным stack'ом `/opt/stacks/storage/`
- CMS app в IIS на windows-host ходит через TCP на `mssql.vds.kzntsv.site:1433` / `minio.vds.kzntsv.site:9000` — connection strings меняются, остальное прозрачно (паттерн уже работает для DBs через traefik raw-TCP, см. [[traefik-tcp-passthrough-vs-starttls]] + [[db-tls-self-signed-via-traefik-raw-tcp]])
- Backup pipeline = расширение **существующего** `/opt/stacks/backup/scripts/run.sh` на VDS: добавить `sqlcmd BACKUP LOG` ежечасно + ежедневный full в существующий rsync-pipeline к kreknin
- **Достижение RPO 1ч** = tx log backup каждые 60 мин (или 15 — tunable)
- **Risk:** сетевая латентность IIS→MSSQL теперь WAN (10-30ms vs localhost). Большинство CMS-операций batchey, не latency-sensitive — но **pre-cutover benchmark обязателен** для hot-paths (admin assets UI / search / каталоги).
**Stop-gap pipeline списывается после Фазы 2** — backup data будет идти через VDS-pipeline.
### IIS migration track
**Текущий статус:** IIS на [[../entities/windows-recovery-host]] — temp-solution recovery 2026-05-19 (изначально recovery-host был VM, переехали на native IIS host для стабильности — см. [[../sources/iis-host-migration-2026-05-19]]). Домашняя машина = SPOF + физический риск (пожар / залив / кража / power-loss).
**Target end-state (долгосрочный):** snolla CMS переписан на node → IIS не нужен вообще, всё в docker на VDS. Это **dev-работа**, вне scope этой roadmap'ы. Прогресс отслеживается отдельно user'ом.
**Переходный план:** managed Windows hosting (RU-провайдер), куда переедет IIS до завершения node-rewrite'а.
Impl-task (research-only): [[../../.tasks/windows-hosting-vendor-research]] (1 день).
- Output: 1-pager с recommended vendor + plan / cost estimate / migration recipe outline.
- Comparison candidates: Rusonyx (у тебя уже там VDS), Selectel, FirstVDS, Beget.
- Critical constraints: RDP-доступ, .NET Framework 4.8 native, IIS 10, external MSSQL/MinIO connectivity (на VDS после Фазы 2).
После research → файлим follow-up impl-task `iis-migration-to-managed-windows-hosting` (большая, mes+ работы) — но **не сейчас**, пока на windows-host'е работает + stop-gap backup'ы плюсуют RPO 24ч safety net.
### Cloud off-site backup target (3-й уровень 3-2-1)
**Recommendation (не файлится impl-таской пока):** **Yandex Object Storage** (~1.6₽/GB/мес Standard tier, S3-compatible — работает с restic / rclone / duplicati). РФ-юрисдикция → нет cross-border сюрпризов.
**Альтернатива rejected:** Backblaze B2 — дешевле (~$5/TB/мес) но cross-border payment friction + sanctions risk.
**Defer impl:** [[../entities/kreknin-synology]] уже географически off-site (другая локация). Cloud добавится **поверх** kreknin'а, когда захочется паранойи "kreknin тоже может сгореть" или после первого incident'а где kreknin не помог. До этого: kreknin = "1 off-site" из 3-2-1.
**Future impl-task:** `cloud-offsite-backup-yandex-object` — ⚪ ready (не file'нута), создать когда руки дойдут.
### Network resilience
**Recommendation (не файлится impl-таской):** **defer multi-WAN.** Провайдер исторически стабилен. OpenWRT mwan3 + 4G/LTE USB-modem = страховка ~3-5к₽ единоразово + симка ~200₽/мес, но low-probability event. Возвращаемся после первого incident'а с потерей провайдера.
**Cloudflare Tunnel** (free tier, IP-абстракция, no DDoS-protection): отдельный вопрос для workshop pass 2 — добавляет vendor dependency на edge, trade-off обсуждается отдельно когда станет интересно (особенно после миграции MSSQL/MinIO на VDS — тогда windows-host станет thin web-layer, легче абстрагировать его IP).
**Future impl-tasks (не file'нуты):**
- `network-mwan3-4g-failover` — ⚪ ready, file когда подгорит провайдером
- `cloudflare-tunnel-edge-abstraction` — workshop pass 2 решение
### Monitoring stack
**Recommendation (не файлится impl-таской, но приоритет high среди not-filed):** **Uptime Kuma на VDS.** Docker, ставится за 30 мин на `/opt/stacks/monitoring/`, push-нотификации через ntfy (уже работает `vds-ops` topic — см. [[../../.tasks/vds-ntfy-push]]). Покрывает HTTP/TCP/ping/DNS healthchecks + cert-expiry warnings.
**Anti-recommend:** Prometheus + Grafana — overkill для one-person infra, full-stack observability нужен когда 20+ сервисов. Внешний (Pingdom/UptimeRobot) — платный, лучше self-host раз VDS уже есть.
**Initial monitor set (для impl):**
- HTTP 200 OK на 8 CMS hosts (snolla.com, labtools.ru/pro, pilorama98.ru, tandemmebel.ru, emspb.ru, kupimknigi.spb.ru, maljarka.tandemmebel.ru)
- HTTP 200 на инфра-hosts: git.kzntsv.site, verdaccio.kzntsv.site, registry.kzntsv.site, ntfy.vds.kzntsv.site
- TCP на DB-park ports: postgres/mariadb/mongo/redis (5432/3306/27017/6379)
- Ping на windows-recovery-host (94.19.247.14) и VDS (89.253.255.94)
- Cert-expiry warnings (Uptime Kuma встроено)
**Желательно установить ДО** того как windows-host опять упадёт — чтобы инцидент был "ntfy push 2 мин назад", а не "мне Серёга позвонил".
**Future impl-task (не file'нута):** `monitoring-uptime-kuma-deploy` — ⚪ ready, **приоритет high** среди not-filed.
### Runbook coverage matrix
**Текущее покрытие** (post-mortem chronologies с step-by-step recipe):
- [[../sources/nas-recovery-session-2026-05-18]] — NAS-loss recovery
- [[../sources/iis-host-migration-2026-05-19]] — IIS migration recipe
- [[../sources/vds-kzntsv-bootstrap-2026-05-20]] — VDS bootstrap
**Missing runbooks:**
- VDS-loss recovery (если Rusonyx упал / аккаунт потерян)
- Cert-expiry (acme.json renewal failure, manual DNS-01 через REGRU)
- DB-corruption restore (per-service: MSSQL, postgres, mariadb, mongo)
- Ransomware-recovery (encrypted backups restore protocol)
- OpenWRT-loss (новый роутер, restore configs)
**Defer:** runbook'и для **текущей** переходной архитектуры устареют через 2 мес (после `mssql-minio-migration-to-vds` + `iis-migration-to-managed-windows-hosting`). Пишем после стабилизации архитектуры — иначе тратим время на документацию которая не доживёт до использования.
**Future impl-task (не file'нута):** `runbook-coverage-matrix-design` — ⚪ ready, file **после** migrations завершены.
### Top-3 impl-tasks filed (2026-05-21)
1. **[[../../.tasks/cms-stopgap-backup-daily]]** ⚪ — Фаза 1, plug RPO=∞ за 1-2 дня.
2. **[[../../.tasks/mssql-minio-migration-to-vds]]** ⚪ — Фаза 2 enabler, achieves RPO 1ч target.
3. **[[../../.tasks/windows-hosting-vendor-research]]** ⚪ — design-task для IIS-переезда (1 день research, output = recommendation + cost).
### Workshop pass 2 — open agenda
Что **не** обсуждалось в этом проходе (для следующего workshop'а когда руки дойдут):
- Cloudflare Tunnel — edge abstraction trade-off
- Off-site cloud target — конкретный configure (decision уже сделано, ждёт kreknin-incident'а или паранойи)
- Hermes service — defer, ждёт уточнения user
- snolla node-rewrite progress — dev-track, периодически переоценивать impact на этот roadmap (если близко к completion → managed Windows hosting миграция отменяется)
- DR drill cadence — failover тесты раз в квартал (упомянуто в §Pre-workshop placeholder, не закрыто)
---
## Pre-workshop placeholder (2026-05-19) — historical draft
> **Note:** ниже — изначальные наброски до workshop pass 1. Не source of truth, оставлены для history. Concrete decisions — в §Workshop pass 1 выше.
## Что сейчас не так
См. [[recovery-architecture-snapshot]] → раздел "Single Points of Failure". Кратко:
- ⚠️ Windows-PC = SPOF
- ⚠️ Один public IP
- ⚠️ Один MSSQL primary без replica
- ⚠️ Один MinIO single-node
- ⚠️ Backup только Hyper Backup на одну синку (Kreknin), сейчас она cold
- ⚠️ Backup нового рабочего состояния (CMS data на recovery-host) **отсутствует**
- ⚠️ Нет off-site backup (cloud)
## Цели верхнего уровня
1. **RTO** (Recovery Time Objective) — сколько максимум **downtime** клиенты должны видеть при отказе.
- Текущий по факту: ~15 часов (что и было).
- Целевой: **< 1 час** для одиночного отказа, **< 4 часов** для каскадного.
2. **RPO** (Recovery Point Objective) — сколько максимум **данных потерять** при отказе.
- Текущий: до 24 часов (если успеет бэкап после изменения) — больше если бэкап не успел.
- Целевой: **< 1 час**, в идеале continuous (replication).
3. **Стоимость** — bounded by разумным % от выручки клиентов.
4. **Operational simplicity** — никаких heroic ops по 15 часов в случае проблемы.
## Технические направления (наброски)
### Backup-стратегия 3-2-1
Стандарт: **3** копии данных, **2** разных media, **1** off-site.
- **Копия 1:** primary storage (live) на NAS / Windows-PC
- **Копия 2:** local backup target ([[kreknin-synology]] остаётся, плюс отдельный)
- **Копия 3:** **cloud off-site** — Backblaze B2 / Yandex Object Storage / AWS S3 Deep Archive / Hetzner Storage Box
- Все backups encrypted client-side
- Retention: 7 daily / 4 weekly / 12 monthly
### Compute resilience
Варианты:
A. **Multi-host on-prem:** 2-3 физических машины с виртуализацией (Proxmox VE), HA-кластер, VMотом migration. Дорого, но автономно.
B. **Cloud-first:** перенос CMS-стека в managed Kubernetes (Yandex Cloud / VK Cloud / hetzner) — managed MSSQL / managed S3 / managed Postgres. Простота операций, но vendor lock-in.
C. **Гибрид:** primary on-prem (Synology с CMR), warm standby в cloud (готовый к failover за 5 мин). Compromise.
### Database resilience
MSSQL options:
- **Always On Availability Groups** (требует Enterprise license — недёшево)
- **Log Shipping** (бесплатно, но manual failover)
- **MSSQL → PostgreSQL миграция?** (если есть полная свобода — выгоды Open Source + распространённые managed предложения).
Для recovery-сценария: **continuous backup** через VDI + native MSSQL TDE backup → S3.
### Network resilience
- **Multi-WAN на OpenWRT:** primary провайдер + 4G/LTE USB-modem как failover. OpenWRT mwan3 package.
- **Cloudflare Proxy / Tunnel:** перенаправление публичного трафика через Cloudflare edge. Помогает с DDoS и переключением IP без DNS-changes.
- **Static IP пересмотр:** разные провайдеры → multi-homed setup.
### Monitoring + auto-recovery
- **Healthchecks** на каждый layer (TCP, HTTP, deep DB query) — Prometheus + Alertmanager или Uptime Kuma.
- **Watchdog скрипты** — auto `ipconfig /release/renew` в VM, auto `docker compose restart` контейнера если healthcheck падает > N min.
- **Telegram-уведомления** на инциденты (для пользователя в реал-тайм когда что-то фейлится).
### Документация и runbook
- Runbook на каждый процедурный сценарий (failover, restore, network swap).
- Регулярные **failover drills** раз в квартал — буквально нажать "восстановиться" и засечь время.
- Wiki эта — стартовая точка.
## Что НЕ цели
- Перестройка с нуля «потому что сейчас не идеально». Текущая архитектура работает, замена должна быть инкрементальной.
- 99.99% uptime — нереалистично для one-person infrastructure, и не нужно для CMS-сайтов клиентов.
## Следующие шаги (когда дойдут руки)
1. Заменить WD40EFAX на CMR-диски, пересобрать пул [[dead-synology-diskstation]].
2. Включить ежедневный Hyper Backup из нового пула на [[kreknin-synology]].
3. Добавить cloud-backup target (Yandex Object Storage наиболее очевидный для RU-юрисдикции).
4. Настроить monitoring/alerts.
5. Документировать runbooks.
Связано со всеми остальными страницами этого wiki.
## Прогресс 2026-05-20
**Decouple инфра-сервисов от windows-recovery-host SPOF.** Поднят отдельный облачный VDS [[vds-kzntsv]] (Rusonyx 160 NVMe), туда мигрированы gitea / verdaccio / docker-registry + shared DB park (Postgres/MariaDB/Mongo/Redis). [[windows-recovery-host]] теперь хостит **только** production CMS. Это не полный multi-host resilience (VDS сам по себе SPOF), но critical infra decoupling сделан.
Запланированы follow-up tasks (см. `.tasks/`):
- `vds-backup-rsync-kreknin` — ежедневный 05:00 MSK rsync VDS → kreknin с email-нотификацией.
- `vds-ntfy-push` — self-hosted push на Android для backup status и monitoring.
- `vds-gc-cron` — cron GC для verdaccio + registry чтобы не повторился incident 99G на registry.
Следующая фаза по originally outlined ladder — добавить **cloud off-site backup target** (Backblaze B2 / Yandex Object Storage) поверх kreknin'а, и replicated MSSQL для production CMS.