Главная / База знаний / Сети в Docker: как контейнеры взаимодействуют друг с другом
инструкция · 29 октября 2026

Сети в Docker: как контейнеры взаимодействуют друг с другом

Разбираем сетевые драйверы Docker — bridge, host, none, overlay, разницу между дефолтным и пользовательским bridge, DNS-резолвинг контейнеров по имени, проброс портов и отладку сетевых проблем.

Зачем у Docker вообще собственная сетевая модель

Каждый контейнер Docker по умолчанию изолирован от хост-системы и от других контейнеров на уровне сетевого пространства имён (network namespace) — отдельной ядерной абстракции Linux, дающей процессу собственный набор сетевых интерфейсов, таблицу маршрутизации и правила firewall, независимые от остальной системы. Без этой изоляции два контейнера, каждый со своим веб-сервером на порту 80, конфликтовали бы друг с другом при попытке занять один и тот же порт на хосте.

Сетевая модель Docker решает сразу несколько практических задач: изоляцию контейнеров друг от друга и от хоста по умолчанию, возможность объединить группу контейнеров в общую изолированную сеть, где они видят друг друга, но не видны контейнерам из других таких групп, и контролируемую публикацию отдельных портов наружу — на хост-машину или, в случае многохостовых кластеров, между узлами.

Реализуется эта модель через систему подключаемых сетевых драйверов — каждый решает конкретный сценарий использования: bridge для изолированной сети на одном хосте (вариант по умолчанию для большинства повседневных задач), host для полного отказа от изоляции ради максимальной сетевой производительности, none для контейнеров, которым сеть вообще не нужна, и overlay для сетей, распределённых между несколькими физическими или виртуальными хостами в кластере.

Эта статья разбирает практическое устройство каждого из этих драйверов, ключевую разницу между дефолтной и пользовательской bridge-сетью (и почему это различие критично для DNS-резолвинга контейнеров по имени), механику проброса портов через флаг -p, взаимодействие сервисов в рамках docker-compose и практические подходы к отладке сетевых проблем.

Драйвер bridge: изолированная сеть на одном хосте

Bridge — сетевой драйвер по умолчанию для контейнеров, запускаемых без явного указания сети. При установке Docker создаёт виртуальный сетевой мост docker0 на хосте, и каждый новый контейнер без явно указанной сети получает виртуальный сетевой интерфейс, подключённый к этому мосту, с собственным приватным IP-адресом из выделенного для этой сети диапазона.

Запуск контейнера в дефолтной bridge-сети и просмотр его IP-адреса внутри неё
docker run -d --name web nginx
docker inspect web --format '{{.NetworkSettings.IPAddress}}'

Контейнеры, подключённые к дефолтной bridge-сети docker0, действительно могут обращаться друг к другу по сети, поскольку физически находятся в одном сетевом сегменте, — но только по IP-адресу, не по имени. Встроенный DNS-резолвинг контейнеров по имени в дефолтной bridge-сети не работает — это одно из наиболее часто вызывающих путаницу свойств Docker у тех, кто только начинает с ним работать, ожидая, что имя контейнера автоматически станет доступным как имя хоста для остальных.

Практическое решение — не полагаться на дефолтную bridge-сеть для взаимодействия между контейнерами, а создавать пользовательскую bridge-сеть явно через docker network create и подключать к ней контейнеры, которым нужно видеть друг друга по имени.

Пользовательская сеть, в которой контейнер app обращается к контейнеру db просто по имени
docker network create app-network
docker run -d --name db --network app-network postgres:16
docker run -d --name app --network app-network -e DB_HOST=db myapp

В пользовательской bridge-сети Docker предоставляет собственный встроенный DNS-сервер, автоматически регистрирующий имя каждого подключённого к сети контейнера, — приложение внутри контейнера app может обратиться к базе данных просто по имени db, и Docker резолвит это имя в реальный текущий IP-адрес контейнера db внутри этой сети, что особенно важно, поскольку IP-адреса контейнеров могут меняться при их перезапуске, а имя остаётся стабильным.

Дефолтная bridge vs пользовательская bridge: практическая разница

Разница между двумя вариантами bridge-сети выходит за рамки одного лишь DNS-резолвинга, хотя это и есть самое частое практическое проявление, с которым сталкиваются при первом знакомстве с Docker. Пользовательские bridge-сети дополнительно обеспечивают лучшую изоляцию: контейнеры из разных пользовательских сетей не видят друг друга вовсе, если явно не подключены к общей сети, тогда как все контейнеры в дефолтной bridge-сети всегда находятся в одном общем сегменте без возможности логически разделить их на группы.

Пользовательские сети также позволяют динамически подключать и отключать контейнеры без их перезапуска — командой docker network connect можно добавить уже работающий контейнер в дополнительную сеть, тогда как для дефолтной bridge-сети такой гибкости в исходной архитектуре Docker не предусмотрено в том же объёме.

Динамическое подключение и отключение уже работающего контейнера от пользовательской сети
docker network connect app-network legacy-service
docker network disconnect app-network legacy-service

На практике для любого нетривиального сценария — от простого связывания приложения с базой данных до сложной многосервисной архитектуры — рекомендуется всегда создавать явную пользовательскую сеть, а не полагаться на поведение дефолтной bridge-сети. Docker Compose следует этой же логике и создаёт отдельную пользовательскую bridge-сеть автоматически для каждого проекта, о чём подробнее в разделе про docker-compose ниже.

Драйверы host, none и overlay

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

Запуск контейнера в режиме host без изоляции сетевого стека от хоста
docker run -d --network host nginx

В host-режиме порт, который слушает процесс внутри контейнера, автоматически становится доступен на том же порту хост-машины напрямую, без какого-либо проброса через NAT и без необходимости в флаге -p вовсе — это устраняет небольшие, но измеримые накладные расходы на трансляцию адресов между контейнером и хостом, что важно для приложений с очень высокими требованиями к сетевой производительности или низкой задержке. Цена — полная потеря изоляции: если два контейнера в режиме host попытаются занять один и тот же порт, возникнет прямой конфликт портов, как если бы это были два обычных процесса на самом хосте.

None — сетевой драйвер, полностью отключающий сеть для контейнера: у него не будет ни виртуального интерфейса, ни IP-адреса, кроме служебного loopback-интерфейса самого контейнера. Используется для узкоспециализированных сценариев, где контейнеру заведомо не требуется сетевое взаимодействие вовсе — например, для изолированной пакетной обработки данных, читающей и записывающей файлы через смонтированные тома, без единого сетевого запроса за всё время своей работы.

Запуск контейнера без какого-либо сетевого доступа
docker run --network none my-batch-processor

Overlay — сетевой драйвер, объединяющий контейнеры, запущенные на разных физических или виртуальных хостах, в единую логическую сеть, как если бы они находились на одном хосте, — используется в кластерных развёртываниях через Docker Swarm или совместно с внешними системами оркестрации. Overlay-сеть инкапсулирует сетевой трафик между хостами в дополнительный уровень туннелирования (обычно на основе VXLAN), что делает возможным DNS-резолвинг контейнеров по имени и прямое сетевое взаимодействие между ними вне зависимости от того, на каком физическом узле кластера конкретный контейнер реально запущен в данный момент.

Проброс портов: -p против EXPOSE

Директива EXPOSE в Dockerfile — распространённый источник путаницы среди тех, кто только начинает работать с Docker: она лишь документирует, какие порты приложение внутри контейнера использует, никак не влияя на реальную сетевую доступность этих портов снаружи контейнера. EXPOSE — это метаданные образа, читаемые некоторыми инструментами (например, docker run -P с заглавной буквой автоматически пробрасывает все объявленные через EXPOSE порты на случайные свободные порты хоста), но сама по себе директива ничего не публикует.

EXPOSE документирует порт внутри образа, а реальную публикацию делает флаг -p при запуске
# В Dockerfile
EXPOSE 3000

# При запуске контейнера
docker run -d -p 8080:3000 myapp

Реальная публикация порта контейнера на хост-машину выполняется флагом -p (или --publish) при запуске контейнера, в формате хост_порт:порт_контейнера. В примере выше приложение внутри контейнера слушает порт 3000, но снаружи, с хост-машины и из внешней сети, оно доступно через порт 8080 — такое разделение позволяет запускать несколько контейнеров с одинаковым внутренним портом (например, несколько экземпляров одного и того же приложения) на разных портах хоста, не меняя код самого приложения.

Частая практическая проблема — сервис недоступен по проброшенному порту, хотя контейнер видимо запущен и логи не показывают ошибок: почти всегда причина в том, что процесс внутри контейнера слушает только на loopback-адресе 127.0.0.1 вместо всех интерфейсов 0.0.0.0. Проброс портов Docker технически реализован через правила iptables, перенаправляющие трафик с интерфейса докер-моста на порт контейнера, и если сам процесс внутри принимает соединения только с адреса 127.0.0.1 контейнера, трафик, приходящий с внешнего для контейнера интерфейса, до процесса просто не доходит, несмотря на формально корректную настройку проброса порта.

Проверка проброшенных портов контейнера и того, на каком адресе реально слушает процесс внутри
docker port web
netstat -tlnp | grep 3000

Сети в docker-compose: автоматика для многосервисных приложений

Docker Compose снимает большую часть ручной работы по настройке сетей для многосервисных приложений: при запуске docker compose up он автоматически создаёт отдельную пользовательскую bridge-сеть для всех сервисов, описанных в одном файле docker-compose.yml, и подключает к ней каждый из них без дополнительных указаний в конфигурации.

Три сервиса в одной сети docker-compose, обращающиеся друг к другу по имени сервиса
services:
  app:
    build: .
    ports:
      - "8080:3000"
    environment:
      DB_HOST: db
      REDIS_HOST: cache
    depends_on:
      - db
      - cache

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret

  cache:
    image: redis:7

В этом примере сервис app обращается к базе данных по имени db и к Redis по имени cache — оба имени берутся прямо из ключей верхнего уровня в описании сервисов, и Docker Compose автоматически регистрирует их во встроенном DNS созданной для проекта сети. Не нужно вручную создавать сеть, узнавать IP-адреса контейнеров или как-либо ещё настраивать связь между сервисами — вся эта работа выполняется автоматически на основе структуры docker-compose.yml.

Директива depends_on контролирует лишь порядок запуска контейнеров (сначала db и cache, затем app), но не гарантирует, что сервис внутри зависимого контейнера уже полностью готов принимать соединения к моменту старта зависящего от него сервиса, — база данных может продолжать инициализацию ещё некоторое время после того, как её контейнер формально запущен. Для критичных сценариев стоит добавлять на стороне приложения повторные попытки подключения с задержкой или использовать директиву condition: service_healthy вместе с настроенным healthcheck у зависимого сервиса.

Для проектов с несколькими независимыми файлами docker-compose (например, отдельно основное приложение и отдельно вспомогательные сервисы мониторинга) сети по умолчанию не пересекаются между разными проектами — при необходимости связать сервисы из разных compose-файлов используется внешняя (external) сеть, создаваемая заранее вручную и подключаемая явным указанием в обоих файлах.

Отладка сетевых проблем между контейнерами

Первый шаг при диагностике сетевой проблемы между контейнерами — убедиться, что оба они действительно подключены к одной и той же сети, что не всегда очевидно на практике, если сервисы запускались из разных docker-compose файлов или разными командами docker run в разное время.

Список всех контейнеров конкретной сети и список сетей конкретного контейнера
docker network inspect app-network
docker inspect web --format '{{json .NetworkSettings.Networks}}'

Если сеть общая подтверждена, следующий шаг — проверить сетевую связность изнутри одного из контейнеров напрямую, зайдя в него через docker exec и попытавшись обратиться к другому контейнеру по имени и напрямую по IP.

Проверка DNS-резолвинга и сетевой доступности изнутри контейнера
docker exec -it app sh
ping db
nslookup db
curl -v http://db:5432

Если ping и nslookup успешно резолвят имя в IP-адрес, но соединение на конкретный порт всё равно не устанавливается, вероятная причина — сам сервис внутри целевого контейнера ещё не запущен, слушает не на всех интерфейсах, либо приложение внутри контейнера использует внутренний firewall, дополнительно ограничивающий входящие соединения даже в пределах контейнерной сети. Если же не резолвится даже само имя — контейнеры находятся в разных сетях или один из них запущен в дефолтной bridge-сети, где встроенный DNS-резолвинг попросту не работает.

Для образов, в которых внутри контейнера нет привычных сетевых утилит вроде ping или curl (что типично для минималистичных production-образов на базе alpine или distroless), проверку сетевой доступности удобнее выполнять снаружи, временно запустив вспомогательный контейнер с сетевыми утилитами в той же сети.

Проверка сетевой доступности через временный контейнер с диагностическими утилитами в целевой сети
docker run --rm --network app-network nicolaka/netshoot curl -v http://db:5432

Типичные ошибки сетевой настройки Docker

Ожидание, что имена контейнеров автоматически резолвятся по DNS в дефолтной bridge-сети, — это не так, и путаница с этим свойством одна из самых частых причин "необъяснимо" не работающего сетевого взаимодействия между контейнерами у тех, кто только начинает работать с Docker.

Приложение внутри контейнера слушает на 127.0.0.1 вместо 0.0.0.0 — снаружи контейнера (в том числе через опубликованный порт хоста и от других контейнеров) сервис при этом оказывается полностью недоступен, хотя логи самого приложения показывают, что оно успешно запустилось и "слушает" ожидаемый порт.

Путаница между EXPOSE в Dockerfile и реальной публикацией порта через флаг -p — вера в то, что одного EXPOSE достаточно для доступа снаружи, приводит к недоумению, почему сервис недоступен, хотя формально "порт открыт" согласно описанию образа.

Смешение сервисов из нескольких независимых docker-compose проектов без явного использования внешней (external) сети — сервисы из разных проектов физически не видят друг друга по имени, даже если запущены на одном и том же хосте одновременно.

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

Чек-лист настройки сети Docker

Шаг 1. Для любого сценария, где контейнерам нужно обращаться друг к другу по имени, создавайте явную пользовательскую bridge-сеть вместо использования дефолтной bridge-сети.

Шаг 2. В docker-compose полагайтесь на автоматически создаваемую сеть проекта и обращайтесь к сервисам по их именам из docker-compose.yml.

Шаг 3. Убедитесь, что приложения внутри контейнеров слушают на 0.0.0.0, а не на 127.0.0.1, если к ним нужен доступ снаружи контейнера.

Шаг 4. Используйте EXPOSE в Dockerfile как документацию порта, но не забывайте про обязательный флаг -p при запуске контейнера для реальной публикации порта наружу.

Шаг 5. Для сервисов, критично зависящих от готовности других сервисов, настраивайте healthcheck и condition: service_healthy вместо расчёта только на порядок запуска через depends_on.

Шаг 6. При диагностике сетевых проблем проверяйте по порядку: общая ли сеть у контейнеров, резолвится ли имя в IP, доступен ли конкретный порт — каждый следующий шаг сужает круг возможных причин.

Итог: сетевая модель Docker как набор точечных инструментов

Сетевая подсистема Docker — не одна универсальная настройка, а набор сетевых драйверов, каждый из которых решает конкретную практическую задачу: bridge для изолированной группы контейнеров на одном хосте с удобным DNS-резолвингом по имени в пользовательском варианте, host для максимальной сетевой производительности ценой изоляции, none для контейнеров без потребности в сети вовсе и overlay для распределённых кластерных развёртываний на нескольких хостах.

Понимание разницы между дефолтной и пользовательской bridge-сетью — тот момент, который снимает большую часть путаницы у тех, кто только начинает работать с многосервисными приложениями в Docker: встроенный DNS-резолвинг по имени работает только в пользовательских сетях, и docker-compose уже реализует эту практику по умолчанию, создавая отдельную сеть для каждого проекта без дополнительных действий со стороны разработчика.

Большинство сетевых проблем между контейнерами на практике сводится к небольшому числу типичных причин — контейнеры в разных сетях, сервис слушает не на всех интерфейсах, порт не опубликован явно через -p, — и последовательная проверка каждой из них через docker network inspect, ping, nslookup и curl изнутри контейнера почти всегда быстро приводит к точной причине, вместо неопределённого перебора настроек наугад.

почему мы

Почему стоит выбрать нас

Быстрый запуск

Подберём конфигурацию и развернём проект в день обращения.

Надёжность

NVMe-диски, защита от DDoS и резервные копии по расписанию.

Поддержка 24/7

Дежурные инженеры на связи в любое время суток.

Настройка под ключ

Установим CMS, настроим веб-сервер, SSL и почту за вас.

Перенос без простоя

Аккуратно перенесём сайт и базу данных на новый сервер.

Прозрачные цены

Актуальные тарифы и честный подбор без навязывания.

частые вопросы

Вопросы и ответы

Чем пользовательская bridge-сеть отличается от дефолтной bridge-сети Docker?

В дефолтной bridge-сети (docker0) контейнеры видят друг друга только по IP-адресу, поскольку встроенный DNS-резолвинг Docker в ней не работает, — приходится либо жёстко прописывать IP, либо использовать устаревшую опцию --link. В пользовательской bridge-сети, создаваемой командой docker network create, Docker предоставляет автоматический DNS-резолвинг: контейнеры обращаются друг к другу просто по имени, заданному при запуске или в docker-compose.

Как контейнеры в одном docker-compose файле находят друг друга по сети?

Docker Compose автоматически создаёт отдельную пользовательскую bridge-сеть для всех сервисов, описанных в одном файле docker-compose.yml, и регистрирует в её встроенном DNS каждое имя сервиса из этого файла — приложение в контейнере app обращается к базе данных просто по имени сервиса, например db:5432, без необходимости знать её реальный IP-адрес.

В чём разница между -p и EXPOSE в Dockerfile?

EXPOSE в Dockerfile — это лишь документирующая директива, сообщающая (в том числе другим разработчикам и некоторым инструментам), какие порты контейнер использует внутри себя, но сама по себе она не публикует эти порты наружу. Реальная публикация порта на хост-машину, делающая сервис в контейнере доступным снаружи, выполняется флагом -p (или publish в docker-compose) при запуске контейнера.

Что произойдёт, если два контейнера в разных пользовательских bridge-сетях попытаются обратиться друг к другу по имени?

Ничего не выйдет напрямую — встроенный DNS-резолвинг Docker работает только в пределах одной сети: контейнер видит по имени только те контейнеры, что подключены к той же самой сети, что и он сам. Для взаимодействия контейнеров из разных сетей нужно либо подключить один из них дополнительно ко второй сети через docker network connect, либо обращаться через опубликованный на хосте порт.

Зачем нужен сетевой драйвer host, если он убирает изоляцию контейнера?

Host-режим устраняет накладные расходы на трансляцию сетевых адресов (NAT) между контейнером и хостом, что важно для приложений с очень высокими требованиями к сетевой производительности или для сервисов, которым по своей природе нужен прямой доступ к сетевому стеку хоста (например, некоторые сетевые мониторинговые инструменты) — цена такого решения — полная потеря изоляции портов контейнера от хоста.

Как проверить, к какой сети подключён конкретный контейнер и какой у него IP внутри неё?

Командой docker inspect с указанием имени или ID контейнера — в выводе в секции NetworkSettings.Networks перечислены все сети, к которым подключён контейнер, вместе с его IP-адресом в каждой из них; более быстрый способ для одной конкретной сети — docker network inspect с именем сети, что выведет список всех подключённых к ней контейнеров сразу.

Почему сервис недоступен по проброшенному порту, хотя контейнер запущен и работает?

Частая причина — приложение внутри контейнера слушает только localhost (127.0.0.1) вместо всех сетевых интерфейсов (0.0.0.0): проброс порта Docker работает поверх сетевого стека контейнера, и если сам процесс внутри принимает соединения только с loopback-адреса контейнера, внешние запросы, приходящие через проброшенный порт с интерфейса docker0, до процесса просто не доходят.

Можно ли подключить один контейнер сразу к нескольким сетям одновременно?

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

Как оплачивать услуги?

Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.

Есть ли круглосуточная поддержка?

Да, дежурные инженеры на связи 24/7.

Выдаёте ли закрывающие документы?

Да, предоставим необходимые документы для оплаты.

Как оставить заявку?

Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.

заявка

Оставьте заявку

Ответим и поможем с подбором и настройкой.