Маршрутизация в Docker и Docker Compose: таблицы маршрутизации контейнеров и диагностика сетевых проблем | AdminWiki

Маршрутизация в Docker и Docker Compose: таблицы маршрутизации контейнеров и диагностика сетевых проблем

18 сентября 2026 11 мин. чтения
Содержание статьи

Маршрутизация в 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.

Что происходит при запуске контейнера: пошаговая схема

  1. Docker создаёт veth-пару: два связанных виртуальных интерфейса, где пакет, отправленный в один конец, выходит из второго.
  2. Один конец остаётся в network namespace хоста и получает имя вида veth3f1a9c2, второй переносится в namespace контейнера и переименовывается в eth0.
  3. В namespace контейнера назначается адрес из подсети bridge, например 172.17.0.2/16.
  4. Интерфейсы поднимаются, в таблице маршрутизации контейнера появляется запись default via 172.17.0.1 dev eth0.
  5. На хосте 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 соседнего контейнера по подсети работает. Диагностика по шагам:

  1. docker exec -it myapp ip route: есть ли маршрут default.
  2. ip route show на хосте: есть ли маршрут до подсети контейнера через docker0.
  3. iptables -t nat -L POSTROUTING -n -v: есть ли MASQUERADE для этой подсети.
  4. sysctl net.ipv4.ip_forward: значение 1.
  5. 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 INPUTFirewall блокирует подсеть контейнеров, сервис слушает 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

  1. ip route внутри контейнера: проверьте наличие строки default со шлюзом из подсети вашей сети.
  2. ip addr show eth0 внутри контейнера: адрес должен принадлежать подсети сети Docker.
  3. ip route show на хосте: маршрут до подсети контейнеров должен идти через docker0, а не через физический или VPN-интерфейс.
  4. iptables -t nat -L POSTROUTING -n -v: наличие MASQUERADE для подсети контейнеров.
  5. sysctl net.ipv4.ip_forward: значение 1.
  6. Проверка DNS: cat /etc/resolv.conf в контейнере, ожидаемый nameserver 127.0.0.11 в пользовательской сети.
  7. Сверка подсетей Docker с локальной и VPN-адресацией компании.
  8. Исправление: docker network create с явной подсетью, правка daemon.json, правило в DOCKER-USER. После изменений — systemctl restart docker и пересоздание сетей с контейнерами.

Набор доступных команд зависит от версии Docker Engine и используемого backend (iptables-legacy, iptables-nft, nftables). Перед диагностикой выполните docker info: в выводе видны версия Engine, активные сетевые драйверы и предупреждения о состоянии firewall. Если после проверки по чек-листу сервис остаётся недоступным, разберите смежные сценарии в чек-листе по устранению ошибок Docker-сети.

Поделиться:
Сохранить гайд? В закладки браузера