Сетевая модель Docker (CNM)
TL;DR: Контейнеры общаются через виртуальные сети (bridge). DNS по именам работает только в user-defined networks. Docker манипулирует iptables напрямую — UFW может не помочь.
Docker использует модель Container Network Model (CNM), которая стандартизирует сетевое взаимодействие для контейнеров. Она позволяет контейнерам общаться друг с другом и с внешним миром, абстрагируясь от физической инфраструктуры сети.
В основе CNM лежат три объекта:
- Sandbox: Изолированная сетевая среда контейнера (Network Namespace). Содержит интерфейсы, таблицу маршрутизации и DNS-настройки.
- Endpoint: Сетевой интерфейс (виртуальная сетевая карта), соединяющий Sandbox с сетью.
- Network: “Программный коммутатор” (Software Switch), через который пакеты передаются между эндпоинтами.
Путь пакета: От контейнера к миру
Когда приложение в контейнере отправляет запрос в Интернет (Outbound Traffic), пакет проходит следующий путь:
- Container (eth0): Пакет покидает интерфейс внутри сетевого пространства имен контейнера.
- veth Pair: Пакет проходит через виртуальную трубу и выходит на хосте через интерфейс
vethXXXX. - Docker Bridge (
docker0): Виртуальный мост (L2 Switch) принимает пакет от veth-интерфейса. - IPtables (Routing & NAT):
- Ядро Linux видит, что пакет адресован во внешнюю сеть.
- Срабатывает правило Masquerading (SNAT) в таблице NAT. IP-адрес источника (внутренний, например
172.17.0.2) подменяется на IP-адрес хоста.
- Host Interface (eth0): Пакет уходит в физическую сеть с IP-адресом хоста.
Драйверы сетей
Docker поддерживает подключаемые драйверы. Выбор драйвера определяет топологию и поведение сети.
1. Bridge (Мост) — По умолчанию
Используется для связи контейнеров на одном хосте.
- При установке Docker создает дефолтный мост
docker0. - Контейнеры получают внутренний IP-адрес из подсети моста (например,
172.17.0.x). - Изоляция: Контейнеры в одной bridge-сети видят друг друга по IP. Контейнеры в разных bridge-сетях изолированы.
- Доступ снаружи: По умолчанию закрыт. Чтобы открыть сервис миру, используется Port Publishing (
-p 80:80), который создает правило DNAT в iptables хоста.
Default vs User-defined Bridge:
- В дефолтном мосту (
bridge) контейнеры могут общаться только по IP-адресам.- В пользовательских мостах (
docker network create my-net) работает автоматический DNS (Service Discovery), позволяющий обращаться к контейнерам по именам.
2. Host
Убирает сетевую изоляцию между контейнером и хостом.
- Контейнер не получает свой IP, а использует IP хоста.
- Порт 80 контейнера сразу открывается на порту 80 хоста (флаг
-pигнорируется). - Плюс: Максимальная производительность (нет накладных расходов на NAT).
- Минус: Возможны конфликты портов. Работает только на Linux (на Mac/Windows из-за VM-прослойки поведение отличается).
3. None
Полностью отключает сеть. В контейнере есть только loopback-интерфейс (lo). Используется для batch-задач, которым не нужен интернет.
4. Overlay
Позволяет связывать контейнеры, запущенные на разных хостах (в режиме Docker Swarm). Создает распределенную подсеть поверх физической сети узлов, инкапсулируя трафик (обычно VXLAN).
5. Macvlan
Позволяет присвоить контейнеру MAC-адрес, делая его полноценным устройством в физической сети (например, подключенным к офисному роутеру). Контейнер получает IP-адрес напрямую от DHCP-сервера вашей локальной сети, минуя NAT Docker-а.
DNS и Service Discovery
Docker имеет встроенный DNS-сервер (Embedded DNS Server, 127.0.0.11 внутри контейнера).
- Когда контейнер
appпытается обратиться кdb: - Запрос идет на
127.0.0.11. - Docker проверяет, находятся ли
appиdbв одной пользовательской сети (User-defined bridge). - Если да — возвращает IP-адрес контейнера
db. - Если имя внешнее (google.com) — запрос пересылается на DNS-сервера хоста.
Важно: DNS resolution по именам контейнеров не работает в дефолтной сети
bridge. Это сделано для обратной совместимости. Поэтому в Docker Compose всегда создается отдельная сеть для проекта, где DNS работает “из коробки”.
Публикация портов (Ingress)
Чтобы сделать сервис доступным извне, используется механизм Port Publishing (-p 8080:80).
Он реализуется двумя способами одновременно:
- iptables (DNAT): Основной механизм. Ядро переписывает Destination IP входящего пакета на IP контейнера. Работает быстро и прозрачно.
- docker-proxy: Процесс в user-space. Слушает порт на хосте и пересылает байты в контейнер. Используется как запасной вариант (fallback) и для обработки traffic с localhost (hairpin NAT).
Внимание (UFW/Firewall): Поскольку Docker манипулирует правилами
iptablesнапрямую, он часто вставляет свои разрешающие правила (DOCKERchain) перед правилами вашего фаервола (UFW/Firewalld). Закрытие порта 8080 в UFW может не сработать, если Docker опубликовал его! Для корректной защиты нужно использовать цепочкуDOCKER-USER.
Подводные камни
| Заблуждение | Реальность |
|---|---|
| «Контейнеры видят друг друга по имени» | Только в user-defined bridge. В default bridge — только по IP |
| «UFW закроет порт Docker» | Docker вставляет iptables-правила ДО UFW. Используй цепочку DOCKER-USER |
| «-p 8080:80 безопасен» | Порт открыт на ВСЕХ интерфейсах (0.0.0.0). Используй -p 127.0.0.1:8080:80 |
| «host-сеть работает одинаково везде» | На macOS/Windows host-mode работает внутри VM, не на реальном хосте |