Сеть в контейнерах
Сеть в контейнерах
Сетевая модель контейнеров определяет, как сервисы находят друг друга, как изолируются по зонам доступа и как трафик выходит наружу. Для стабильной эксплуатации важно понимать базовые типы сетей и их ограничения.
Как работает сеть в контейнерах?
Сеть — одна из ключевых тем контейнеризации, потому что именно через неё сервисы находят друг друга и обмениваются данными.
Для взаимодействия контейнеров обычно создают пользовательские сети Docker. Так проще управлять связностью сервисов и границами доступа.
По умолчанию Docker предоставляет несколько типов сетей, таких как bridge, host и none. Для сложных сценариев создают пользовательскую сеть и подключают контейнеры (подробнее — в разделе "Собственные сети" ниже):
docker network create my_custom_network
docker run -d --network=my_custom_network <image_name>
Разбор:
docker network create my_custom_network— создаёт пользовательскую bridge-сеть с именемmy_custom_network(встроенный DNS для имён контейнеров).docker run -d— запуск контейнера в фоне (detached).--network=my_custom_network— подключает контейнер к этой сети вместо дефолтнойbridge.<image_name>— образ для запуска (напримерnginx:alpine).- Контейнеры в одной пользовательской сети резолвят друг друга по имени без
--link.
Пользовательские сети позволяют контейнерам общаться по имени через встроенный DNS Docker. Поэтому сервисы обращаются друг к другу как api, db, redis, без жёсткой привязки к IP-адресам. В Compose то же правило: DB_HOST=db, не localhost — см. готовые стеки.
Типы виртуальных сетей
Docker поддерживает несколько сетевых режимов. Выбор режима определяет уровень изоляции, доступ извне и модель производительности.
Bridge (мостовая сеть) - тип сети, создаваемый Docker по умолчанию. Эта сеть называется docker0 - контейнеры, подключённые к этой сети, получают IP-адреса из внутреннего пула (например, 172.17.0.x). Это подходит для изоляции контейнеров друг от друга. Для доступа к контейнерам извне используется проброс портов (-p).
Пример:
docker run -d --name my-app -p 8080:80 nginx
Разбор:
-d— контейнер работает в фоне.--name my-app— фиксированное имя дляdocker logs,execи DNS в пользовательской сети.-p 8080:80— проброс: порт 80 внутри контейнера доступен на 8080 хоста (хост:контейнер).nginx— официальный образ веб-сервера; слушает 80 внутри контейнера.- Режим bridge по умолчанию: изоляция от других контейнеров, доступ с хоста через проброс порта.
Здесь порт 80 контейнера проброшен на порт 8080 хоста.
Host (хостовая сеть) используется контейнером напрямую, без изоляции. Это увеличивает производительность, но снижает безопасность и не требует проброса портов:
docker run -d --network host my-app
Разбор:
--network host— контейнер использует сетевой стек хоста напрямую, без отдельного network namespace.- Проброс
-pне нужен: сервис слушает порты хоста как обычный процесс. - Выше производительность и проще отладка сети, ниже изоляция и предсказуемость портов.
my-app— имя контейнера; образ задаётся в полной команде (в примере сокращено).
Overlay (наложенная сеть) используется в распределённых системах (Docker Swarm, например) и позволяет контейнерам на разных хостах взаимодествовать через виртуальную сеть. Это подходит для микросервисной архитектуры, но требует настройки оркестратора.
Пример:
docker network create -d overlay my-overlay-net
Разбор:
-d overlay— драйвер overlay для multi-host сетей (Docker Swarm, несколько нод).my-overlay-net— имя виртуальной сети; контейнеры на разных хостах видят друг друга в одном L2/L3 overlay.- Требует инициализированный Swarm (
docker swarm init) и подключённые worker/manager. - Используют для микросервисов в кластере, где сервисы не сидят на одной машине.
None (без сети) - контейнер полностью изолирован от сети, и нет доступа к внешнему миру. Пример:
docker run -d --network none my-app
Разбор:
--network none— у контейнера нет сетевых интерфейсов кроме loopback.- Нет исходящего доступа в интернет и входящих соединений через Docker-сеть.
- Подходит для изолированных batch-задач или когда сеть подключают позже вручную.
my-app— имя контейнера при запуске.
Собственные сети
Собственные сети удобно использовать для разделения сервисов по зонам доступа — публичная часть, внутренние API, базы данных.
При создании сети:
docker network create my-bridge-net
Разбор:
- Создаётся отдельная bridge-сеть
my-bridge-netдля сегментации (например только backend). - По умолчанию драйвер
bridge— локальная сеть на одном Docker-хосте. - Имя сети используют в
docker run --networkи вdocker composeв секцииnetworks.
Далее можно просто подключать контейнеры к сети:
docker run -d --name app1 --network my-bridge-net nginx
docker run -d --name app2 --network my-bridge-net mysql
Разбор:
- Оба контейнера в одной сети
my-bridge-net— могут обращаться друг к другу по имениapp1,app2. app1+nginx— веб-сервер;app2+mysql— СУБД; типичная пара frontend/БД в одном сегменте.-d— фоновый режим для обоих сервисов.- Из хоста к MySQL обычно не пробрасывают порт, если доступ нужен только
app1внутри сети.
В таком случае, контейнеры app1 и app2 будут находиться в одной сети и смогут взаимодействовать друг с другом по имени (DNS-разрешение).
Если же контейнер уже запущен, можно его подключить к другой сети через network connect:
docker network connect my-bridge-net existing-container
Разбор:
docker network connect— подключает уже запущенный контейнер к дополнительной сети без пересоздания.my-bridge-net— целевая сеть;existing-container— имя или ID контейнера.- Контейнер может одновременно состоять в нескольких сетях (разные интерфейсы).
- Парная команда —
docker network disconnectдля отключения от сети.
Соответственно disconnect - отключение контейнера от сети.
Проброс портов позволяет сделать сервисы, работающие внутри контейнера, доступными извне.
Синтаксис таков:
-p <хост_порт>:<контейнер_порт>
Разбор:
- Флаг
-p(или--publish) пробрасывает TCP/UDP-порт с хоста в контейнер. <хост_порт>— порт, на который приходят клиенты с машины или из LAN.<контейнер_порт>— порт процесса внутри контейнера, который слушает приложение.- Можно указать протокол:
-p 8080:80/tcp; привязка к интерфейсу:127.0.0.1:8080:80.
К примеру, если выполнить команду:
docker run -d -p 8080:80 nginx
Разбор:
- Запрос к
http://localhost:8080на хосте перенаправляется на порт 80 процесса nginx в контейнере. -d— nginx работает в фоне.- Без
-pсервис из браузера на хосте недоступен (только из других контейнеров в той же пользовательской сети). - Если 8080 занят на хосте, Docker вернёт ошибку bind.
…то сервис будет доступен по адресу http://localhost:8080.
Для взаимодействия контейнеров в одной сети Docker автоматически настраивает DNS-резолвинг имён. Историческая опция --link устарела; рабочий стандарт сегодня — пользовательские сети и имена сервисов.
Для просмотра сетей:
docker network ls
Разбор:
- Выводит таблицу сетей —
NETWORK ID,NAME,DRIVER,SCOPE. - Видны встроенные
bridge,host,noneи созданные вамиmy-bridge-net, overlay-сети Swarm. - Используют перед
inspectи отладкой "почему контейнеры не видят друг друга".
По умолчанию, Docker использует NAT (Network Address Translation) для маршрутизации трафика между контейнерами и хостом. Это обеспечивает изоляцию контейнеров.
Важно также отметить, что контейнер может быть подключен к нескольким сетям одновременно, и что Docker поддерживает IPv6 (но для этого нужно явно включить его в конфигурации Docker).
В Docker Desktop (Mac/Windows) для доступа к хосту из контейнера используют host.docker.internal. На Linux по умолчанию этого имени нет — укажите IP хоста или добавьте extra_hosts в Compose.
В пользовательской bridge-сети встроенный DNS резолвит имена контейнеров и имён сервисов Compose — поэтому app может обращаться к db:5432 без --link и без знания IP.
Сценарий "frontend + api + db"
Типовая схема для проекта:
frontendпубликуется наружу через-p 80:80;apiпубликуется только внутрь внутренней сети;dbостаётся доступной только дляapiи не открывается наружу.
Пример через Compose:
Код ITЗагрузка примера кода…
Разбор:
services.frontend— nginx с пробросом80:80наружу и участием в сетяхpublicиinternal(шлюз между зонами).services.api— толькоinternal: снаружи не публикуется, доступен frontend по имени сервисаapi.services.db— PostgreSQL только воinternal; с интернета и сpublicнапрямую недоступен.networks.public/networks.internal— объявление двух изолированных bridge-сетей Compose.- DNS внутри Compose:
apiрезолвится дляfrontend,db— дляapi; имена совпадают с ключами сервисов.
Такой подход разделяет внешнюю и внутреннюю связность и упрощает контроль доступа.
Частые сетевые проблемы и диагностика
- "connection refused" — сервис не слушает нужный порт внутри контейнера или не запущен.
- Неверный хост для подключения — внутри сети Compose используют имя сервиса, а не
localhost. - Контейнеры в разных сетях — DNS-имя не резолвится, пока контейнеры не в одной сети.
- Порт занят на хосте —
-p 8080:80не применится, если8080уже используется.
Быстрая проверка:
docker network ls
docker network inspect <network_name>
docker ps
docker logs <container_id>
Разбор:
docker network ls— какие сети есть и какой у них драйвер.docker network inspect <network_name>— какие контейнеры подключены, подсети, gateway, опции DNS.docker ps— запущен ли контейнер, какие порты опубликованы.docker logs <container_id>— ошибки приложения (connection refused, bind failed, неверный хост БД).- Цепочка закрывает типичную диагностику "сеть есть, но сервис не отвечает".
Практики безопасности сети
- открывайте наружу только действительно необходимые порты;
- критичные сервисы (БД, брокеры) держите в внутренних сетях;
- избегайте
--network hostбез явной необходимости; - ограничивайте межсервисный доступ сетевой сегментацией;
- фиксируйте правила доступа в инфраструктурном коде.
Связанные статьи
- Docker
- docker-compose
- Docker Compose — готовые стеки
- Работа с Docker
- Dockerfile
- Dockerfile — 10 типовых образов