Инструменты пользователя

Инструменты сайта


linux:docker_network_conflict

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
linux:docker_network_conflict [2026/08/13 08:02]
werwolf
linux:docker_network_conflict [2026/08/13 08:03] (текущий)
werwolf
Строка 1: Строка 1:
 ====== Конфликт сети Docker с корпоративной / VPN-сетью ====== ====== Конфликт сети Docker с корпоративной / VPN-сетью ======
  
-**Страница:​** ''​dev:​docker_network_conflict''​ **Когда смотреть:​** внутренний сервис не открывается,​ DNS «в порядке»,​ VPN подключён,​ недавно поднимали Docker.+<note tip> 
 +**Страница:​** ''​dev:​docker_network_conflict'' ​  
 +**Когда смотреть:​** внутренний сервис не открывается,​ DNS «в порядке»,​ VPN подключён,​ недавно поднимали Docker. 
 +</​note>​ 
 ===== Симптомы ===== ===== Симптомы =====
  
-Не открывается внутренний сервис (Jira, LDAP, ''​change-pass.iep.loc'',​ git и т.п.) — ни в браузере,​ ни в ''​curl''​ +  * Не открывается внутренний сервис (Jira, LDAP, ''​change-pass.iep.loc'',​ git и т.п.) — ни в браузере,​ ни в ''​curl''​ 
-Ошибка:​ ''​Connection refused''​ или ''​Failed to connect''​ на портах 80/443 +  ​* ​Ошибка:​ ''​Connection refused''​ или ''​Failed to connect''​ на портах 80/443 
-''​getent hosts hostname''​ возвращает ожидаемый IP +  ​* ​''​getent hosts hostname''​ возвращает ​**ожидаемый** IP 
-Проблема часто появляется после ''​docker compose up''​ или когда на машине много compose-проектов +  ​* ​Проблема часто появляется после ''​docker compose up''​ или когда на машине много compose-проектов 
-VPN подключён (''​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 
-''​172.22.0.0/​16''​ — сеть проекта ''​rpcserver_default''​ +  ​* ​''​172.22.0.0/​16''​ — сеть проекта ''​rpcserver_default''​ 
-''​172.18.0.0/​16''​ — ''​status_app-network''​ +  ​* ​''​172.18.0.0/​16''​ — ''​status_app-network''​ 
-Если подсеть Docker пересекается с корпоративной (часто диапазон ''​172.16.0.0/​12'',​ в IEP — ''​172.22.0.0/​16''​),​ Linux направляет пакеты в Docker-bridge,​ а не в VPN.+ 
 +Если подсеть Docker ​**пересекается** с корпоративной (часто диапазон ''​172.16.0.0/​12'',​ в IEP — ''​172.22.0.0/​16''​),​ Linux направляет пакеты ​**в Docker-bridge**, а не в VPN. 
 + 
 +<note important>​ 
 +Это **не** проблема DNS и **не** ''​resolv.conf''​. Имя резолвится верно; ломается **маршрутизация** на уровне ядра Linux. 
 +</​note>​
  
-Это **не** проблема DNS и **не** ''​resolv.conf''​. Имя резолвится верно; ломается **маршрутизация** на уровне ядра. 
 ===== Один IP — два разных случая ===== ===== Один IP — два разных случая =====
  
-Типичная ловушка:​ DNS и маршрут говорят разное про один хост. ​Кажется,​ что «имя резолвится — значит всё ок», а ''​curl'' ​и браузер ​URL не открывают.+Типичная ловушка: ​**DNS и маршрут говорят разное** про один ​и тот же хост. ​Поэтому кажется,​ что «имя резолвится — значит всё ок», а ''​curl''​/браузер ​всё равно ​не открывают ​URL. 
 + 
 +==== Пример с change-pass.iep.loc ====
  
-==== Пример: change-pass.iep.loc ====+Выполняем подряд:
  
 +<code bash>
 grep -n change-pass /etc/hosts /​etc/​resolv.conf 2>/​dev/​null grep -n change-pass /etc/hosts /​etc/​resolv.conf 2>/​dev/​null
 getent hosts change-pass.iep.loc getent hosts change-pass.iep.loc
 ip route get 172.22.224.153 ip route get 172.22.224.153
-При конфликте с Docker вывод такой:+</​code>​
  
 +Вывод **при конфликте с Docker**:
 +
 +<​code>​
 172.22.224.153 ​ change-pass.iep.loc 172.22.224.153 ​ change-pass.iep.loc
 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 uid 1891243316 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 uid 1891243316
     cache     cache
-Разбор:​+</​code>​
  
-^ Строка / команда ^ Что кажется ^ Что на самом деле ^ | ''​getent hosts''​ → ''​172.22.224.153 change-pass.iep.loc''​ | DNS/hosts работает | Имя → IP верно. ''​resolv.conf''​ тут ни при чём | | ''​ip route get''​ → ''​dev br-355c08cd95dd src 172.22.0.1''​ | — | Пакеты уходят в Docker-bridge,​ не в VPN/IEP | | ''​grep … /​etc/​hosts''​ — пусто | — | Имя ​резолвится через DNS, без записи в ''/​etc/​hosts''​ |+Разбор по строкам:
  
-Одна ​подсеть ''​172.22.0.0/16''​ — два назначения:+^ Команда / строка ^ Что кажется ^ Что ​на самом деле ^ 
 +| ''​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''​ |
  
-Корпоративная IEP — сервер ''​change-pass.iep.loc''​ (''​172.22.224.153''​),​ доступ через VPN (''​tun0''​) +<note important>​ 
-Docker ''​rpcserver_default''​ — bridge ''​br-355c08cd95dd'', ​адрес ''​172.22.0.1/​16''​ +**Два случая использования одной подсети ''​172.22.0.0/​16''​:**
-Ядро не различает «корпоративный''​ и «docker''​ ''​172.22.x.x''​. Срабатывает маршрут ​''​172.22.0.0/​16 ​dev br-...'' ​→ трафик не доходит до IEP → ''​Connection refused''​.+
  
-==== DNS vs маршрут — сводка ​====+  - **Корпоративная сеть IEP** — реальный ​сервер ''​change-pass.iep.loc''​ (''​172.22.224.153''​), ​доступ через VPN (''​tun0''​) 
 +  - **Docker-сеть ''​rpcserver_default''​** — локальный bridge ''​br-355c08cd95dd''​ с адресом ''​172.22.0.1/​16''​
  
-^ Проверка ^ Норма ^ Конфликт с Docker ^ | ''​getent hosts HOST''​ | IP из корпсети | То же — IP верный | | ''​ip route get IP''​ | ''​via 10.x.x.x dev tun0''​ | ''​dev br-... src 172.22.0.1''​ | | ''​curl https://​HOST/''​ | HTTP 200 | ''​Connection refused''​ |+Ядро Linux не различает «корпоративный''​ и «docker''​ ''​172.22.x.x''​ — срабатывает маршрут ''​172.22.0.0/​16 dev br-...''​. Трафик **не доходит** до сервера в IEP → ''​Connection refused''​. 
 +</​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>​
  
-Если ''​getent''​ «работает», а ''​curl''​ — нет, **сначала ''​ip route get''​**,​ не ''​resolv.conf''​. +Ожидаемый маршрут:​
-После устранения конфликта тот же IP, другой маршрут:​+
  
 +<​code>​
 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211
     cache     cache
 +</​code>​
 +
 +Тот же IP ''​172.22.224.153'',​ но теперь трафик идёт **через VPN**, а не в Docker-bridge.
 +
 ===== Быстрая диагностика ===== ===== Быстрая диагностика =====
  
 ==== 1. DNS ==== ==== 1. DNS ====
  
 +<code bash>
 HOST=change-pass.iep.loc HOST=change-pass.iep.loc
 getent hosts "​$HOST"​ getent hosts "​$HOST"​
-IP получен — резолв в порядке (DNS или ''/​etc/​hosts''​).+</​code>​ 
 + 
 +Если ​IP получен — резолв в порядке (DNS или ''/​etc/​hosts''​).
  
 ==== 2. Маршрут (главная проверка) ==== ==== 2. Маршрут (главная проверка) ====
  
 +<code bash>
 ip route get $(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​) ip route get $(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​)
-Плохо:+</​code>​ 
 + 
 +**Плохо** — трафик в Docker:
  
 +<​code>​
 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1
-Хорошо:​+</​code>​
  
 +**Хорошо** — трафик через VPN:
 +
 +<​code>​
 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211
 +</​code>​
 +
 ==== 3. Порты и HTTP ==== ==== 3. Порты и HTTP ====
  
 +<code bash>
 IP=$(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​) IP=$(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​)
 nc -vz "​$IP"​ 80 nc -vz "​$IP"​ 80
 nc -vz "​$IP"​ 443 nc -vz "​$IP"​ 443
 curl -vk --connect-timeout 5 "​https://​$HOST/"​ curl -vk --connect-timeout 5 "​https://​$HOST/"​
-При конфликте с Docker — ''​Connection refused''​ до VPN, даже если ''​tun0''​ поднят.+</​code>​ 
 + 
 +При конфликте с Docker — ''​Connection refused'' ​**до** VPN, даже если ''​tun0''​ поднят.
  
-==== 4. Конфликтующая Docker-сеть ====+==== 4. Найти конфликтующую Docker-сеть ====
  
 +<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'​
-Ищите ''​Subnet'',​ совпадающий с корпоративным диапазоном.+</​code>​
  
-Кейс 2026-08-13:+Ищите сеть, у которой ''​Subnet''​ совпадает с диапазоном корпоративных адресов. 
 + 
 +**Кейс 2026-08-13:** 
 + 
 +  * Сеть: ''​rpcserver_default''​ 
 +  * Subnet: ''​172.22.0.0/​16''​ 
 +  * Bridge: ''​br-355c08cd95dd''​ 
 +  * Конфликт с: ''​change-pass.iep.loc''​ (''​172.22.224.153''​)
  
-Сеть: ''​rpcserver_default''​ 
-Subnet: ''​172.22.0.0/​16''​ 
-Bridge: ''​br-355c08cd95dd''​ 
-Конфликт с: ''​change-pass.iep.loc''​ (''​172.22.224.153''​) 
 ===== Скрипт проверки ===== ===== Скрипт проверки =====
  
-#​!/​usr/​bin/​env bash set -u+Сохраните как ''​check-docker-network-conflict.sh'':​ 
 + 
 +<file bash check-docker-network-conflict.sh>​ 
 +#​!/​usr/​bin/​env bash 
 +set -u 
 HOST="​${1:​-change-pass.iep.loc}"​ HOST="​${1:​-change-pass.iep.loc}"​
  
-echo "=== DNS ===" getent hosts "​$HOST"​ || { echo "DNS fail"; exit 1; } IP=$(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​) echo "​IP=$IP"​+echo "=== DNS ===" 
 +getent hosts "​$HOST"​ || { echo "DNS fail"; exit 1; } 
 +IP=$(getent hosts "​$HOST"​ | awk '​{print $1; exit}'​) 
 +echo "IP=$IP" 
 + 
 +echo 
 +echo "=== Route ===" 
 +ip route get "$IP"
  
-echo echo "​=== ​Route ===" ​ip route get "​$IP"​+echo 
 +echo "​=== ​TCP 80/​443 ​===" 
 +nc -vz "​$IP" ​80  2>&1 || true 
 +nc -vz "​$IP"​ 443 2>&1 || true
  
-echo echo "​=== ​TCP 80/443 ===" ​nc -vz "​$IP"​ 80 2>&1 || true nc -vz "​$IP"​ 443 2>&1 || true+echo 
 +echo "​=== ​Docker routes (172.16/12) ===" 
 +ip route | grep -E '​172\.(1[6-9]|2[0-9]|3[0-1])\.' ​|| echo "​(none)"​
  
-echo echo "=== Docker ​routes (172.16/​12) ​===" ​ip route | grep -E '172.(1[6-9]|2[0-9]|3[0-1]).' || echo "(none)"+echo 
 +echo "=== Docker ​networks ​===" 
 +docker network inspect $(docker network ls -q) 2>/​dev/​null \ 
 +  ​| grep -E '"​Name"​|Subnet|Gateway' || echo "docker not available" 
 +</​file>​
  
-echo echo "=== Docker networks ===" docker network inspect $(docker network ls -q) 2>/​dev/​null +Запуск:​
-| grep -E '"​Name"​|Subnet|Gateway'​ || echo "​docker not available"​+
  
 +<code bash>
 chmod +x check-docker-network-conflict.sh chmod +x check-docker-network-conflict.sh
 ./​check-docker-network-conflict.sh change-pass.iep.loc ./​check-docker-network-conflict.sh change-pass.iep.loc
 +</​code>​
 +
 ===== Решение ===== ===== Решение =====
  
 ==== Сразу восстановить доступ ==== ==== Сразу восстановить доступ ====
  
 +<code bash>
 cd ~/​projects/​rpc.server cd ~/​projects/​rpc.server
 docker compose down docker compose down
 +</​code>​
 +
 Проверка:​ Проверка:​
  
 +<code bash>
 ip route get 172.22.224.153 ip route get 172.22.224.153
 curl -vk https://​change-pass.iep.loc/​ curl -vk https://​change-pass.iep.loc/​
-Маршрут ​— через ''​tun0'',​ не ''​br-...''​. ''​curl''​ — HTTP 200.+</​code>​ 
 + 
 +Маршрут ​должен идти ​через ''​tun0'',​ не через ​''​br-...''​. ''​curl''​ — HTTP 200.
  
 ==== Навсегда:​ другой subnet в compose ==== ==== Навсегда:​ другой subnet в compose ====
  
 +В ''​docker-compose.yml''​ проекта задайте подсеть **вне** корпоративной:​
 +
 +<code yaml>
 networks: networks:
   default:   default:
Строка 127: Строка 219:
       config:       config:
         - subnet: 192.168.220.0/​24         - subnet: 192.168.220.0/​24
-Берите диапазоны ​**вне** ''​172.16.0.0/​12'',​ если VPN/IEP использует ''​172.18–172.31''​. ​Примеры: ''​192.168.200.0/​24'',​ ''​192.168.220.0/​24''​. +</​code>​ 
-==== Глобально:​ пулы для новых сетей ​Docker ​====+ 
 +<note tip> 
 +Выбирайте диапазоны вне ''​172.16.0.0/​12'' ​(172.16–172.31), если VPN/IEP использует ''​172.22.x''​, ''​172.18.x'' ​и т.п. Удобные варианты: ''​192.168.200.0/​24'',​ ''​192.168.220.0/​24''​. 
 +</​note>​ 
 + 
 +==== Глобально:​ пулы для новых ​Docker-сетей ====
  
 ''/​etc/​docker/​daemon.json'':​ ''/​etc/​docker/​daemon.json'':​
  
 +<code json>
 { {
   "​default-address-pools":​ [   "​default-address-pools":​ [
Строка 137: Строка 235:
   ]   ]
 } }
 +</​code>​
 +
 +Применить:​
 +
 +<code bash>
 sudo systemctl restart docker sudo systemctl restart docker
-После смены пулов пересоздайте compose-проекты (''​docker compose down && docker compose up -d''​). Старые сети с ''​172.22.0.0/​16''​ останутся,​ пока их не удалите.+</​code>​ 
 + 
 +<note warning>​ 
 +После смены пулов ​**пересоздайте** существующие compose-проекты (''​docker compose down && docker compose up -d''​). Старые сети с ''​172.22.0.0/​16''​ останутся,​ пока их не удалите. 
 +</​note>​ 
 ===== Таблица:​ что проверять ===== ===== Таблица:​ что проверять =====
  
-^ Симптом ^ Причина ^ Действие ^ | DNS не резолвит | ''​resolv.conf'',​ VPN DNS, ''/​etc/​hosts''​ | ''​getent hosts''​ | | DNS OK, route через ''​br-...''​ | Конфликт subnet Docker | ''​ip route get'',​ ''​docker network inspect''​ | | Route через ''​tun0'',​ timeout | VPN без маршрута в сеть | ''​ip route'',​ админы VPN | | Route OK, Connection refused | Сервис не слушает порт | ''​nc -vz''​ на сервере | | TLS verify error | Корпоративный CA | ''​curl -k''​ / установка CA |+^ Симптом ^ Вероятная причина ^ Что ​смотреть ^ 
 +| DNS не резолвит | ''​resolv.conf'',​ VPN DNS, ''/​etc/​hosts''​ | ''​getent hosts''​ | 
 +| DNS OK, route через ''​br-...''​ | Конфликт subnet Docker | ''​ip route get'',​ ''​docker network inspect''​ | 
 +| Route через ''​tun0'', ​но timeout | VPN без маршрута в сеть | ''​ip route'',​ админы VPN | 
 +| Route OK, Connection refused | Сервис не слушает порт | ''​nc -vz IP 443''​ на сервере | 
 +| 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>
 +# все маршруты Docker-bridge в 172.16/12
 ip route | grep '​^172\.'​ ip route | grep '​^172\.'​
 +
 +# какой bridge за какой сетью
 docker network ls docker network ls
 docker network inspect rpcserver_default docker network inspect rpcserver_default
-docker network rm rpcserver_default ​  # если compose уже down+ 
 +удалить «висячую» сеть (если compose уже down
 +docker network rm rpcserver_default 
 +</​code>​ 
 ===== История кейса ===== ===== История кейса =====
  
-Дата: 2026-08-13 +  * **Дата:** 2026-08-13 
-Хост: ''​change-pass.iep.loc''​ → ''​172.22.224.153''​ +  * **Хост:** ''​change-pass.iep.loc''​ → ''​172.22.224.153''​ 
-Причина:​ ''​rpcserver_default''​ (''​172.22.0.0/​16''​) перехватывала маршрут +  * **Причина:​** Docker-сеть ​''​rpcserver_default''​ (''​172.22.0.0/​16''​) перехватывала маршрут 
-Решение:​ ''​docker compose down''​ + subnet ''​192.168.220.0/​24''​ в compose +  * **Решение:​** ''​docker compose down''​ в ''​rpc.server''​ + subnet ''​192.168.220.0/​24''​ в compose 
-Результат:​ route via ''​tun0'',​ ''​curl''​ → HTTP 200 +  * **Результат:​** ''​ip ​route get'' ​→ ''​via 10.5.192.1 dev tun0'',​ ''​curl''​ → HTTP 200
-Структура:​ симптомы → суть → пример с двумя случаями → пошаговая диагностика → скрипт → решение → таблица → профилактика → кейс. Блок про ''​getent''​ vs ''​ip route get''​ стоит сразу после «Суть проблемы»,​ перед пошаговыми командами — логика «сначала понять ловушку,​ потом чинить».+
linux/docker_network_conflict.1786608127.txt.gz · Последние изменения: 2026/08/13 08:02 — werwolf