Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Docker-сети: связь между контейнерами

Сети Docker: как контейнеры находят друг друга и почему localhost не работает

Практика docker-сетей: bridge и DNS по именам сервисов, почему localhost внутри контейнера — это он сам, compose-сети, публикация портов, изоляция и диагностика связей.

Прямой ответ

Правило, которое решает 90% проблем со связностью контейнеров: внутри сети Docker контейнеры обращаются друг к другу по именам сервисов, а не по localhost и не по IP. localhost внутри контейнера — это сам контейнер (поэтому «приложение в контейнере не видит БД на localhost:5432» — не баг, а модель), IP-адреса динамические и меняются при пересоздании, а DNS по имени сервиса (db, redis, api) работает из коробки в пользовательских bridge-сетях — именно их создаёт compose для каждого проекта. Отсюда три следствия: строка подключения из app-контейнера — postgresql://user:pass@db:5432/mydb (имя сервиса как хост); порты наружу публикуются только тому, что нужно снаружи (ports:), внутренняя связность портов не требует вовсе; и две сети изолированы по построению — сервисы в разных сетях compose-проектов друг друга не видят, пока вы явно не соедините.

Модель: сети, DNS, порты

По умолчанию docker run подключает контейнер к сети bridge — но без встроенного DNS по именам (там только IP или --link-легаси); пользовательские сети (docker network create mynet) дают DNS автоматически — именно поэтому compose создаёт собственную сеть проекта и подключает к ней все сервисы. Как это работает: каждый контейнер получает IP внутри сети и запись в встроенном DNS (127.0.0.11 внутри контейнера) по имени сервиса и алиасам; обращение ping db из соседнего контейнера резолвится в его текущий IP. Порты — два разных мира: expose/отсутствие публикации — порт доступен только внутри сети (норма для БД и редиса); ports: "5432:5432" — публикация на хост (нужна, когда с хоста подключается IDE или скрипт). Частая ошибка — публиковать всё подряд: внутренняя БД, торчащая на 0.0.0.0:5432 без пароля, — регулярная находка сканеров.

Compose-сети: дефолты и управление

Compose v2 для каждого проекта создаёт сеть <проект>_default и подключает к ней все сервисы — в 95% случаев этого достаточно, и явные networks: в файле не нужны. Явное управление подключают по задачам: фронт/бэк-разделение — публичная сеть (прокси + веб) и внутренняя (БД, кеш), БД подключена только к внутренней и извне недоступна даже теоретически; доступ наружу из контейнера — не «сеть», а адрес хоста: host.docker.internal (Docker Desktop) или extra_hosts: ["host.docker.internal:host-gateway"] (Linux) — стандартный способ достать сервис, живущий на хосте; связь двух compose-проектов — внешняя сеть (networks: [shared] с external: true в обоих файлах) вместо костылей с публикацией портов наружу. Имена сервисов в DNS стабильны: depends_on влияет только на порядок старта, не на доступность — «готовность» БД проверяется healthcheck'ом (схема стека — в статье про локальный docker-стек).

Диагностика связности: порядок проверок

Когда «не подключается», порядок механический. Первый шаг — изнутри контейнера: docker compose exec app sh и проверка резолва (getent hosts db / ping db) — не резолвится: сервис в другой сети или опечатка в имени (имя сервиса, не имя контейнера — при container_name резолвятся оба, но канон — имя сервиса). Второй — доступность порта из соседнего контейнера (nc -zv db 5432 или curl) — резолвится, но порт закрыт: приложение в контейнере-цели слушает не 0.0.0.0 (классика: сервер, забинденный на 127.0.0.1 внутри контейнера, недоступен никому, кроме самого себя — bind на 0.0.0.0 обязателен; та же ловушка с dev-серверами в preview-средах). Третий — с хоста на опубликованный порт (curl localhost:8080) — не работает: маппинг ports: проверьте (порядок «хост:контейнер»). Инструменты: docker network inspect <сеть> (кто подключён), docker compose ps (порты и статусы), логи целевого сервиса (может, он упал при старте и DNS тут ни при чём).

Практический сценарий: стек «app + db + redis + nginx»

Собираем типовую схему с правильной изоляцией. Сервисы: db (postgres, без публикации портов — наружу не нужен), redis (то же), api (python-приложение; EnvironmentFile с DATABASE_URL=postgresql://...@db:5432/app — хост db резолвится сетью), nginx (reverse proxy — единственный с публикацией 80:80; проксирует на — снова имя сервиса; конфиг-подходы в статье про [nginx](./nginx-reverse-proxy-nastroyka-praktika.md)). Сети:frontend(nginx + api) иbackend(api + db + redis) — api в обеих как мост, nginx до БД не дотянется даже при RCE. Healthcheck'и на db и redis,depends_on: condition: service_healthyу api — старт по готовности, а не по факту запуска. Для отладки с хоста — временная публикацияports: ["127.0.0.1:5432:5432"]` у db (только loopback, не 0.0.0.0) — IDE подключается, наружу не торчит. Стек поднимается одной командой, связь — по именам, изоляция — сетями, диагностика — по порядку из прошлого раздела.

FAQ

Чем сети Docker отличаются от Kubernetes-сервисов? Идея та же (DNS по имени, виртуальная сеть), но в K8s сеть плоская между подами, а Service — отдельный объект с типами доступа (ClusterIP/NodePort/LoadBalancer); навыки docker-сетей переносятся концептуально, синтаксис — другой. Для локальной имитации K8s — kind/k3s (ресурсы — в статье про Kubernetes на ноутбуке).

Почему container_name — зло для библиотечных образов? Фиксированное имя мешает поднять два инстанса стека (конфликт имён) и ломает масштабирование (--scale api=2 несовместимо с container_name); плюс DNS по имени сервиса работает и без него. Оставляйте имена по умолчанию, фиксируйте только в осознанных случаях.

Как контейнеру достучаться до интернета при корпоративном прокси? Прокси задаётся на уровне демона/клиента docker (конфиг прокси для build — в ~/.docker/config.json или build args); внутри работающей сети контейнеры ходят наружу через NAT хоста — отдельная настройка не нужна, если хост в сети.

Нужен ли host-режим сети? Почти никогда в разработке: network_mode: host убирает изоляцию и DNS-удобства (порты прямо на хосте, localhost работает «как на железе») — оправдан для специфичных сетевых инструментов и высокой производительности в Linux-прод-кейсах; в compose-стеках — антипаттерн.

Источники: документация Docker (networking, compose networking, compose services) — ссылки в шапке.