Status: done (false) -> paused (true). Миграция была сделана, сломала prod (Docker port-loop), откатили. Артефакты на хосте остались inert для следующей попытки. - STATUS: 🟢 done -> 🟡 paused с детальным where-I-stopped - iis-on-host-migration: Phase 9 rollback задокументирован, что НЕ делать в следующей попытке (cross-ref на post-mortem) - NEXT-SESSION-PROMPT: переписан для retry-сценария — обязательное чтение post-mortem перед началом, recipe из 5 пунктов Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
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-сайты из VMC:\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.IdentityManagerstostayer(port 8080)stostayer.old(port 8081)
- Web.config patch:
Data Source=10.0.2.2→Data 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/:8091→C:\stayer\MoreThenCms.Web/C:\stayer\stostayer.old, AppPools созданы, ACL поставлен. Но локально оба отвечают timeout — рантайм-проблема не разбиралась. - Phase 3 — traefik switch (2026-05-19): 11 yml пропатчены
host.docker.internal:18080→host.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.ymlredirect-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\: renameMoreThenCms.Web → snolla, moveC:\stayer\MoreThenCms.Web → C:\sites\stostayer, moveC:\stayer\stostayer.old → C:\sites\stostayer.old, move + kebab-renameC:\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.2→localhost. После —:8091→ 200 локально (была наша ошибка в Phase 2 — пропустили этот файл). - Phase 6 — stostayer DB-migration: старый
89.253.219.2,1433не reachable. Новые creds:www.stostayer.ru,1433, userstayer_site. Patched Web.config. Поймана и зафиксирована новая gotcha в webconfig-password-xml-escape —&в пароле нужно XML-escape как&. - 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.