Перейти к основному содержимому

Сеть в контейнерах

Разработчику Архитектору Инженеру

Сеть в контейнерах

Сетевая модель контейнеров определяет, как сервисы находят друг друга, как изолируются по зонам доступа и как трафик выходит наружу. Для стабильной эксплуатации важно понимать базовые типы сетей и их ограничения.


Как работает сеть в контейнерах?

Сеть — одна из ключевых тем контейнеризации, потому что именно через неё сервисы находят друг друга и обмениваются данными.

Для взаимодействия контейнеров обычно создают пользовательские сети 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 без явной необходимости;
  • ограничивайте межсервисный доступ сетевой сегментацией;
  • фиксируйте правила доступа в инфраструктурном коде.

Связанные статьи