Маршрутизация в Docker держится на трёх элементах: виртуальный мост (docker0 или созданный вами bridge), пара veth-интерфейсов и таблица маршрутизации хоста вместе с правилами NAT в iptables. Контейнер получает адрес из подсети моста, шлюзом по умолчанию становится адрес моста на хосте, а исходящий трафик подменяется на адрес хоста правилом MASQUERADE в цепочке POSTROUTING.
Если контейнер не видит внешнюю сеть, проверяйте по одному порядку: ip route внутри контейнера, затем ip route на хосте, затем iptables NAT и параметр net.ipv4.ip_forward. Такой порядок отсекает большинство причин за несколько команд, без перезапуска контейнеров и без правки рабочей конфигурации наугад.
Дальше: пошаговая схема построения маршрутов, команды для вывода таблиц маршрутизации внутри контейнера и на хосте, разбор трёх типовых сбоев и исправления через docker network, цепочку DOCKER-USER и daemon.json.
Как Docker строит маршрутизацию: bridge, veth и таблицы маршрутизации
Демон Docker поднимает bridge docker0 и использует его как шлюз для контейнеров, запущенных без явного указания сети. Адрес моста по умолчанию 172.17.0.1/16, подсеть 172.17.0.0/16. Каждый контейнер получает адрес из этой подсети и собственный сетевой namespace с интерфейсом eth0.
Что происходит при запуске контейнера: пошаговая схема
- Docker создаёт veth-пару: два связанных виртуальных интерфейса, где пакет, отправленный в один конец, выходит из второго.
- Один конец остаётся в network namespace хоста и получает имя вида veth3f1a9c2, второй переносится в namespace контейнера и переименовывается в eth0.
- В namespace контейнера назначается адрес из подсети bridge, например 172.17.0.2/16.
- Интерфейсы поднимаются, в таблице маршрутизации контейнера появляется запись default via 172.17.0.1 dev eth0.
- На хосте Docker добавляет маршрут до подсети контейнеров через docker0 и правила NAT для исходящего трафика.
Связку удобно проверить сразу после запуска контейнера:
ip link show type bridge ip link show type veth docker network inspect bridge
Вывод docker network inspect bridge содержит подсеть, шлюз и список подключённых контейнеров с их адресами. Этот срез данных нужен до любых изменений: по нему видно, какой адрес ожидать в контейнере и совпадает ли подсеть с адресацией вашей локальной сети.
В Docker Compose схема та же, меняется владелец моста. Compose создаёт отдельную bridge-сеть с именем проекта, например myapp_default, и подключает к ней все сервисы из файла. Сравнение bridge, host, overlay и macvlan с примерами проброса портов приведено в руководстве по типам сетей Docker.
Где хранятся маршруты и правила: таблицы маршрутизации и iptables
Маршруты хоста показывает ip route, трансляцию адресов и фильтрацию — iptables. Docker управляет цепочками DOCKER, DOCKER-USER, DOCKER-ISOLATION-STAGE-1 и DOCKER-ISOLATION-STAGE-2 в таблице filter, а также правилами MASQUERADE в POSTROUTING таблицы nat.
ip route iptables -t nat -L -n -v iptables -L -n -v sysctl net.ipv4.ip_forward
Значение net.ipv4.ip_forward = 1 обязательно: без пересылки пакетов трафик из контейнера не пойдёт дальше хоста. Docker включает параметр при старте, но сторонние скрипты усиления безопасности иногда его сбрасывают.
Правила в цепочках DOCKER и DOCKER-ISOLATION-STAGE Docker пересоздаёт при перезапуске демона и при подключении контейнеров. Правки там не сохраняются, а конфликты с логикой Docker приводят к тому, что после рестарта сервис перестаёт работать. Собственные правила размещают в DOCKER-USER: эту цепочку Docker не изменяет.
Набор таблиц зависит от backend. В системах встречаются iptables-legacy, iptables-nft и nftables, а вывод команд отличается. Перед диагностикой сверьте docker info и версию Docker Engine, чтобы понимать, какие правила обслуживают трафик контейнеров.
Как посмотреть таблицы маршрутизации внутри контейнера и на хосте
Сбор данных занимает две команды на стороне контейнера и три на стороне хоста. Начинайте с контейнера: так вы сразу отделите проблему внутри namespace от проблемы хоста.
Команды для контейнера: ip route, ip addr, resolv.conf
docker exec -it myapp ip route docker exec -it myapp ip addr show eth0 docker exec -it myapp cat /etc/resolv.conf
Ожидаемый вывод ip route: строка default via 172.17.0.1 dev eth0 и строка 172.17.0.0/16 dev eth0 scope link src 172.17.0.2. Отсутствие строки default означает, что шлюз контейнеру неизвестен и за пределы своей подсети трафик не уйдёт. Это самая частая причина ситуации «контейнер не видит внешнюю сеть».
В пользовательской сети файл /etc/resolv.conf содержит nameserver 127.0.0.11: встроенный DNS Docker, который разрешает имена контейнеров и сервисов. В сети bridge по умолчанию адрес 127.0.0.11 не используется, и в файл попадают DNS-серверы хоста.
В минимальных образах (alpine, distroless, scratch) утилиты ip может не быть. Тогда читают /proc/net/route либо заходят в namespace контейнера с хоста:
docker exec -it myapp cat /proc/net/route
nsenter -t $(docker inspect -f '{{.State.Pid}}' myapp) -n ip route
Команды для хоста: ip route, bridge, iptables
ip route show ip addr show docker0 bridge link show iptables -t nat -L POSTROUTING -n -v
В выводе ip route show ищите строку вида 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1. Она подтверждает, что трафик к контейнерам уходит в мост. В POSTROUTING должна присутствовать запись MASQUERADE для подсети контейнеров: она подменяет исходный адрес контейнера на адрес хоста.
Признак конфликта подсетей: маршрут до подсети контейнеров указывает не на docker0, а на физический интерфейс или VPN-туннель. Хост в этом случае пытается отправить трафик к контейнеру во внешнюю сеть и теряет пакеты. Разбор интерпретации таких выводов с примерами собран в шпаргалке по диагностике сетевых проблем Docker и Kubernetes.
Типовые сбои маршрутизации в Docker и как их диагностировать
Проверка идёт в одном порядке: ip route внутри контейнера, ip route на хосте, iptables NAT, параметр ip_forward. Три сбоя ниже покрывают основную часть обращений по сетевой связности контейнеров.
Контейнер не видит внешнюю сеть
Симптом: ping 8.8.8.8 из контейнера не проходит, при этом ping соседнего контейнера по подсети работает. Диагностика по шагам:
- docker exec -it myapp ip route: есть ли маршрут default.
- ip route show на хосте: есть ли маршрут до подсети контейнера через docker0.
- iptables -t nat -L POSTROUTING -n -v: есть ли MASQUERADE для этой подсети.
- sysctl net.ipv4.ip_forward: значение 1.
- iptables -L FORWARD -n -v: нет ли запрета для трафика из подсети контейнеров.
Причины: default route отсутствует или удалён; пересылка пакетов выключена; правила firewall на хосте блокируют FORWARD. Перезапуск демона командой systemctl restart docker возвращает штатные правила Docker, а собственные ограничения переносите в DOCKER-USER, чтобы их не затирало.
Конфликт подсетей между Docker и локальной сетью
Симптом: контейнер не достаёт часть локальных адресов, либо хост теряет связь с удалённой подсетью после запуска контейнеров. Причина — пересечение подсети Docker (по умолчанию 172.17.0.0/16) с офисной, дата-центровой или VPN-адресацией.
docker network inspect bridge ip route show
Сравните подсеть из docker network inspect с таблицей маршрутизации хоста и списком подсетей компании. Если адреса пересекаются, маршрут до конфликтующей сети будет уходить не в docker0, и соединения станут непредсказуемыми.
Исправление: смена подсети на уровне демона через /etc/docker/daemon.json, перезапуск Docker и пересоздание сетей вместе с контейнерами. Смена подсети затрагивает все контейнеры на хосте, поэтому планируйте окно обслуживания и предупредите коллег.
Хост недоступен из контейнера
Симптом: внешняя сеть работает, а адрес хоста не пингуется или порт на хосте не открывается. Причины: правила INPUT блокируют трафик из подсети контейнеров; сервис на хосте слушает 127.0.0.1 вместо 0.0.0.0; адрес шлюза bridge недоступен из namespace.
docker exec -it myapp ip route docker exec -it myapp ping -c 2 172.17.0.1 ss -lntp | grep 8080 iptables -L INPUT -n -v
Если сервис слушает только loopback, поменяйте bind-адрес на 0.0.0.0 либо оставьте loopback и публикуйте порт через -p при запуске контейнера. Трафик из подсети контейнеров разрешают правилом в DOCKER-USER или в цепочке INPUT.
Имя host.docker.internal работает на Docker Desktop для macOS и Windows. На Linux оно по умолчанию не резолвится: вместо него используют адрес шлюза bridge (например, 172.17.0.1) или запись в extra_hosts.
| Симптом | Первая проверка | Вероятная причина |
|---|---|---|
| Нет доступа во внешнюю сеть | ip route внутри контейнера | Нет default route, выключен ip_forward, блокировка FORWARD |
| Недоступна часть локальных адресов | ip route show на хосте | Пересечение подсети Docker с локальной или VPN-сетью |
| Хост не отвечает из контейнера | ss -lntp и iptables -L INPUT | Firewall блокирует подсеть контейнеров, сервис слушает 127.0.0.1 |
Как исправить маршрутизацию через docker network и iptables
Порядок действий: создать пользовательскую сеть с явной подсетью, подключить контейнеры, при необходимости разрешить трафик правилом в DOCKER-USER, при системном конфликте сменить подсеть демона и пересоздать сети.
Создание и настройка пользовательской сети Docker
docker network create --driver bridge --subnet 10.10.0.0/24 --gateway 10.10.0.1 mynet docker run -d --name myapp --network mynet nginx docker network inspect mynet docker exec -it myapp ip route
Пользовательская сеть даёт две вещи: явную подсеть, которую легко проверить на пересечение с локальной адресацией, и встроенный DNS 127.0.0.11 для разрешения имён контейнеров. Назначение статических адресов в Compose и прямая маршрутизация с хоста описаны в статье о продвинутой маршрутизации в Docker и Compose.
Правка iptables: DOCKER-USER и NAT
iptables -I DOCKER-USER -s 172.17.0.0/16 -d 192.168.1.0/24 -j ACCEPT iptables -L DOCKER-USER -n -v iptables -t nat -L POSTROUTING -n -v
Цепочка DOCKER-USER обрабатывается раньше цепочек Docker, поэтому правило действует до перезапуска демона и не мешает трансляции адресов. После перезагрузки хоста правила iptables не сохраняются: для постоянного хранения нужен пакет iptables-persistent или свой systemd-юнит со скриптом восстановления.
В цепочки DOCKER и в правило MASQUERADE POSTROUTING правки не вносите: Docker пересоздаёт их, а ручные изменения нарушают работу проброса портов и изоляции между сетями.
Изменение подсети Docker через daemon.json
{
"bip": "10.20.0.1/24",
"default-address-pools": [
{"base": "10.30.0.0/16", "size": 24}
]
}
Параметр bip задаёт адрес и маску docker0, а default-address-pools определяет диапазон, из которого Docker выделяет подсети пользовательским сетям. После правки файла: systemctl restart docker, затем остановите и пересоздайте контейнеры и сети. Проверка результата: ip addr show docker0 и docker network inspect для нужной сети.
Маршрутизация между контейнерами в Docker Compose
Как Compose создаёт сети и DNS между сервисами
Compose создаёт bridge-сеть с именем проекта и подключает к ней все сервисы файла. Внутри сети работает встроенный DNS 127.0.0.11: сервис web находит базу по имени db, а алиасы из секции networks добавляют дополнительные имена.
docker compose up -d docker network ls docker compose exec web getent hosts db
Если сервисы подключены к разным сетям, разрешение имён работает только в пределах общих сетей. Отсутствие ответа от getent hosts указывает на то, что сервисы не пересекаются ни в одной сети.
Настройка networks в docker-compose.yml
services:
web:
image: nginx
networks:
- frontend
db:
image: postgres
networks:
- backend
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 10.40.0.0/24
backend:
driver: bridge
ipam:
config:
- subnet: 10.50.0.0/24
Явная подсеть убирает случайные пересечения с офисной и VPN-адресацией, а разделение на frontend и backend ограничивает доступ сервисов друг к другу. Меняйте подсеть до первого запуска: Docker не позволяет переназначить подсеть у существующей сети, её придётся удалить вместе с контейнерами командой docker compose down.
Особенности маршрутизации в Kubernetes: чем отличается от Docker
В Kubernetes маршрутизацию pod'ов обеспечивает CNI-плагин (например, Calico), а не bridge docker0. Порядок развёртывания кластера kubeadm на Ubuntu 22.04 LTS с проверкой сетевых параметров описан в инструкции по развёртыванию кластера Kubernetes.
Типовые ошибки CIDR и портов при развёртывании
- CIDR в custom-resources.yaml Calico должен совпадать с CIDR, заданным при kubeadm init: значение по умолчанию у оператора Tigera с ним не совпадает.
- Для связи между нодами должны быть открыты порты: 6443 (Kubernetes API server), 2379-2380 (etcd client и peer), 10250 (kubelet API), 10259 (scheduler), 10257 (controller-manager), 30000-32767 (NodePort Services).
- Swap отключают на всех нодах.
- Модули ядра overlay и br_netfilter загружают на каждой ноде.
- kubelet и containerd используют один cgroup driver: SystemdCgroup = true в /etc/containerd/config.toml, иначе кластер не инициализируется.
- Параметр --apiserver-advertise-address при kubeadm init обязателен, если на ноде несколько сетевых интерфейсов: без него kubeadm выберет не тот адрес.
До инициализации требуется полная сетевая связность между всеми нодами напрямую или через VPN и туннель, а проверку конфигурации удобно начинать с kubeadm config print и kubectl get nodes. Подробности по этим шагам приведены в материале по развёртыванию kubeadm. Перечисленные пункты относятся к Kubernetes и не переносятся на отдельные контейнеры Docker Engine: там за связность отвечают bridge, veth и правила NAT.
Чек-лист диагностики и исправления маршрутизации в Docker
- ip route внутри контейнера: проверьте наличие строки default со шлюзом из подсети вашей сети.
- ip addr show eth0 внутри контейнера: адрес должен принадлежать подсети сети Docker.
- ip route show на хосте: маршрут до подсети контейнеров должен идти через docker0, а не через физический или VPN-интерфейс.
- iptables -t nat -L POSTROUTING -n -v: наличие MASQUERADE для подсети контейнеров.
- sysctl net.ipv4.ip_forward: значение 1.
- Проверка DNS: cat /etc/resolv.conf в контейнере, ожидаемый nameserver 127.0.0.11 в пользовательской сети.
- Сверка подсетей Docker с локальной и VPN-адресацией компании.
- Исправление: docker network create с явной подсетью, правка daemon.json, правило в DOCKER-USER. После изменений — systemctl restart docker и пересоздание сетей с контейнерами.
Набор доступных команд зависит от версии Docker Engine и используемого backend (iptables-legacy, iptables-nft, nftables). Перед диагностикой выполните docker info: в выводе видны версия Engine, активные сетевые драйверы и предупреждения о состоянии firewall. Если после проверки по чек-листу сервис остаётся недоступным, разберите смежные сценарии в чек-листе по устранению ошибок Docker-сети.