Здесь показаны различия между двумя версиями данной страницы.
| Следующая версия | Предыдущая версия | ||
|
linux:docker_network_conflict [2026/08/13 07:47] werwolf создано |
linux:docker_network_conflict [2026/08/13 08:03] (текущий) werwolf |
||
|---|---|---|---|
| Строка 1: | Строка 1: | ||
| - | ====== Конфликт сети Docker с корпоративной/VPN-сетью ====== | + | ====== Конфликт сети Docker с корпоративной / VPN-сетью ====== |
| + | |||
| + | <note tip> | ||
| + | **Страница:** ''dev:docker_network_conflict'' | ||
| + | **Когда смотреть:** внутренний сервис не открывается, DNS «в порядке», VPN подключён, недавно поднимали Docker. | ||
| + | </note> | ||
| ===== Симптомы ===== | ===== Симптомы ===== | ||
| - | * В браузере или ''curl'' не открывается внутренний сервис (Jira, LDAP, change-pass, git и т.п.) | + | * Не открывается внутренний сервис (Jira, LDAP, ''change-pass.iep.loc'', git и т.п.) — ни в браузере, ни в ''curl'' |
| * Ошибка: ''Connection refused'' или ''Failed to connect'' на портах 80/443 | * Ошибка: ''Connection refused'' или ''Failed to connect'' на портах 80/443 | ||
| - | * DNS резолвится **правильно** (''getent hosts hostname'' возвращает ожидаемый IP) | + | * ''getent hosts hostname'' возвращает **ожидаемый** IP |
| - | * Проблема появляется **после** ''docker compose up'' или когда на машине много Docker-сетей | + | * Проблема часто появляется после ''docker compose up'' или когда на машине много compose-проектов |
| - | * VPN подключён, но трафик до нужного IP **не идёт через tun0/tun1** | + | * VPN подключён (''tun0'' / ''tun1'' есть), но сервис недоступен |
| ===== Суть проблемы ===== | ===== Суть проблемы ===== | ||
| - | Docker создаёт bridge-интерфейсы (''br-xxxxxxxxxxxx'') с собственными подсетями, например: | + | Docker создаёт bridge-интерфейсы (''br-xxxxxxxxxxxx'') с собственными подсетями: |
| * ''172.17.0.0/16'' — default bridge | * ''172.17.0.0/16'' — default bridge | ||
| Строка 17: | Строка 22: | ||
| * ''172.18.0.0/16'' — ''status_app-network'' | * ''172.18.0.0/16'' — ''status_app-network'' | ||
| - | Если подсеть Docker **пересекается** с корпоративной сетью (часто ''172.16.0.0/12'' или конкретно ''172.22.0.0/16'' для IEP), Linux направляет пакеты **в Docker-bridge**, а не в VPN. | + | Если подсеть Docker **пересекается** с корпоративной (часто диапазон ''172.16.0.0/12'', в IEP — ''172.22.0.0/16''), Linux направляет пакеты **в Docker-bridge**, а не в VPN. |
| - | **Пример:** ''change-pass.iep.loc'' → ''172.22.224.153''. Сеть ''rpcserver_default'' заняла ''172.22.0.0/16''. Маршрут: | + | <note important> |
| + | Это **не** проблема DNS и **не** ''resolv.conf''. Имя резолвится верно; ломается **маршрутизация** на уровне ядра Linux. | ||
| + | </note> | ||
| + | |||
| + | ===== Один IP — два разных случая ===== | ||
| + | |||
| + | Типичная ловушка: **DNS и маршрут говорят разное** про один и тот же хост. Поэтому кажется, что «имя резолвится — значит всё ок», а ''curl''/браузер всё равно не открывают URL. | ||
| + | |||
| + | ==== Пример с change-pass.iep.loc ==== | ||
| + | |||
| + | Выполняем подряд: | ||
| + | |||
| + | <code bash> | ||
| + | grep -n change-pass /etc/hosts /etc/resolv.conf 2>/dev/null | ||
| + | getent hosts change-pass.iep.loc | ||
| + | ip route get 172.22.224.153 | ||
| + | </code> | ||
| + | |||
| + | Вывод **при конфликте с Docker**: | ||
| <code> | <code> | ||
| - | 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 | + | 172.22.224.153 change-pass.iep.loc |
| + | 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 uid 1891243316 | ||
| + | cache | ||
| </code> | </code> | ||
| - | Пакеты уходят в пустой Docker-bridge → ''Connection refused''. Реальный сервер в корпсети недоступен. | + | Разбор по строкам: |
| + | |||
| + | ^ Команда / строка ^ Что кажется ^ Что на самом деле ^ | ||
| + | | ''getent hosts'' → ''172.22.224.153 change-pass.iep.loc'' | DNS/hosts работает, IP «правильный» | Да, имя → IP определено верно. Это **не** проблема ''resolv.conf'' | | ||
| + | | ''ip route get'' → ''dev br-355c08cd95dd src 172.22.0.1'' | — | Пакеты к ''172.22.224.153'' уходят в **Docker-bridge**, а не в VPN/IEP | | ||
| + | | ''grep … /etc/hosts'' — пусто | Запись только в DNS | Имя может резолвиться и без ''/etc/hosts'' | | ||
| <note important> | <note important> | ||
| - | Это **не** проблема DNS и **не** ''resolv.conf''. Имя резолвится верно; ломается **маршрутизация** на уровне ядра Linux. | + | **Два случая использования одной подсети ''172.22.0.0/16'':** |
| + | |||
| + | - **Корпоративная сеть IEP** — реальный сервер ''change-pass.iep.loc'' (''172.22.224.153''), доступ через VPN (''tun0'') | ||
| + | - **Docker-сеть ''rpcserver_default''** — локальный bridge ''br-355c08cd95dd'' с адресом ''172.22.0.1/16'' | ||
| + | |||
| + | Ядро Linux не различает «корпоративный'' и «docker'' ''172.22.x.x'' — срабатывает маршрут ''172.22.0.0/16 dev br-...''. Трафик **не доходит** до сервера в IEP → ''Connection refused''. | ||
| </note> | </note> | ||
| + | |||
| + | ==== Как отличить: DNS vs маршрут ==== | ||
| + | |||
| + | ^ Проверка ^ Норма (конфликта нет) ^ Конфликт с Docker ^ | ||
| + | | ''getent hosts HOST'' | IP из корпсети (напр. ''172.22.224.153'') | То же — IP верный | | ||
| + | | ''ip route get IP'' | ''via 10.x.x.x dev tun0'' (VPN) | ''dev br-xxxxxxxxxxxx src 172.22.0.1'' | | ||
| + | | ''curl https://HOST/'' | HTTP 200 / редирект | ''Connection refused'' | | ||
| + | |||
| + | <note tip> | ||
| + | Если ''getent'' и ''curl'' расходятся по смыслу — **сначала смотрите ''ip route get''**, не ''resolv.conf''. | ||
| + | </note> | ||
| + | |||
| + | ==== После устранения конфликта ==== | ||
| + | |||
| + | <code bash> | ||
| + | docker compose down # в проекте с сетью 172.22.0.0/16 | ||
| + | ip route get 172.22.224.153 | ||
| + | </code> | ||
| + | |||
| + | Ожидаемый маршрут: | ||
| + | |||
| + | <code> | ||
| + | 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211 | ||
| + | cache | ||
| + | </code> | ||
| + | |||
| + | Тот же IP ''172.22.224.153'', но теперь трафик идёт **через VPN**, а не в Docker-bridge. | ||
| ===== Быстрая диагностика ===== | ===== Быстрая диагностика ===== | ||
| - | ==== 1. Проверить DNS ==== | + | ==== 1. DNS ==== |
| <code bash> | <code bash> | ||
| - | HOST=change-pass.iep.loc # подставьте свой хост | + | HOST=change-pass.iep.loc |
| getent hosts "$HOST" | getent hosts "$HOST" | ||
| </code> | </code> | ||
| - | Если IP получен — DNS в порядке (или запись в ''/etc/hosts''). | + | Если IP получен — резолв в порядке (DNS или ''/etc/hosts''). |
| - | ==== 2. Куда уходит маршрут ==== | + | ==== 2. Маршрут (главная проверка) ==== |
| <code bash> | <code bash> | ||
| Строка 48: | Строка 110: | ||
| </code> | </code> | ||
| - | **Плохо** (конфликт с Docker): | + | **Плохо** — трафик в Docker: |
| <code> | <code> | ||
| - | 172.22.224.153 dev br-xxxxxxxxxxxx src 172.22.0.1 | + | 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 |
| </code> | </code> | ||
| - | **Хорошо** (через VPN/корпсеть): | + | **Хорошо** — трафик через VPN: |
| <code> | <code> | ||
| Строка 60: | Строка 122: | ||
| </code> | </code> | ||
| - | ==== 3. Проверить порты ==== | + | ==== 3. Порты и HTTP ==== |
| <code bash> | <code bash> | ||
| Строка 69: | Строка 131: | ||
| </code> | </code> | ||
| - | При конфликте с Docker — ''Connection refused'' **до** VPN, даже если VPN подключён. | + | При конфликте с Docker — ''Connection refused'' **до** VPN, даже если ''tun0'' поднят. |
| ==== 4. Найти конфликтующую Docker-сеть ==== | ==== 4. Найти конфликтующую Docker-сеть ==== | ||
| <code bash> | <code bash> | ||
| - | ip route | grep 172.22 # или другая проблемная подсеть | + | ip route | grep 172.22 |
| docker network ls | docker network ls | ||
| docker network inspect $(docker network ls -q) | grep -E '"Name"|Subnet|Gateway' | docker network inspect $(docker network ls -q) | grep -E '"Name"|Subnet|Gateway' | ||
| Строка 81: | Строка 143: | ||
| Ищите сеть, у которой ''Subnet'' совпадает с диапазоном корпоративных адресов. | Ищите сеть, у которой ''Subnet'' совпадает с диапазоном корпоративных адресов. | ||
| - | **Пример из практики:** | + | **Кейс 2026-08-13:** |
| * Сеть: ''rpcserver_default'' | * Сеть: ''rpcserver_default'' | ||
| Строка 113: | Строка 175: | ||
| echo | echo | ||
| - | echo "=== Docker routes (172.16/12 overlap) ===" | + | echo "=== Docker routes (172.16/12) ===" |
| ip route | grep -E '172\.(1[6-9]|2[0-9]|3[0-1])\.' || echo "(none)" | ip route | grep -E '172\.(1[6-9]|2[0-9]|3[0-1])\.' || echo "(none)" | ||
| Строка 121: | Строка 183: | ||
| | grep -E '"Name"|Subnet|Gateway' || echo "docker not available" | | grep -E '"Name"|Subnet|Gateway' || echo "docker not available" | ||
| </file> | </file> | ||
| + | |||
| + | Запуск: | ||
| <code bash> | <code bash> | ||
| Строка 129: | Строка 193: | ||
| ===== Решение ===== | ===== Решение ===== | ||
| - | ==== Временное (сразу восстановить доступ) ==== | + | ==== Сразу восстановить доступ ==== |
| - | + | ||
| - | Остановить проект, который создал конфликтующую сеть: | + | |
| <code bash> | <code bash> | ||
| - | cd ~/projects/rpc.server # путь к compose-проекту | + | cd ~/projects/rpc.server |
| docker compose down | docker compose down | ||
| - | # или: docker-compose down | ||
| </code> | </code> | ||
| - | Проверить: | + | Проверка: |
| <code bash> | <code bash> | ||
| Строка 146: | Строка 207: | ||
| </code> | </code> | ||
| - | Маршрут должен идти через ''tun0'' / ''tun1'', не через ''br-...''. | + | Маршрут должен идти через ''tun0'', не через ''br-...''. ''curl'' — HTTP 200. |
| - | ==== Постоянное (при следующем ''docker compose up'') ==== | + | ==== Навсегда: другой subnet в compose ==== |
| - | В ''docker-compose.yml'' проекта задайте **другую** подсеть, не пересекающуюся с корпоративной: | + | В ''docker-compose.yml'' проекта задайте подсеть **вне** корпоративной: |
| <code yaml> | <code yaml> | ||
| Строка 158: | Строка 219: | ||
| config: | config: | ||
| - subnet: 192.168.220.0/24 | - subnet: 192.168.220.0/24 | ||
| - | </code> | ||
| - | |||
| - | Или для named network: | ||
| - | |||
| - | <code yaml> | ||
| - | networks: | ||
| - | app-network: | ||
| - | ipam: | ||
| - | config: | ||
| - | - subnet: 192.168.221.0/24 | ||
| </code> | </code> | ||
| Строка 174: | Строка 225: | ||
| </note> | </note> | ||
| - | ==== Глобально для всех новых Docker-сетей ==== | + | ==== Глобально: пулы для новых Docker-сетей ==== |
| ''/etc/docker/daemon.json'': | ''/etc/docker/daemon.json'': | ||
| Строка 185: | Строка 236: | ||
| } | } | ||
| </code> | </code> | ||
| + | |||
| + | Применить: | ||
| <code bash> | <code bash> | ||
| Строка 198: | Строка 251: | ||
| ^ Симптом ^ Вероятная причина ^ Что смотреть ^ | ^ Симптом ^ Вероятная причина ^ Что смотреть ^ | ||
| | DNS не резолвит | ''resolv.conf'', VPN DNS, ''/etc/hosts'' | ''getent hosts'' | | | DNS не резолвит | ''resolv.conf'', VPN DNS, ''/etc/hosts'' | ''getent hosts'' | | ||
| - | | DNS OK, route через ''br-...'' | Конфликт Docker subnet | ''ip route get'', ''docker network inspect'' | | + | | DNS OK, route через ''br-...'' | Конфликт subnet Docker | ''ip route get'', ''docker network inspect'' | |
| | Route через ''tun0'', но timeout | VPN без маршрута в сеть | ''ip route'', админы VPN | | | Route через ''tun0'', но timeout | VPN без маршрута в сеть | ''ip route'', админы VPN | | ||
| - | | Route OK, Connection refused | Сервис не слушает порт | ''nc -vz IP 443'' на стороне сервера | | + | | Route OK, Connection refused | Сервис не слушает порт | ''nc -vz IP 443'' на сервере | |
| - | | TLS verify error | Корпоративный CA | ''curl -k'' vs установка CA в систему | | + | | TLS verify error | Корпоративный CA | ''curl -k'' / установка CA в систему | |
| ===== Профилактика ===== | ===== Профилактика ===== | ||
| - Не использовать ''172.16.0.0/12'' для Docker, если работаете с IEP/RTLabs VPN | - Не использовать ''172.16.0.0/12'' для Docker, если работаете с IEP/RTLabs VPN | ||
| - | - Перед ''docker compose up'' нового проекта: ''docker network inspect ... | grep Subnet'' | + | - Перед ''docker compose up'' нового проекта: ''docker network inspect … | grep Subnet'' |
| - | - При смене машины / VPN — один раз прогнать ''ip route get <корп-хост>'' | + | - При смене машины / VPN — один раз: ''ip route get <корп-хост>'' |
| - | - Документировать subnet каждого локального compose в README проекта | + | - Subnet каждого локального compose — в README проекта |
| - | ===== Связанные команды ===== | + | ===== Полезные команды ===== |
| <code bash> | <code bash> | ||
| - | # все маршруты Docker-bridge | + | # все маршруты Docker-bridge в 172.16/12 |
| ip route | grep '^172\.' | ip route | grep '^172\.' | ||
| Строка 226: | Строка 279: | ||
| ===== История кейса ===== | ===== История кейса ===== | ||
| - | **Дата:** 2026-08-13 | + | * **Дата:** 2026-08-13 |
| - | + | * **Хост:** ''change-pass.iep.loc'' → ''172.22.224.153'' | |
| - | **Хост:** ''change-pass.iep.loc'' → ''172.22.224.153'' | + | * **Причина:** Docker-сеть ''rpcserver_default'' (''172.22.0.0/16'') перехватывала маршрут |
| - | + | * **Решение:** ''docker compose down'' в ''rpc.server'' + subnet ''192.168.220.0/24'' в compose | |
| - | **Причина:** Docker-сеть ''rpcserver_default'' с ''172.22.0.0/16'' перехватывала маршрут. | + | * **Результат:** ''ip route get'' → ''via 10.5.192.1 dev tun0'', ''curl'' → HTTP 200 |
| - | + | ||
| - | **Решение:** ''docker compose down'' в ''rpc.server'' + смена subnet в compose на ''192.168.220.0/24''. | + | |
| - | + | ||
| - | **Результат:** ''ip route get'' → ''via 10.5.192.1 dev tun0'', ''curl'' → HTTP 200. | + | |