Зачем нужны сети в Docker Compose: от изоляции до безопасности микросервисов
При запуске сервисов через Docker Compose без явной конфигурации сетей все контейнеры автоматически попадают в одну общую мостовую сеть с именем проекта. Это удобно для быстрого старта, но создает серьезные риски для production-среды: уязвимость одного сервиса может привести к компрометации всего приложения, сложность диагностики проблем и отсутствие четкой архитектурной сегментации. Согласно статистике, в первом полугодии 2025 года было зарегистрировано более 25 000 попыток атак на облачные и гибридные инфраструктуры, что делает вопрос изоляции критически важным.
Создание пользовательских сетей в Docker Compose превращает хаотичную группу контейнеров в структурированную, безопасную систему. Вы получаете контроль над тем, какие сервисы могут взаимодействовать друг с другом, организуете трафик по четким правилам и снижаете «атакуемую поверхность» вашего приложения. Это не просто настройка — это фундамент для построения отказоустойчивых и безопасных микросервисных архитектур.
Сеть по умолчанию vs. Пользовательские сети: в чем разница на практике
Основные различия между автоматически созданной сетью и пользовательскими сетями, которые вы определяете в docker-compose.yml, сводятся к контролю и прозрачности:
- Идентификация: Сеть по умолчанию получает автоматическое имя (например,
myproject_default), которое сложно отследить. Пользовательские сети имеют осмысленные, заданные вами названия (frontend,backend,database), что сразу отражает их роль в архитектуре. - IP-адресация: В сети по умолчанию Docker динамически назначает IP-адреса из пула, что затрудняет планирование и настройку брандмауэров. В пользовательских сетях вы можете задать фиксированную подсеть, диапазон адресов и даже статические IP для критичных сервисов.
- Изоляция: В сети по умолчанию все сервисы проекта видят друг друга, что противоречит принципам минимального необходимого доступа. Пользовательские сети позволяют создать строгие сегменты: например, база данных будет доступна только для backend-сервиса, а не для frontend.
- Диагностика: Проблемы связности в общей сети сложнее локализовать. В сегментированной архитектуре вы сразу понимаете, в каком сегменте произошел сбой.
Архитектурные паттерны: как правильно сегментировать типовое приложение
Для типичного стека приложения (например, Nginx, веб-приложение, PostgreSQL, Redis) лучшей практикой является создание трех отдельных сетей:
- Frontend сеть (например,
front-tier): В этой сети находится только reverse proxy (Nginx/Traefik). Она может быть подключена к внешнему миру или к другой внешней сети. Сервисы backend и database к этой сети не подключены. - Backend сеть (например,
app-tier): Сеть для взаимодействия frontend и backend сервисов. Здесь находятся Nginx и веб-приложение. База данных и кэш остаются изолированными. - Database сеть (например,
data-tier): Полностью внутренняя, изолированная сеть. В нее подключены веб-приложение и база данных (и, возможно, кэш). Это гарантирует, что к данным могут обращаться только сервисы, которым это действительно необходимо.
Такая сегментация аналогична организации офиса: бухгалтерия (database сеть), отдел разработки (backend сеть) и приемная (frontend сеть) имеют разный уровень доступа к информации и ресурсам.
Базовый синтаксис: создание и подключение сетей в docker-compose.yml
Конфигурация сетей в Docker Compose состоит из двух ключевых шагов: объявление сети на корневом уровне файла и подключение конкретных сервисов к этой сети. Рассмотрим минимальный рабочий пример для версии 3.8, который является современным стандартом.
Объявление сети на корневом уровне
Сети объявляются в отдельной секции networks в начале файла docker-compose.yml. Это определение создает сеть, но пока не связывает ее с сервисами.
version: '3.8'
services:
# Сервисы будут описаны ниже
networks:
backend:
# Базовая сеть без дополнительных параметров
database:
# Вторая сеть для изоляцииВ этом примере мы объявили две сети: backend и database. Docker Compose создаст их при запуске проекта.
Подключение сервиса к сети
Чтобы сервис мог использовать объявленную сеть, необходимо указать ее в секции networks внутри описания сервиса.
version: '3.8'
services:
webapp:
image: myapp:latest
networks:
- backend # Подключаем сервис к сети backend
db:
image: postgres:15
networks:
- database # Подключаем сервис к сети database
- backend # Сервис db также подключен к backend для связи с webapp
networks:
backend:
database:В этом конфигурации сервис webapp находится только в сети backend. Сервис db подключен к обеим сетям, что позволяет ему общаться с webapp через сеть backend, но также быть частью изолированной сети database. Внутри одной сети контейнеры могут находить друг друга по имени сервиса (в данном случае webapp и db) через встроенный DNS-сервер Docker.
Версии 2 и 3: ключевые отличия в сетевой конфигурации
Формат файла Docker Compose существенно изменился между версиями 2 и 3. Это часто приводит к ошибкам, когда код из старой документации или примеров не работает в современных проектах. Знание различий поможет избежать этих проблем.
Синтаксис версии 2: устаревший, но встречающийся
В версии 2 формата сети часто объявлялись неявно, непосредственно при подключении сервиса. Отдельная корневая секция networks могла отсутствовать.
version: '2'
services:
webapp:
image: myapp:latest
networks:
- backend
db:
image: postgres:15
networks:
- backend
networks:
backend:
driver: bridgeОбратите внимание, что даже при наличии секции networks, возможности управления (такие как ipam для задания подсети) были ограничены. Использование версии 2 для новых проектов не рекомендуется.
Синтаксис версии 3: современный стандарт
Версия 3 (и более поздние, например, 3.8) требует явного объявления всех сетей в корневой секции networks. Это делает конфигурацию более чистой и предоставляет доступ к расширенным параметрам.
version: '3.8'
services:
webapp:
image: myapp:latest
networks:
- backend
db:
image: postgres:15
networks:
- backend
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1Ключевое отличие: в версии 3 вы можете детально настроить драйвер сети (driver) и управление IP-адресами (ipam), что невозможно или сильно ограничено в версии 2. Для всех новых проектов следует указывать version: '3.8' или выше.
Продвинутые сценарии: внешние сети, статические IP и алиасы
После освоения базовой изоляции часто возникает необходимость в тонком управлении: интеграция с существующей инфраструктурой, фиксированные адреса для критичных сервисов или дополнительные DNS-имена.
Подключение к внешней, предсозданной сети
Когда ваш Compose-проект должен взаимодействовать с контейнерами, созданными вне этого проекта (например, с reverse proxy Traefik, запущенным отдельно), необходимо подключить сервисы к уже существующей внешней сети.
version: '3.8'
services:
webapp:
image: myapp:latest
networks:
- traefik-public
- app-internal
networks:
traefik-public:
external: true
name: traefik-public # Имя сети, созданной ранее через `docker network create`
app-internal:
driver: bridgeКлючевые параметры: external: true указывает, что сеть не будет создаваться Compose, а name задает точное имя существующей сети. Внимание: эта сеть должна быть создана (например, командой docker network create traefik-public) до запуска docker-compose up.
Ручное управление IP-адресацией (IPAM)
Для назначения статических IP-адресов контейнерам используется секция ipam (IP Address Management). Это важно, когда IP-адрес сервиса должен быть фиксированным для конфигурации брандмауэров, других систем или для гарантии постоянного внутреннего endpoint.
networks:
backend:
driver: bridge
ipam:
driver: default
config:
- subnet: "172.22.0.0/24"
gateway: "172.22.0.1"
ip_range: "172.22.0.0/28"
services:
database:
image: postgres:15
networks:
backend:
ipv4_address: 172.22.0.10 # Статический IP для этого контейнера в сети backendВ этом примере для сети backend задана подсеть 172.22.0.0/24, шлюз и даже диапазон адресов (ip_range). Сервису database явно назначен статический адрес 172.22.0.10. Предостережение: необходимо тщательно планировать адресацию, чтобы избежать конфликтов между контейнерами.
Алиасы (aliases) для резервирования имён
Алиасы предоставляют контейнеру дополнительные DNS-имена внутри сети. Это полезно для организации сервисов (например, указание роли) или создания резервных точек доступа.
services:
db-primary:
image: postgres:15
networks:
backend:
aliases:
- "postgres"
- "primary-db"
app:
image: myapp:latest
networks:
- backendКонтейнер db-primary будет доступен в сети backend не только по имени сервиса db-primary, но также по адресам postgres и primary-db. Сервис app может подключиться к базе данных, используя любое из этих имен.
Готовые примеры конфигураций для микросервисного стека
Ниже представлены две полные, готовые к использованию конфигурации, которые демонстрируют применение всех рассмотренных принципов.
Пример 1: Стандартная трехзвенная архитектура (Web + App + DB)
Это универсальный шаблон для классического веб-приложения с четкой сегментацией трафика.
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- front-tier
- app-tier
app:
image: my-webapp:latest
networks:
- app-tier
- data-tier
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
networks:
- data-tier
networks:
front-tier:
driver: bridge
app-tier:
driver: bridge
data-tier:
driver: bridgeАрхитектура и поток трафика:
- Сеть
front-tier: Изолированная сеть для входящего трафика. В идеале, толькоnginxдолжен быть здесь, но в данном примере он также подключен кapp-tierдля передачи запросов. - Сеть
app-tier: Связываетnginxиapp. База данныхpostgresв эту сеть не подключена. - Сеть
data-tier: Полностью внутренняя сеть для связиappиpostgres. Это гарантирует, что база данных недоступна напрямую из внешнего мира или даже из frontend-сервиса.
Пример 2: Архитектура с внешним прокси и кэшем
Более сложный сценарий, приближенный к production, с использованием внешней сети для reverse proxy и внутренней сети с ручной IP-адресацией.
version: '3.8'
services:
traefik:
image: traefik:v3.0
ports:
- "80:80"
- "443:443"
networks:
- traefik-public
- app-network
backend:
image: my-backend:latest
networks:
- app-network
aliases:
- "api"
redis:
image: redis:7
networks:
- app-network
ipv4_address: 172.30.0.5
networks:
traefik-public:
external: true
name: traefik-public
app-network:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24
gateway: 172.30.0.1Ключевые особенности:
traefikподключен к внешней сетиtraefik-public, которая уже существует в Docker, и к внутренней сети проектаapp-network.backendсервис находится только в внутренней сетиapp-networkи имеет алиасapi, по которому Traefik может его найти.redisполностью изолирован в сетиapp-networkи имеет статический IP-адрес172.30.0.5, который может быть использован в конфигурации приложения для гарантированного подключения.- Сервис
redisне подключен к внешней сетиtraefik-publicи, следовательно, недоступен извне, что повышает безопасность.
Для управления более сложными контейнерными инфраструктурами, особенно в кластерных средах, полезно понимать принципы оркестрации. В нашей базе знаний есть практическое руководство по Docker для системных администраторов и DevOps, которое охватывает архитектуру и развертывание многоконтейнерных приложений.
Диагностика и управление: команды для проверки и отладки
После настройки сетей важно иметь инструменты для проверки их состояния и диагностики проблем связности.
Ключевые команды Docker CLI для работы с сетями
docker network ls— список всех сетей в Docker, включая созданные Compose. Показывает имя, драйвер и область (scope).docker network inspect <network_name>— детальная информация о конкретной сети: подсеть, шлюз, список подключенных контейнеров с их IP-адресами и MAC-адресами. Это основной инструмент для проверки конфигурации.docker-compose ps— показывает статус всех сервисов текущего проекта, что помогает быстро понять, какие контейнеры запущены и готовы к взаимодействию.docker logs <service_name>— просмотр логов конкретного сервиса. Если контейнер не может подключиться к другому, ошибки часто видны здесь.
Как проверить связь между контейнеров
Эмпирическая проверка — самый надежный способ убедиться, что сеть настроена правильно.
- Запустите проект:
docker-compose up -d. - Подключитесь к одному контейнеру:
docker-compose exec <service_name> sh(илиbash). Например,docker-compose exec backend sh. - Внутри контейнера попробуйте разрешить DNS-имя другого сервиса:
nslookup redis. Успешный ответ подтвердит, что DNS работает. - Проверьте связность. Пинг (
ping redis) может не работать, если контейнер не отвечает на ping. Вместо этого используйте инструменты сетевого уровня, доступные в контейнере: например,curl http://redis:6379(если сервис ожидает соединения) илиnc -zv redis 6379для проверки открытого порта.
Если вы столкнулись с проблемами, которые не удается решить стандартными командами, возможно, требуется более глубокий анализ. Для комплексной диагностики сложных систем, таких как Kubernetes, рекомендуем ознакомиться с полным пошаговым гайдом по диагностике Custom Resources в Kubernetes.