Phase 10 успех — recipe из post-mortem применён полностью: backend port :8089, bak-pre-attempt2 серия, IIS binding + Stop+Start, loop-detect через docker exec wget, 2 canary phone-tests от мобильного интернета, batch 9 cms; stayer routes user-ом подтверждены internal → .yml.disabled. Touched: - sources/iis-host-migration-2026-05-19.md — append Phase 10 - concepts/iis-migration-2026-05-19-postmortem.md — footer attempt-2-succeeded с маппингом recipe A-G - concepts/recovery-architecture-snapshot.md — major rewrite, chain через host-IIS:8089, VM = parallel fallback - entities/snolla-recovery-vm.md — status parallel-fallback, 24-48h soak - entities/windows-recovery-host.md — IIS sites active prod - NEW concepts/docker-host-loopback-detect.md — recipe loop-detect + WinHTTP-proxy gotcha - index.md, log.md
7.8 KiB
title, type, tags, sources, updated
| title | type | tags | sources | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Docker host-loopback detection — как доказать что host.docker.internal:N не петля в traefik | concept |
|
|
2026-05-19 |
Docker host-loopback detection
Конкретная техника доказать что host.docker.internal:<port> внутри docker-контейнера действительно резолвится в host-уровневый процесс, а не возвращается обратно в тот же docker-контейнер (Docker Desktop NAT loopback). Эта проверка обязательна прежде traefik patch'a на host-backend, иначе можно повторить incident attempt 1 из iis-migration-2026-05-19-postmortem (traefik backend host.docker.internal:80 → Docker NAT loop в собственный HTTP entrypoint → 301 от http-catchall middleware → TOO_MANY_REDIRECTS).
Когда применять
- Любой раз когда traefik (или другой reverse-proxy в docker container) должен идти на host (нативный IIS, nginx, Postgres, etc.).
- Особенно когда backend port совпадает с одним из traefik publish-ports или потенциально может маршрутизироваться обратно в traefik через Docker Desktop port-mapping.
Recipe
Шаг 1 — выбрать backend port вне traefik publish-set
Запомни traefik publish-ports — это запретный список для backend. Например при наших traefik publish'ах :8000, :4443, :8080 — backend :80 тоже опасен (Docker Desktop NAT loopback creates implicit mapping в нижележащих случаях).
Безопасные porter — те что никем другим в docker не публикуются и не совпадают с traefik publish-set. Прежде выбора — Test-NetConnection -Port N на host, чтобы убедиться нет другого listener'а.
Шаг 2 — IIS binding и Stop/Start
# 🖥️ ELEVATED PowerShell on Windows host
Import-Module WebAdministration
$site = 'snolla'; $port = 8089
New-WebBinding -Name $site -Protocol http -Port $port -IPAddress '*'
Stop-Website -Name $site
Start-Website -Name $site # ← КРИТИЧНО, без restart binding не активируется
Get-NetTCPConnection -LocalPort $port -State Listen # verify
Шаг 3 — probe изнутри traefik container (НЕ через локальный curl)
КЛЮЧЕВОЕ: probe должен идти изнутри docker-контейнера, чтобы воспроизвести точно тот же путь что traefik будет использовать.
# 💻 Windows host (non-elevated)
docker exec traefik wget --spider -S --header="Host: <domain>" "http://host.docker.internal:8089/" 2>&1 | Select-Object -First 10
--spider = HEAD-only, не follow redirects. -S = show response headers. Без этих флагов wget может уйти follow public DNS → router → traefik → backend → искусственная петля через интернет (не имеет отношения к Docker NAT).
Шаг 4 — интерпретировать
Хороший знак (traefik найдёт host IIS):
Connecting to host.docker.internal:8089 (192.168.65.254:8089)
HTTP/1.1 200 OK
Server: Microsoft-IIS/10.0 ← НАШ IIS отвечает
X-Powered-By: ASP.NET
Content-Length: 28413
Признаки:
- IP-резолв
host.docker.internal= 192.168.65.254 (Docker Desktop host gateway, может быть другой адрес в зависимости от версии). Server: Microsoft-IIS/10.0⇒ это IIS, не traefik.X-Powered-By: ASP.NET⇒ ASP.NET runtime обработал.- Content-Length ≠ 17 (traefik «Moved Permanently» body длиной 17 = подозрительный знак, см. ниже).
Плохой знак (Docker NAT loopback):
HTTP/1.1 301 Moved Permanently
Content-Type: text/plain; charset=utf-8
Content-Length: 17 ← traefik signature (= "Moved Permanently\n")
Location: https://<host>/ ← redirect-to-https middleware
Признаки:
- Нет
Server: Microsoft-IIS/10.0(илиServer: traefik). Content-Length: 17— общая длина text/plain "Moved Permanently".- 301 на
https://<входной-host>/— это traefik http-catchall middleware (см.https.ymlredirect-to-https).
Если видишь второй паттерн — STOP. Backend port пересекается с traefik. Не делай traefik patch, выбери другой port.
Шаг 5 — public-path verification
После step 4 — проверь по реальному пути client → traefik → backend:
# 💻 Windows host
curl.exe -k -sS -I --max-redirs 0 -m 10 -H "Host: <domain>" "https://localhost:4443/"
Должно быть first response = Server: Microsoft-IIS/10.0 (либо CMS canonical 301 от IIS — main thing — Server header указывает на IIS, не на traefik).
Pitfall — WinHTTP proxy на хосте strip's headers
Локальный curl.exe http://localhost:<host-port>/ на Windows может ходить через WinHTTP system proxy который strips Server / X-Powered-By headers на response (плюс часто добавляет Proxy-Connection: keep-alive). Это даёт ложное впечатление «не IIS отвечает», хотя на самом деле IIS работает корректно.
Признаки что probe идёт через WinHTTP proxy:
- Нет
Server:в response, но контент корректный (например HTML страница сайта). Proxy-Connection: keep-aliveв response.- Headers выглядят «обрезанными».
Решение: для loop-detect / IIS-confirmation тестов используй docker exec traefik wget (изнутри docker, минует Windows-уровневые proxy) или Invoke-WebRequest через PS с -Proxy ''. НЕ используй curl.exe как единственный источник truth для headers.
Подтверждённый рабочий пример (2026-05-19 attempt 2)
docker exec traefik wget --spider -S --header="Host: emspb.ru" "http://host.docker.internal:8089/"
→ Connecting to host.docker.internal:8089 (192.168.65.254:8089)
→ HTTP/1.1 301 Moved Permanently
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Location: http://www.emspb.ru/
docker exec traefik wget --spider -S --header="Host: localhost:8089" "http://host.docker.internal:8089/"
→ HTTP/1.1 200 OK
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Content-Length: 28413
Заключение: host.docker.internal:8089 → 192.168.65.254:8089 (Docker Desktop host gateway) → host IIS. NO Docker NAT loop. 301 — это CMS canonical (CMS-side www-redirect), не traefik http-catchall (тот бы дал Content-Length: 17 text/plain без Server: Microsoft-IIS/10.0).
Связано
- iis-migration-2026-05-19-postmortem — почему attempt 1 сломался (этот же loop, но с
:80который пересекался с docker-NAT mapping). - traefik-on-windows-docker-desktop — общие traefik pitfalls на Docker Desktop.
- iis-host-migration-2026-05-19 Phase 10 — где этот recipe применён первый раз и подтверждён.