Каждый контейнер Docker получает собственный сетевой namespace — изолированный стек с интерфейсами, таблицей маршрутизации и правилами iptables. То, как этот namespace подключается к внешнему миру и к другим контейнерам, определяет сетевой драйвер. Выбор драйвера влияет на три вещи: видимость контейнеров друг для друга, необходимость пробрасывать порты и уровень сетевой изоляции.
Bridge — драйвер по умолчанию. При установке Docker создаёт виртуальный мост
Между ними принципиальная разница в обнаружении сервисов:
На default bridge контейнеры могут обращаться друг к другу только по IP-адресу. Встроенный DNS-сервер Docker не работает в этой сети. Это делает default bridge непригодным для большинства реальных сценариев: IP-адреса меняются при перезапуске, а хардкодить их в конфигурации — прямой путь к хрупкой инфраструктуре.
User-defined bridge решает обе проблемы. Docker запускает встроенный DNS-сервер (на
Контейнер
Контейнер может состоять в нескольких user-defined сетях одновременно. Это основной механизм сегментации:
Контейнер
Порт контейнера по умолчанию доступен только внутри его сети. Чтобы сделать сервис доступным с хоста или извне, используется флаг
Здесь
Типичная ошибка: публиковать порты всех контейнеров, хотя они общаются между собой внутри bridge-сети. Каждый опубликованный порт — это правило в iptables и потенциальная поверхность атаки.
Драйвер
Nginx слушает порт 80 хоста. Флаг
Overlay-сеть инкапсулирует трафик между контейнерами на разных физических или виртуальных машинах. Пакеты оборачиваются в VXLAN и передаются через underlying-сеть хостов.
Overlay работает в двух контекстах:
Флаг
Если все контейнеры работают на одном хосте, overlay избыточен. Bridge-сеть проще, не требует Swarm и не добавляет overhead на инкапсуляцию.
Драйвер
Проверьте, что оба контейнера в одной user-defined сети:
Если контейнер в default bridge — DNS не работает. Переподключите его к user-defined сети:
Убедитесь, что порт опубликован:
Если вывод пустой — порт не проброшен. Для доступа изнутри сети это нормально, для доступа с хоста нужен
Docker выбирает подсети для bridge-сетей автоматически. Если на хосте уже занята
При конфликте создайте сеть с явной подсетью:
Docker управляет цепочками iptables напрямую. Если на хосте работает UFW или firewalld, правила могут конфликтовать. Docker добавляет правила в цепочки
Для диагностики:
Default bridge подходит для
Bridge: изоляция с управляемым доступом
Bridge — драйвер по умолчанию. При установке Docker создаёт виртуальный мост
docker0 с подсетью 172.17.0.0/16. Каждый контейнер, запущенный без явного указания сети, подключается к этому мосту и получает IP из этой подсети.Default bridge vs user-defined bridge
Между ними принципиальная разница в обнаружении сервисов:
| Возможность | Default bridge (docker0) | User-defined bridge |
|---|---|---|
| Связь по IP | Да | Да |
| DNS-резолвинг по имени контейнера | Нет | Да |
| Автоматическое подключение | Да | Нет, нужно указать явно |
| Изоляция между контейнерами | Все видят всех | Только контейнеры в одной сети |
На default bridge контейнеры могут обращаться друг к другу только по IP-адресу. Встроенный DNS-сервер Docker не работает в этой сети. Это делает default bridge непригодным для большинства реальных сценариев: IP-адреса меняются при перезапуске, а хардкодить их в конфигурации — прямой путь к хрупкой инфраструктуре.
User-defined bridge решает обе проблемы. Docker запускает встроенный DNS-сервер (на
127.0.0.11 внутри контейнера), который резолвит имена контейнеров в их текущие IP.Создание и использование user-defined bridge
Bash:
## Создание сети
docker network create myapp
## Запуск контейнеров в этой сети
docker run -d --name backend --network myapp nginx
docker run -d --name frontend --network myapp alpine sleep 3600
## Из контейнера frontend имя backend резолвится в его IP
docker exec frontend ping backend
Контейнер
frontend обратится к backend по имени — Docker подставит актуальный IP. Если backend перезапустится и получит новый адрес, DNS обновится автоматически.Подключение к нескольким сетям
Контейнер может состоять в нескольких user-defined сетях одновременно. Это основной механизм сегментации:
Bash:
docker network create frontend-net
docker network create backend-net
## Nginx доступен из обеих сетей
docker run -d --name nginx --network frontend-net nginx
docker network connect backend-net nginx
## База данных только в backend-net
docker run -d --name postgres --network backend-net postgres
Контейнер
postgres не виден из frontend-net. Контейнеры, которым нужен доступ к обоим слоям, подключаются к обеим сетям. Это заменяет файрвол-правила на уровне хоста.Публикация портов
Порт контейнера по умолчанию доступен только внутри его сети. Чтобы сделать сервис доступным с хоста или извне, используется флаг
-p:
Bash:
docker run -d --name web --network myapp -p 8080:80 nginx
Здесь
8080 — порт на хосте, 80 — порт внутри контейнера. Контейнеры внутри myapp обращаются к web по имени и порту 80 без всякой публикации. Флаг -p нужен только для доступа снаружи сети.Типичная ошибка: публиковать порты всех контейнеров, хотя они общаются между собой внутри bridge-сети. Каждый опубликованный порт — это правило в iptables и потенциальная поверхность атаки.
Host: без изоляции
Драйвер
host убирает сетевой namespace контейнера. Контейнер использует сетевой стек хоста напрямую: те же интерфейсы, те же порты, та же таблица маршрутизации.
Bash:
docker run -d --network host nginx
Nginx слушает порт 80 хоста. Флаг
-p в этом режиме игнорируется — пробрасывать нечего, контейнер уже на хосте.Когда host оправдан
- Высокая производительность сети. Нет overhead на NAT и виртуальный мост. Разница заметна на нагрузках с десятками тысяч соединений в секунду.
- Протоколы, чувствительные к NAT. Некоторые приложения некорректно работают за NAT (отдельные реализации SIP, IPsec в режиме transport).
- Мониторинг и агенты. Агенты, которым нужно видеть сетевой трафик хоста или слушать на определённых интерфейсах.
Ограничения host-режима
- Нет изоляции: два контейнера не могут слушать один порт.
- Нет DNS-резолвинга по именам контейнеров — контейнеры не знают друг о друге.
- Контейнер видит все интерфейсы хоста, включая служебные.
- На Docker Desktop (macOS/Windows)
--network hostработает иначе, чем на Linux: контейнер получает сеть виртуальной машины, а не физического хоста.
Overlay: связь между хостами
Overlay-сеть инкапсулирует трафик между контейнерами на разных физических или виртуальных машинах. Пакеты оборачиваются в VXLAN и передаются через underlying-сеть хостов.
Overlay работает в двух контекстах:
- Docker Swarm — создаётся автоматически или вручную, сервисы в одном overlay видят друг друга по имени.
- Ручное создание — через
docker network create --driver overlayс указанием внешнего key-value store (Consul, etcd). Этот вариант используется редко, так как Swarm покрывает большинство сценариев.
Bash:
## В Swarm-режиме
docker network create -d overlay --attachable myapp-overlay
## Сервис подключается к overlay
docker service create --name api --network myapp-overlay myimage
Флаг
--attachable позволяет подключать к overlay-сети не только Swarm-сервисы, но и обычные контейнеры через docker run --network.Когда overlay не нужен
Если все контейнеры работают на одном хосте, overlay избыточен. Bridge-сеть проще, не требует Swarm и не добавляет overhead на инкапсуляцию.
None: полная изоляция
Драйвер
none отключает сеть полностью. Контейнер получает только loopback-интерфейс. Используется для batch-задач, которые не требуют сети, или как дополнительный уровень изоляции.
Bash:
docker run --network none alpine ip addr
## Покажет только lo
Диагностика сетевых проблем
Контейнер не резолвит имя другого контейнера
Проверьте, что оба контейнера в одной user-defined сети:
Bash:
docker inspect <container> --format '{{json .NetworkSettings.Networks}}' | jq 'keys'
Если контейнер в default bridge — DNS не работает. Переподключите его к user-defined сети:
Bash:
docker network disconnect bridge <container>
docker network connect myapp <container>
Контейнер не доступен с хоста
Убедитесь, что порт опубликован:
Bash:
docker port <container>
Если вывод пустой — порт не проброшен. Для доступа изнутри сети это нормально, для доступа с хоста нужен
-p.Конфликт подсетей
Docker выбирает подсети для bridge-сетей автоматически. Если на хосте уже занята
172.17.0.0/16 или диапазон пересекается с корпоративной сетью, контейнеры станут недоступны. Проверить:
Bash:
docker network ls
docker network inspect <network> --format '{{.IPAM.Config}}'
При конфликте создайте сеть с явной подсетью:
Bash:
docker network create --subnet 10.10.0.0/24 myapp
iptables и файрвол хоста
Docker управляет цепочками iptables напрямую. Если на хосте работает UFW или firewalld, правила могут конфликтовать. Docker добавляет правила в цепочки
DOCKER, DOCKER-USER и FORWARD. Ручное изменение этих цепочек без понимания механики Docker — частая причина «пропавшей» связности.Для диагностики:
Bash:
iptables -t nat -L DOCKER -n -v
iptables -L FORWARD -n -v
Выбор драйвера: краткая матрица
| Сценарий | Драйвер |
|---|---|
| Несколько сервисов на одном хосте, нужна связь по именам | User-defined bridge |
| Максимальная производительность сети, один сервис на порт | Host |
| Сервисы на разных хостах (Swarm) | Overlay |
| Контейнер без сети (batch, изоляция) | None |
| Быстрый тест, не продакшен | Default bridge |
Default bridge подходит для
docker run hello-world и одноразовых экспериментов. Для любой конфигурации из двух и более связанных контейнеров создавайте user-defined bridge — это занимает одну команду и устраняет класс проблем с DNS и изоляцией.
