Files
admin/.wiki/concepts/recovery-architecture-snapshot.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

172 lines
13 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: Recovery architecture — текущая инфраструктура (2026-05-19, attempt 2)
type: concept
tags: [architecture, current-state, snapshot, recovery, historical]
sources: [../sources/nas-recovery-session-2026-05-18.md, ../sources/iis-host-migration-2026-05-19.md]
updated: 2026-05-19
---
> ⚠ **ИСТОРИЧЕСКАЯ ЗАПИСЬ — архитектура на 2026-05-19.** С тех пор всё изменилось: CMS переехала на [[../entities/ruvds-iis-host]] (2026-05-24), MSSQL/MinIO на [[../entities/vds-kzntsv]] (2026-05-22), imgproxy на [[../entities/books-vds]] (2026-06-08), [[../entities/windows-recovery-host]] декоммишнен (2026-06-08). Содержимое ниже — только для понимания recovery-сессии 2026-05-19. Содержит broken links на несуществующие страницы (`cms-server-port-leak-fix`, `cms-admin-assets-root-folder-seed`, `webconfig-password-xml-escape`).
# Recovery Architecture Snapshot
Снимок production-инфраструктуры на конец **второй (успешной) попытки** миграции 2026-05-19. 14 cms hostnames теперь идут через native IIS на [[windows-recovery-host]] напрямую. VM `snolla-recovery`**parallel-fallback**, running но больше не на prod-пути (24h+ soak, потом savestate). 2 stayer routes окончательно **disabled** через traefik (host IIS sites живут для прямого доступа).
История: attempt 1 в этот же день сломал prod, был revert; recipe — в [[iis-migration-2026-05-19-postmortem]]. Attempt 2 выполнен по recipe — см. [[iis-host-migration-2026-05-19]] Phase 10.
Это **рабочее, но всё ещё временное** состояние — single-point-of-failure на бытовом железе. Замечания по улучшению — [[future-resilient-architecture-goals]].
## Цепочка запроса от клиента до CMS (host-IIS chain, attempt 2)
```
Клиент (browser)
→ DNS resolve (REGRU): *.snolla.com (включая on.snolla.com — default subdomain),
labtools.ru/pro, pilorama98.ru, tandemmebel.ru, emspb.ru, kupimknigi.spb.ru,
maljarka.tandemmebel.ru, sestech.ru, ics-artmaterials.com, rimiz.ru (404 CMS-side)
→ 94.19.247.14 (public IP, статический у провайдера)
→ router OpenWRT (192.168.1.1) [[openwrt-router]]
→ NAT 443 → 192.168.1.143:4443
→ NAT 80 → 192.168.1.143:8000
→ Windows-PC (192.168.1.143) [[windows-recovery-host]]
→ traefik 2.6.6 на 4443/8000
→ TLS termination, certResolver=letsEncrypt из acme.json
→ match по Host header (file-provider rules в data/custom/*.yml)
→ backend = http://host.docker.internal:8089/
→ host:8089 → IIS site `snolla` (binding *:8089)
→ IIS native на хосте
→ site `snolla`, .NET Framework 4.8.1, AppPoolIdentity
→ C:\sites\snolla\, sitePath patched, conn → localhost
→ ASP.NET CMS code (.NET Framework 4.8)
→ Connection strings:
→ MSSQL: Data Source=localhost,1433 (host:1433 = MSSQL container)
→ MinIO/storage: вопрос снят пользователем
→ Elasticsearch: не используется CMS
→ MSSQL container на host:1433
→ 5 production DB (MoreThenCms, Stayer*, stostayer, TireService)
← HTTP response back through chain
```
**VM `snolla-recovery`:** running parallel, no traffic (24h+ soak fallback). NAT port forwards `:18080/:18180/:18181/:18189` холостые. Будет savestate'ena после стабильности → потом unregistervm для освобождения ~92 GB.
## Stayer chain — DISABLED через traefik
```
stostayer.snolla.com / old.stostayer.ru
→ DNS → 94.19.247.14
→ router → traefik
→ match Host → нет routes (stostayer.yml.disabled, oldstostayer.yml.disabled)
→ traefik 404 "no route"
```
Host IIS sites `stostayer (:8090)` и `stostayer.old (:8091)` **живут** для прямого/локального доступа. Conn-strings:
- `stostayer`: `Data Source=www.stostayer.ru,1433`, user `stayer_site`, password XML-escaped (web.config, pass-protected)
- `stostayer.old`: `Data Source=localhost` (наш MSSQL контейнер)
## Запущенные docker контейнеры на хосте
| Container | Image | Port (host) | Volume |
|---|---|---|---|
| **traefik** | `traefik:v2.6.6` | 4443, 8000, 8080 | named: `traefik_traefik_letsencrypt`; bind: `data/traefik.yml`, `data/custom/` |
| **mssql** | `mcr.microsoft.com/mssql/server:2019-latest` | 1433 | named: `mssql_mssql_data` (filled from production tar) |
| **minio** | `minio/minio:RELEASE.2020-07-13T18-09-56Z` | 9000 | bind: `./data` |
| **imgproxy** | `darthsim/imgproxy:latest` | 8787 | (нет state) |
| **imgproxy-nginx** | `nginx:alpine` | 8788 | bind: `./nginx/cache`, `./nginx/nginx.conf` |
| **elasticsearch** | `elasticsearch:7.10.1` | 9200 | bind: `./data` |
Все на docker network `proxy` (external).
## Traefik routes (после attempt 2)
11 cms yml репойнтены на host IIS:8089. 2 stayer yml — `.disabled`.
| File | Hosts | Backend | Статус |
|---|---|---|---|
| snolla.yml | snolla.com + 10 *.snolla.com subdomains (rule explicit) | host.docker.internal:8089 | active → host IIS |
| rimiz.yml | rimiz.ru, www.rimiz.ru | host.docker.internal:8089 | active (но CMS-side 404 — known) |
| labtools.yml | labtools.ru, www.labtools.ru | host.docker.internal:8089 | active → host IIS |
| labtoolspro.yml | labtools.pro, www.labtools.pro | host.docker.internal:8089 | active → host IIS |
| pilorama98.yml | pilorama98.ru, www.pilorama98.ru | host.docker.internal:8089 | active → host IIS |
| tandemmebel.yml | tandemmebel.ru, www.tandemmebel.ru | host.docker.internal:8089 | active → host IIS |
| emspb.yml | emspb.ru, www.emspb.ru | host.docker.internal:8089 | active → host IIS (canary 1, phone-test ✅) |
| kupimknigi.yml | kupimknigi.spb.ru | host.docker.internal:8089 | active → host IIS |
| maljarka.yml | maljarka.tandemmebel.ru | host.docker.internal:8089 | active → host IIS |
| sestech.yml | sestech.ru, www.sestech.ru | host.docker.internal:8089 | active → host IIS |
| isc-artmaterials.yml | ics-artmaterials.com, www.ics-artmaterials.com | host.docker.internal:8089 | active → host IIS (CMS-side 404 — known) |
| **stostayer.yml.disabled** | stostayer.snolla.com | (n/a, route disabled) | **DISABLED**, host IIS :8090 локально |
| **oldstostayer.yml.disabled** | old.stostayer.ru | (n/a, route disabled) | **DISABLED**, host IIS :8091 локально |
**Backup-stamp файлы** (накопились за обе попытки):
- `*.yml.bak-2026-05-19` — самый ранний backup (до session).
- `*.yml.bak-phase3-2026-05-19` — rollback baseline (attempt 1 → revert state, всё на VM `:18080`).
- `*.yml.bak-hostip-2026-05-19` — failed attempt 1 (host.docker.internal:80 ⇒ Docker NAT loop).
- `*.yml.bak-stayer-switch-2026-05-19` — stayer switch attempt artefact (Phase 5/6 prev session).
- **`*.yml.bak-pre-attempt2-2026-05-19`** — текущая live conf attempt 2 (host:8089 backend). Это baseline для **atomic revert** этой попытки.
Плюс file-provider маршруты для инфраструктурных хостов:
| File | Host | Backend |
|---|---|---|
| elasticsearch.yml | elasticold.kzntsv.site | http://elasticsearch:9200 + basicAuth `books:...` |
| minio.yml | minio.kzntsv.site | http://minio:9000 |
| imgproxy.yml | imgproxy.kzntsv.site | http://imgproxy-nginx:80 |
И мёртвые (не отключены, но смотрят в никуда):
- `disk.yml` → 192.168.1.10:5005 (Synology disk на мёртвой синке)
- `dsm.yml` → 192.168.1.10:5000 (DSM мёртвой синки)
## Host IIS configuration (active prod)
| Site | Bindings | Physical path | Pool identity | Прим. |
|---|---|---|---|---|
| **snolla** | `*:80`, `*:8089` | `C:\sites\snolla` | `ApplicationPoolIdentity` (.NET v4.0 Integrated) | **active prod** — catch-all для 11 cms hosts, traefik backend `:8089` |
| **stostayer** | `*:8090` | `C:\sites\stostayer` | `ApplicationPoolIdentity` | local-only (traefik route disabled), conn → `www.stostayer.ru,1433` |
| **stostayer.old** | `*:8091` | `C:\sites\stostayer.old` | `ApplicationPoolIdentity` | local-only (traefik route disabled), conn → `localhost,1433` |
| Default Web Site | (stopped, autoStart=false) | — | — | — |
ACL: `IIS AppPool\<site>:(OI)(CI)M` рекурсивно на каждом site root.
## DNS
Все домены остались указывать на **94.19.247.14** (public IP, статический). REGRU как registrar/DNS.
## Backup инфраструктура
Текущая (на момент 2026-05-19, после attempt 2):
- [[kreknin-synology]] держит Hyper Backup репо мёртвой синки (~430 GB). Новых бэкапов с recovered Windows-PC **НЕТ**.
- Локально на Windows-PC: `C:\nas-recovery\vm-sites\` (~11 GB, дамп IIS sites из VM до patches) — резерв если host-IIS сломается катастрофически. После 48h+ uptime можно почистить.
- `C:\nas-recovery\backup\snolla\snolla.ova` (42.5 GB) — оригинал OVA. После cleanup VM можно удалить.
**Дыра:** если Windows-PC сгорит — всё ляжет. Никакой репликации, никакого off-site backup для нового рабочего состояния.
## SSH ключи и доступ
- [[windows-recovery-host]] → [[kreknin-synology]]: `id_ed25519_kreknin` (vitya@195.19.90.188)
- [[windows-recovery-host]] → [[openwrt-router]]: `id_ed25519_openwrt` (root@192.168.1.1)
- [[windows-recovery-host]] → [[snolla-recovery-vm]]: `id_ed25519_snolla_vm` (vitya@127.0.0.1:8022)
После recovery — отозвать публичные ключи Claude из этих 3 машин (`~/.ssh/authorized_keys` или эквивалент). См. соответствующие entity-страницы.
## Известные открытые баги
1. ~~**X-Forwarded headers** не передаются от traefik в IIS → CMS делает redirect с `:4443` в URL.~~**RESOLVED 2026-05-19 вечер** через URL Rewrite 2.1 + `<serverVariables>` rule на host IIS. Также закрыл утечку `:8089` в admin SPA после attempt 2 миграции.
2. **rimiz.ru → 404**. CMS-side, не инфра.
3. **ics-artmaterials.com → 404**. Аналогично — CMS-side (`www.ics-artmaterials.com → 301 → ics-artmaterials.com → 404`).
8. ~~**emspb.snolla.com `/admin/assets/<guid>/getList` → 500 NullReferenceException**.~~**RESOLVED 2026-05-19 вечер** через DB seed root AssetsFolder rows для 15 sites без них (включая emspb, pilorama98, и др.). Симптом был НЕ site-specific — общий для всех sites которые никогда не использовали admin assets UI. Долгосрочный TODO — null-guard в `AssetsJsonViewModelBuilder.Build` (требует recompile DLL, отложено до восстановления build env).
4. **acme.json HTTP-01 renewal** будет фейлить для доменов с DNS не на нашем IP → переезд на DNS-01 через REGRU до истечения сертификатов (~3 месяца).
5. **C:\inetpub\logs\** растёт — нужна ротация.
6. **docker.sock provider в traefik** не работает — но не блокирует (всё через file-provider). См. [[traefik-on-windows-docker-desktop]] Pitfall 3.
7. **WinHTTP proxy на хосте strips response headers** для `curl.exe http://localhost:...` — для loop-detect/IIS confirmation использовать `docker exec traefik wget` или `Invoke-WebRequest`. См. [[docker-host-loopback-detect]].
## Single Points of Failure
- Один Windows-PC (если сгорит — всё ляжет)
- Один публичный IP / провайдер
- Один WiFi-канал
- Один OpenWRT-роутер
- Один **host IIS instance** обслуживает весь cms-трафик (VM остаётся parallel fallback ещё 24-48h)
- Один MSSQL контейнер (single primary, нет replica)
- Один MinIO (single drive, не distributed)
Каждый SPOF — кандидат на улучшение в [[future-resilient-architecture-goals]].