— Когда контейнер ищет внешний сервер у себя под носом —
TL;DR
Разбираемся, куда пропадают пакеты, при чём тут маршрутизация и как закончить войну за IP-адреса.
Кто‑то скажет — сделал из мухи слона! Да у меня каждый день такие приключения! Но для меня получился интересный кейс, и я лучше зафиксирую его для потомков.
Случилось страшное – из
docker-контейнера недоступен удалённый хост. При этом с хост-машины никаких проблем с доступность нет – что надо пингуется, что пингуется, то роутится. Хост для определённости - 172.19.10.50.WTF?!
Диагностика. Раунд первый
Для начала проверяем подсети, которые создал
docker:
docker network inspect $(docker network ls -q) \
--format '{{.Name}}: {{range .IPAM.Config}}{{.Subnet}} {{end}}'
В моём случае обнаружились:
bridge: 172.17.0.0/16
traefik: 172.19.0.0/16
app-backend: 172.23.0.0/16
app-notifications: 172.29.0.0/16
В целом, картина уже ясна – видим пересечение сетей
docker и внешнего хоста. Для пущей убедительности проверяем маршрут в сетевом пространстве
docker:
docker run --rm --network host \
nicolaka/netshoot \
ip route get 172.19.10.50
Бинго!
172.19.10.50 dev br-caf585119744 src 172.19.0.1
Cреда
docker считает, что внешний сервер находится непосредственно в локальной сети.Первое лечение. Неудачное
Перенёс сеть
traefik в другой диапазон – 10.250.10.0/24, предварительно убедившись, что подсеть не используется где-то ещё.
networks:
traefik:
name: traefik
driver: bridge
ipam:
config:
- subnet: 10.250.10.0/24
Тут надо сказать, что
app-backend и app-notifications используют общие external-сети. Поэтому сети создавались отдельно, а не средствами docker compose.
docker network create app-backend
docker network create app-notifications
Остановил использующие сеть проекты, удалил старые сети, поднял сети заново.
И… внешний сервер снова недоступен.
WTF?!
Диагностика. Раунд второй
Смотрим подсети ещё раз:
traefik: 10.250.10.0/24
app-backend: 172.18.0.0/16
app-notifications: 172.19.0.0/16
Постоянству
docker можно позавидовать – не отпускает подсеть 172.19.0.0/16.Лечение. Теперь окончательное
После повторного конфликта стало понятно: доверять
docker автоматическое распределение адресов конкретно в этом окружении — идея так себе. И поэтому сети создаём с явным указанием подсетей:
docker network create \
--driver bridge \
--subnet 10.250.11.0/24 \
app-backend
docker network create \
--driver bridge \
--subnet 10.250.12.0/24 \
app-notifications
Проверяем итоговую адресацию:
docker network inspect traefik app-backend app-notifications \
--format '{{.Name}}: {{range .IPAM.Config}}{{.Subnet}}{{end}}'
Получаем:
traefik: 10.250.10.0/24
app-backend: 10.250.11.0/24
app-notifications: 10.250.12.0/24
Проверяем доступность внешнего сервера:
docker run --rm \
--network container:notifications-php \
nicolaka/netshoot \
ping -c 3 172.19.10.50
И вот теперь всё заработало как надо!
Для кого-то это база, а для меня кирпичик опыта!
Полезное открытие: nicolaka/netshoot
У минималистичных
docker-образов, таких как alpine, debian:slim часто отсутствуют даже базовые сетевые утилиты: ping, curl, nslookup, ip. Когда возникает необходимость разобраться с сетевыми проблемами, то удобнее использовать образ nicolaka/netshoot с целым арсеналом сетевых инструментов.Но самое интересное — возможность подключиться к сетевому пространству уже работающего контейнера:
docker run --rm -it \
--network container:notifications-php \
nicolaka/netshoot
Теперь мы находимся в отдельном диагностическом контейнере, но используем сетевые интерфейсы, IP-адреса и маршруты
notifications-php.При этом исходный контейнер не пересобирается, а после выхода диагностический контейнер автоматически удалится благодаря
--rm.Отличная DevOps-выручалочка. Крайне рекомендую.
#docker #dockercompose #networking #devops