Гайд: сети в Docker для самохостера — bridge, host, macvlan, ipvlan

«Контейнеры видят друг друга по имени? почему порт не проброшен? зачем macvlan?» — сети в Docker спотыкают почти каждого самохостера. Разберём встроенные сетевые драйверы по официальной документации и по делу: что выбрать под реверс-прокси, под Pi-hole, под стек мониторинга.

Встроенные драйверы

Docker Engine на Linux даёт несколько сетевых драйверов:

  • bridge — драйвер по умолчанию для одиночных контейнеров на одном хосте. Контейнеры в одной bridge-сети общаются между собой, изолированы от других.
  • host — убирает сетевую изоляцию: контейнер использует сетевой стек хоста напрямую. Быстро, но контейнер «слышит» все порты хоста; удобно для того, что должно видеть всю сеть (например, обнаружение устройств).
  • none — полная изоляция: у контейнера нет сети вообще.
  • overlay — соединяет несколько демонов Docker (Swarm) в одну сеть для меж-хостового общения.
  • macvlan — контейнер получает собственный MAC и выглядит как отдельное устройство в вашей физической сети (свой IP из подсети LAN).
  • ipvlan — похоже на macvlan, но без отдельных MAC; удобно, когда провайдер/коммутатор ограничивает число MAC, и для работы с VLAN.

Главная развилка: default bridge vs user-defined bridge

Это то, обо что спотыкаются чаще всего.

  • В дефолтной bridge-сети (куда контейнер попадает, если вы не указали сеть) контейнеры не могут обращаться друг к другу по имени — только по IP. А IP выдаются динамически, так что это боль.
  • В пользовательской bridge-сети (созданной вами) работает встроенный DNS Docker (адрес 127.0.0.11), и контейнеры резолвят друг друга по имени контейнера. Это то, что вам почти всегда нужно.

Практический вывод: всегда создавайте свою сеть, а не полагайтесь на дефолтную:

docker network create appnet
docker run -d --name db --network appnet postgres:16
docker run -d --name app --network appnet myapp   # достучится до db как host "db"

В docker-compose это происходит автоматически: Compose заводит отдельную сеть для проекта, и сервисы видят друг друга по именам сервисов. Поэтому в compose вы пишете в конфиге приложения db:5432, а не IP.

Проброс портов

Порты контейнера в bridge-сети доступны с самого хоста, но чтобы сервис был виден снаружи хоста, порт нужно опубликовать флагом -p/--publish:

docker run -d -p 8080:80 nginx      # хост:контейнер

Тонкость безопасности: -p 8080:80 слушает на всех интерфейсах. Если сервис должен быть доступен только локально (например, за реверс-прокси), привязывайте к loopback: -p 127.0.0.1:8080:80.

Что выбрать под типовые задачи

  • Реверс-прокси (Nginx Proxy Manager, Caddy, Traefik) + сервисы: одна user-defined bridge-сеть, публикуете наружу только 80/443 у прокси, остальные сервисы держите без -p — прокси ходит к ним по именам.
  • Pi-hole/AdGuard, которым нужен «настоящий» IP в LAN: macvlan — контейнер получает адрес вашей подсети и виден в сети как отдельное устройство.
  • Мониторинг/сервис, которому нужен доступ ко всем портам хоста: host-сеть (осознанно, помня про потерю изоляции).
  • Общий сетевой namespace двух контейнеров: --network container:<имя> — второй контейнер делит интерфейс первого и общается через 127.0.0.1 (частый приём для «прицепить» sidecar/VPN-контейнер).

Трезвая оценка

  • macvlan — не бесплатный обед. По умолчанию хост не может общаться со своими macvlan-контейнерами (изоляция родительского интерфейса); нужен отдельный macvlan-шлюз-трюк. Плюс не все Wi-Fi-адаптеры и облачные провайдеры это переваривают.
  • host-сеть ломает изоляцию. Экономит на NAT, но контейнер получает доступ ко всему сетевому стеку хоста — используйте только там, где это осознанно нужно.
  • Дефолтная сеть — источник половины «почему не резолвится». Если что-то не видит соседа по имени — почти всегда забыли создать свою сеть.

Что делать

  1. Возьмите за правило user-defined bridge для любого много-контейнерного стека (или просто используйте docker-compose, где это из коробки).
  2. Публикуйте наружу минимум — в идеале только 80/443 у реверс-прокси, всё остальное держите внутри сети и биндите на 127.0.0.1, если публикация всё же нужна.
  3. macvlan — только когда реально нужен LAN-IP (Pi-hole/AdGuard, устройства умного дома), и заранее решите вопрос «хост↔контейнер».
  4. Проверяйте связность из контейнера: docker exec -it app ping db и getent hosts db — сразу видно, работает ли встроенный DNS.

Источники

Как у вас организованы сети в докер-стеке — одна общая сеть, сегментация по проектам, macvlan для отдельных сервисов? И ловили ли «не резолвится по имени» на дефолтной сети?