Files
admin/.tasks/iis-on-host-migration.md
vitya c55cb11948 tasks: merge imported MoreThenCms task history with admin live agenda
- 7 admin per-task files moved from .tasks-imported/ to .tasks/ (renames)
- NEXT-SESSION-PROMPT.md (iis-migration session handoff) kept as sibling
- .tasks-imported/STATUS.md removed (content merged into admin STATUS.md)
- admin STATUS.md gains 'Imported from MoreThenCms' section with all 7 admin
  done tasks verbatim. 4 CMS-domain task blocks excluded (stay in MoreThenCms):
  cms-admin-assets-root-folders-seed, cms-port-leak-fix,
  traefik-maljarka-502-bug, cms-maljarka-https-mode-bug-fix
- morecms-subtree-split marked done (acceptance: 4 split branches exist with
  per-file history)
- admin-subtree-import-and-cleanup marked active (in-progress this session)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:51:48 +03:00

14 KiB
Raw Blame History

iis-on-host-migration

Goal

Перенести IIS-сайты MoreThenCms из VirtualBox VM (snolla-recovery) на нативный IIS Windows-хоста. VM остаётся как hot fallback на первое время; после успешного нативного-запуска — выключаем VM, освобождаем 4 GB RAM + ~92 GB диска.

Зачем: VM на VBox = лишний слой нестабильности (видели один network hang). Нативный IIS на хосте — проще, быстрее, без NAT-форвардов и VBox-капризов. Контейнеры MSSQL/MinIO/etc. уже на этом же хосте — устраняем сетевой роутинг.

Что у нас уже есть

  • IIS установлен на хосте (W3SVC running, default site empty, .wiki/entities/windows-recovery-host)
  • .NET Framework 4.8.1 на хосте
  • Полная копия C:\inetpub\wwwroot\ из VM → C:\nas-recovery\vm-sites\wwwroot\ (8.69 GB)
  • Полная копия C:\stayer\ из VM → C:\nas-recovery\vm-sites\stayer\ (2.21 GB)
  • MSSQL контейнер на host:1433 (5 production DB)
  • MinIO на host:9000, Elasticsearch на host:9200, imgproxy на host:8787/8788
  • Исходники CMS в C:\Users\vitya\projects\MoreThenCms\ (Git репо)
  • Traefik с 13 client routes (сейчас target = host.docker.internal:18080 = VM)

Key files

  • C:\nas-recovery\vm-sites\wwwroot\ — IIS-сайты из VM
  • C:\nas-recovery\vm-sites\stayer\ — stostayer-проекты
  • C:\inetpub\wwwroot\ — целевое расположение на хосте
  • C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\*.yml — backends, нужно переключить с host.docker.internal:18080 на localhost:80 (или какие-то local IIS-bindings)
  • Web.config файлы — connection strings уже патчены на 10.0.2.2, нужно вернуть на localhost или 127.0.0.1 для нативного IIS

План этапов

Phase 1 — Анализ и подготовка

  • Изучить IIS site bindings в VM (MoreThenCms.Web *:80, Snolla.IdentityManager *:80, stostayer *:8080, stostayer.old *:8081)
  • Решить port-mapping на хосте — оставлять 80? Или другие порты, traefik на 80/443 переадресует?
  • Изучить, какие IIS modules/features нужны (URL Rewrite, ARR, обязательные .NET runtimes — может потребоваться доставить)

Phase 2 — Импорт сайтов на хост

  • Скопировать C:\nas-recovery\vm-sites\wwwroot\*C:\inetpub\wwwroot\ (или другой root)
  • Скопировать C:\nas-recovery\vm-sites\stayer\C:\stayer\
  • Создать application pools (.NET v4.5 или эквивалент) для каждого сайта в IIS Manager
  • Создать sites в IIS:
    • MoreThenCms.Web (port 80 / другой)
    • Snolla.IdentityManager
    • stostayer (port 8080)
    • stostayer.old (port 8081)
  • Web.config patch: Data Source=10.0.2.2Data Source=localhost (или 127.0.0.1 / .\ ; UTF-8 BOM!)

Phase 3 — Перенос на хост IIS, тест

  • Stop VM (но не удалять!)
  • Update traefik backends в data/custom/*.yml:
    • http://host.docker.internal:18080/http://host.docker.internal:80/ (или какой порт IIS использует)
    • :18180:8080, :18181:8081
  • traefik restart
  • Прокликать все 11 клиентских доменов из публичного интернета
  • Убедиться что .NET Framework 4.8.1 справляется, нет missing assemblies, нет permission issues на App_Data / temp folders

Phase 4 — Cleanup VM

  • Когда стабильно ~неделя на хост-IIS — выключить VM окончательно
  • VBoxManage controlvm "snolla-recovery" poweroff
  • Удалить port forwards в OpenWRT, которые больше не нужны (host:18080, host:18180, etc.) — это уже не traefik backend
  • (Опционально) удалить VM из VBox: VBoxManage unregistervm "snolla-recovery" --delete. Это освобождает 92 GB на C:.
  • Удалить C:\nas-recovery\backup\snolla\snolla.ova (42.5 GB) и C:\nas-recovery\vm-sites\ (11 GB) — backup уже не нужен.

Phase 5 — Бонусы

  • Установить URL Rewrite + ARR для нормальной обработки X-Forwarded-Proto headers (fix CMS-редиректа с :4443 в URL — см. .wiki/concepts/traefik-on-windows-docker-desktop)
  • Настроить log rotation для C:\inetpub\logs\
  • Application Initialization (для warm-start CMS) — IIS Optional Feature, autostart сайтов

Open questions

  • CMS-код использует абсолютные пути типа C:\inetpub\wwwroot\MoreThenCms.Web\ (видели в Web.config <add key="sitePath" .../>)? Если да — путь должен остаться, либо обновить.
  • Authentication / IIS Application Identity — какая учётка должна крутить app pool? (LocalSystem? NetworkService? IIS AppPool<sitename>?)
  • Нужен ли .NET Framework Repair / SAC update перед миграцией?
  • Что с storage providers (Azure-SDK adapter в CMS)? — Связано с открытым вопросом о MinIO/Azure, что пользователь обещал прояснить.

Decisions log

  • 2026-05-19: задача поставлена после успешного recovery в VM. VM показала себя нестабильной (один network hang за день uptime), решено мигрировать на нативный IIS как более простой и предсказуемый стек.

Completed steps

  • Pulled VM sites to host (49 минут tar+ssh, 11 GB total)
  • IIS на хосте установлен (в рамках recovery, ещё нативно не использовался)
  • Phase 1 — discovery (2026-05-19): VM сайты/пулы/vdirs/features через SSH + appcmd, host IIS features через elevated DISM, source grep на hardcoded paths. См. .wiki/sources/iis-host-migration-2026-05-19.md.
  • Phase 2 — миграция MoreThenCms.Web (2026-05-19): C:\sites\MoreThenCms.Web (8.7 GB robocopy), Web.config patch (sitePath + conn → localhost, UTF-8 BOM), AppPool + Website на *:80, ACL :(OI)(CI)M для IIS AppPool\MoreThenCms.Web. Default Web Site остановлен. Smoke test через localhost с Host header — 10/11 хостов отвечают (rimiz.ru → 404, как issue ниже).
  • Phase 2 — stostayer / stostayer.old (2026-05-19, частично): сайты созданы на :8090/:8091C:\stayer\MoreThenCms.Web / C:\stayer\stostayer.old, AppPools созданы, ACL поставлен. Но локально оба отвечают timeout — рантайм-проблема не разбиралась.
  • Phase 3 — traefik switch (2026-05-19): 11 yml пропатчены host.docker.internal:18080host.docker.internal:80, traefik file-provider auto-reload, public smoke через https://localhost:4443/10/11 хостов → HTTP 200, rimiz.ru → 404. Stostayer/oldstostayer backend в traefik не трогали (остаются на VM).

Решения (decisions log дополнен)

  • 2026-05-19: Snolla.IdentityManager не мигрируется — пользователь подтвердил «не нужен» (Q-C в сессии). Если что-то внутри CMS дёргает его по localhost:8089 — увидим в логах, тогда вернёмся.
  • 2026-05-19: Sub-apps stostayer.old (/calc, /price, /price/tireService) не мигрируются — пользователь подтвердил «не нужны».
  • 2026-05-19: stostayer.connection-string на 89.253.219.2 не трогаем (Q-B пользователя «не трогай, так надо»). Это внешний production MSSQL для stayer-инфраструктуры, не наш контейнер.
  • 2026-05-19: AppPool identity = ApplicationPoolIdentity для всех 3 пулов (default outside SCM, безопасно). ACL :(OI)(CI)M (Modify) рекурсивно на site root.
  • 2026-05-19: Path strategy = C:\sites\ (не C:\inetpub\wwwroot\) — пользователь выбрал (б) в Q1.

Status — ROLLBACK (2026-05-19 вечер)

Миграция сделана, сломала prod, откатили. См. полный разбор в .wiki/concepts/iis-migration-2026-05-19-postmortem.md.

  • ⚠️ Phase 1-8 выполнены технически, но Phase 3 ввёл Docker port-loop (host.docker.internal:80 резолвится через Docker Desktop NAT обратно в сам traefik, https.yml redirect-to-https middleware → 301 → loop). Симптомы появились ~10 мин после моего "done" → 502 → TOO_MANY_REDIRECTS.
  • Revert (Phase 9) успешен: VM поднята из savestate, traefik backends восстановлены из .bak-phase3 → trafic снова через VM (как до session).
  • Артефакты для следующей попытки сохранены на хосте: C:\sites\{snolla, stostayer, stostayer.old, snolla-identity-manager} + IIS sites + AppPools + 3 traefik backup-stamp серий.

Completed Phase 4-9 (вечер 2026-05-19)

  • Phase 4 — реорг C:\sites\: rename MoreThenCms.Web → snolla, move C:\stayer\MoreThenCms.Web → C:\sites\stostayer, move C:\stayer\stostayer.old → C:\sites\stostayer.old, move + kebab-rename C:\stayer\Snolla.IdentityManager → C:\sites\snolla-identity-manager. Удалены 3 sub-app папки. C:\stayer\ снёс полностью.
  • Phase 4 — IIS rename: site MoreThenCms.Web → snolla, pool пересоздан как snolla (rename невозможен in-place), site rebound, физпуть обновлён на C:\sites\<name> для всех 3, sitePath в Web.config'ах подправлен под новые пути. ACL re-granted.
  • Phase 5 — stostayer.old DB-fix: Data Source=10.0.2.2localhost. После — :8091 → 200 локально (была наша ошибка в Phase 2 — пропустили этот файл).
  • Phase 6 — stostayer DB-migration: старый 89.253.219.2,1433 не reachable. Новые creds: www.stostayer.ru,1433, user stayer_site. Patched Web.config. Поймана и зафиксирована новая gotcha в webconfig-password-xml-escape& в пароле нужно XML-escape как &amp;.
  • Phase 7 — traefik privacy: stostayer.yml и oldstostayer.yml переименованы в .yml.disabled (потом возвращены в Phase 9). Backup: *.yml.bak-stayer-switch-2026-05-19.
  • Phase 8 — VM savestate: savestate. VMState=saved (потом возвращена в Phase 9). БЫЛО ПРЕЖДЕВРЕМЕННЫМ.
  • Phase 9 — ROLLBACK (после catastrophe): VBoxManage startvm, ipconfig release/renew для разморозки network, traefik yml восстановлены из .bak-phase3-2026-05-19, stayer .yml.disabled удалены и yml восстановлены из .bak-stayer-switch, docker restart traefik. Public smoke — 7 хостов через VM-chain ответили (2× 200, 5× 301 CMS-redirect = normal), пользователь подтвердил в браузере.

Что НЕ делать в следующей попытке

См. полный recipe в .wiki/concepts/iis-migration-2026-05-19-postmortem.md, кратко:

  • НЕ использовать backend host.docker.internal:80 (Docker NAT loopback с traefik HTTP entrypoint :80)
  • НЕ тестировать только -MaximumRedirection 5 (скрывает loop)
  • НЕ тестировать только из LAN (router-hairpin lying)
  • НЕ замораживать VM раньше чем через 24-48h стабильности host-стека
  • НЕ делать reorg/rename in same session as migration (атомность важна)
  • НЕ объявлять "done" до 24h+ uptime + теста из НЕ-LAN сети
  • При первом anomaly — STOP, revert на known-good, понять, потом fix (НЕ каскадные reactive changes)

Notes

  • VM не удалять до конца Phase 3 — это working fallback. Только после двух недель стабильной работы host-IIS.
  • Git push в gitea невозможен пока — gitea был на мёртвой синке. Восстановление gitea — отдельная задача (есть бэкап /docker/gitea/ 2.6 GB на kreknin-синке, можно поднять локально или временно класть code в другое место).
  • При работе с Web.config — обязательно UTF-8 BOM через [System.IO.File]::WriteAllText с [System.Text.UTF8Encoding]::new($true). Иначе IIS 500.19. Детали в .wiki/concepts/cms-config-rewrite-pattern.