5. Сеть Docker
Цель темы: уметь настраивать и диагностировать сеть контейнеров: понимать драйверы (bridge, host, none, overlay, macvlan), port mapping, связь контейнер↔контейнер и контейнер↔хост, DNS внутри Docker и роль iptables (цепочки DOCKER, NAT, FORWARD).
Определения терминов
Сетевой драйвер Docker
Сетевой драйвер определяет, как контейнер подключается к сети: какой тип виртуального устройства используется (bridge, veth, macvlan и т.д.) и как настроены маршрутизация и изоляция. По умолчанию используется драйвер bridge: контейнеры подключаются к виртуальному коммутатору (bridge) на хосте и получают IP из подсети этого bridge.
Bridge (мост)
Bridge — виртуальный коммутатор уровня 2 в ядре Linux. Docker создаёт bridge docker0 (или пользовательскую сеть с драйвером bridge). Каждый контейнер получает пару veth: один конец в network namespace контейнера (интерфейс eth0), другой привязан к bridge. Контейнеры в одном bridge могут общаться по IP; для выхода в интернет и приёма трафика с хоста используются NAT и правила iptables.
Port mapping (publish)
Port mapping — проброс порта хоста на порт контейнера. При обращении к <host_ip>:<host_port> ядро (через iptables или docker-proxy) перенаправляет пакеты в контейнер на указанный порт. Задаётся флагом -p или --publish, например -p 8080:80.
Overlay-сеть
Overlay — сеть, размазанная по нескольким хостам (Docker Swarm, Kubernetes). Трафик между контейнерами на разных нодах инкапсулируется (VXLAN и др.) и доставляется поверх физической сети. На одной ноде overlay не обязателен; для многопоточной оркестрации он нужен для «сети между подами/сервисами» поверх кластера.
macvlan
macvlan — драйвер, создающий у контейнера виртуальный интерфейс с собственным MAC-адресом, привязанный к физическому интерфейсу хоста. Контейнер выглядит как отдельное устройство в той же L2-сети, что и хост (свой IP из подсети сети). Удобно, когда контейнер должен быть «рядом» с хостом в сети (например, для legacy-интеграций).
Драйверы сетей
bridge (по умолчанию)
Контейнеры подключаются к bridge (по умолчанию docker0 или к пользовательской сети с драйвером bridge). Изоляция между разными сетями; внутри одной сети контейнеры видят друг друга по IP и по имени (если включён встроенный DNS).
docker network create mynet
docker run -d --name web --network mynet nginx
docker run -it --rm --network mynet alpine ping web
host
Контейнер использует сетевой стек хоста напрямую: общие с хостом интерфейсы, порты, нет изоляции. Подходит для высокопроизводительных или специфичных сценариев (например, сбор метрик с хоста); на Windows/macOS поведение ограничено.
docker run --network host myapp
none
У контейнера нет сетевых интерфейсов (кроме loopback). Полная сетевая изоляция; доступ только с хоста через другие каналы (volumes, exec).
docker run --network none alpine
overlay
Используется в Swarm и при необходимости в Compose с несколькими нодами. Создаётся через docker network create -d overlay .... На одной ноде для связи контейнеров достаточно bridge.
macvlan
Контейнер получает свой MAC и IP в подсети, к которой подключён хост. Требует указания родительского интерфейса и подсети.
docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth0 macnet
docker run --network macnet --ip=192.168.1.100 myapp
Практика
Для большинства сценариев на одной машине достаточно пользовательской bridge-сети (по умолчанию встроенный DNS по имени контейнера) и port mapping для доступа с хоста/извне.
Port mapping
Публикация порта
docker run -d -p 8080:80 nginx
# хост:контейнер
docker run -d -p 127.0.0.1:8080:80 nginx # только localhost
docker run -d -p 80:80/tcp -p 443:443/tcp nginx
Трафик на <host>:8080 перенаправляется в контейнер на порт 80. Правила создаются в iptables (цепочки DOCKER, NAT PREROUTING/OUTPUT).
Проверка проброса
docker port <container>
# 80/tcp -> 0.0.0.0:8080
Контейнер → контейнер
Контейнеры в одной пользовательской сети (или в одной default bridge до определённых версий) могут общаться по имени контейнера: встроенный DNS резолвит имя в IP. На default bridge раньше имена не резолвились — использовали --link (deprecated). Рекомендация: создавать пользовательскую сеть и подключать контейнеры к ней.
docker network create appnet
docker run -d --name db --network appnet postgres
docker run -d --name app --network appnet -e DB_HOST=db myapp
Внутри контейнера app имя db разрешается в IP контейнера postgres.
Контейнер → хост
- Обращение к сервисам на хосте: с Linux при стандартной bridge IP хоста с точки зрения контейнера — это шлюз bridge (например, 172.17.0.1 для docker0). Можно использовать этот IP или специальный DNS-имя
host.docker.internal(на Docker Desktop для Mac/Windows). На Linuxhost.docker.internalможет быть добавлен вручную или через extra_hosts. - Обращение с хоста к контейнеру: через port mapping (
-p) или, если контейнер в пользовательской сети, по IP контейнера в этой сети (доступ с хоста к подсети bridge обычно разрешён).
docker run --add-host=host.docker.internal:host-gateway app
На части установок host-gateway подставляется в IP хоста.
DNS внутри Docker
Пользовательские сети предоставляют встроенный DNS: имя контейнера (и имя сети, если заданы алиасы) резолвится в IP контейнера. Запросы идут на резолвер демона (127.0.0.11). Настройка DNS контейнера: --dns, --dns-search; при проблемах с резолвингом проверяют resolv.conf внутри контейнера и доступность 127.0.0.11.
docker run --dns 8.8.8.8 alpine
--link (deprecated)
--link раньше использовался для связи контейнеров по имени и для передачи переменных окружения. Недостатки: работает только между двумя контейнерами, при перезапуске контейнера ссылки ломаются, не масштабируется. Вместо этого используют пользовательские сети: контейнеры в одной сети резолвят друг друга по имени без --link. Знать про --link нужно только чтобы не использовать его в новом коде.
iptables: DOCKER, NAT, FORWARD
Как контейнер получает IP?
При создании контейнера в bridge-сети Docker (через демон и сетевой драйвер): создаётся пара veth, один конец попадает в network namespace контейнера и получает IP из подсети bridge (например, 172.18.0.2/16); в контейнере прописываются default gateway (IP bridge на хосте) и resolv.conf. IP выдаётся из пула подсети, назначенной этой сети.
Почему контейнер не видит внешний мир?
Типичные причины:
- Нет NAT/masquerade для исходящего трафика: пакеты из контейнера должны выходить с IP хоста (SNAT). Правила в iptables в цепочке POSTROUTING (masquerade для подсети bridge). Если правил нет (например, после ручной очистки iptables или смены firewall) — исходящий трафик не уходит или ответы не возвращаются.
- FORWARD закрыт: пакеты между контейнером и внешней сетью проходят через хост; цепочка FORWARD должна разрешать трафик для интерфейсов Docker. Обычно Docker добавляет правила сам; при жёстком firewall их могут удалять или блокировать.
- DNS: контейнер не может резолвить имена (проблемы с 127.0.0.11 или с upstream DNS). Проверка:
docker run --rm alpine nslookup google.com.
Цепочки DOCKER и NAT
Docker создаёт цепочки в таблицах nat и filter:
- nat: PREROUTING, OUTPUT — направляют трафик к контейнерам при port mapping; POSTROUTING — masquerade для исходящего трафика контейнеров.
- filter: цепочка DOCKER и правила в FORWARD — разрешают трафик к/от контейнеров.
Просмотр (на хосте):
sudo iptables -t nat -L -n -v
sudo iptables -L DOCKER -n -v
Внимание
Ручное изменение iptables может конфликтовать с правилами Docker. Восстановление: перезапуск демона или пересоздание сетей. В production изменения firewall делают с учётом правил, которые создаёт Docker.
Паттерны использования
| Паттерн | Описание |
|---|---|
| Пользовательские сети вместо default bridge | Изоляция групп сервисов, встроенный DNS по имени контейнера. |
| Port mapping только при необходимости | Публиковать только те порты, которые должны быть доступны с хоста/извне. |
| Не использовать --link | Везде пользовательские сети и резолвинг по имени. |
| Диагностика: смотреть с хоста и из контейнера | Проверять iptables, docker network inspect, ping/nslookup из контейнера. |
Антипаттерны
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
| Полагаться на default bridge и --link | Нет DNS по имени, устаревший механизм. | Создавать сети и подключать контейнеры к ним. |
| Открывать все порты (-p без ограничений) | Увеличение поверхности атаки. | Публиковать только нужные порты, при возможности привязывать к 127.0.0.1. |
| Игнорировать firewall на хосте | Docker добавляет правила; жёсткая политика может их блокировать. | Согласовывать правила firewall с работой Docker (FORWARD, цепочки DOCKER). |
Вопросы на собеседовании
Как контейнер получает IP?
При подключении к bridge-сети Docker создаёт пару veth, один конец в network namespace контейнера. Этому интерфейсу назначается IP из подсети сети (пул выдаёт демон); в контейнере настраиваются default gateway (IP bridge) и DNS (резолвер демона 127.0.0.11).
Почему контейнер не видит внешний мир?
Проверить: 1) NAT/masquerade для подсети контейнеров (iptables POSTROUTING); 2) разрешён ли FORWARD для интерфейсов Docker; 3) DNS (резолвинг имён). На хосте посмотреть iptables и логи демона; из контейнера — ping шлюза и nslookup.
Примеры из production
После обновления firewall контейнеры потеряли доступ наружу
Часто причина — сброс или изменение правил FORWARD и NAT. Нужно восстановить правила Docker (перезапуск демона) или добавить в скрипты firewall исключения для интерфейсов Docker и цепочек DOCKER.
Контейнеры в одной сети не резолвят имена
Проверить, что оба контейнера в одной пользовательской сети (docker network inspect <net>), что резолвер 127.0.0.11 доступен из контейнера и что демон не перезапускался с очисткой сетей. При использовании Compose убедиться, что сервисы в одной network.
Port mapping не срабатывает
Проверить, что порт не занят на хосте; что привязка к 0.0.0.0 или к нужному IP; что правила iptables на месте (цепочка DOCKER, PREROUTING). На некоторых системах docker-proxy слушает порт; при его отключении полагаются только на iptables.