← Назад к вопросам

Как контейнеры общаются между собой в Docker Compose?

2.3 Middle🔥 121 комментариев
#DevOps и инфраструктура

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сетевое взаимодействие в Docker Compose

Докер Compose автоматически создаёт сеть и позволяет контейнерам обнаруживать друг друга по имени сервиса. Это работает благодаря встроенному DNS сервису.

Базовое взаимодействие

# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
    depends_on:
      - db
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Как это работает:

  • Контейнер web может обращаться к db по адресу db:5432 (имя сервиса + порт)
  • Docker Compose создаёт внутреннюю сеть с DNS, где имя db разрешается в IP контейнера
  • Нет необходимости знать IP адреса контейнеров — они динамические

Python код для подключения

# app/config.py
import os
from sqlalchemy import create_engine

# Для контейнера: db:5432
# Для локальной разработки: localhost:5432
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_PORT = os.getenv('DB_PORT', '5432')
DB_USER = os.getenv('DB_USER', 'user')
DB_PASSWORD = os.getenv('DB_PASSWORD', 'password')
DB_NAME = os.getenv('DB_NAME', 'mydb')

DATABASE_URL = f'postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
engine = create_engine(DATABASE_URL)

# Или просто использовать имя сервиса
DATABASE_URL = 'postgresql://user:password@db:5432/mydb'

Разные сценарии взаимодействия

1. FastAPI + PostgreSQL + Redis

version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:password@postgres:5432/app
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - redis
    networks:
      - app-network
  
  postgres:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=app
    networks:
      - app-network
  
  redis:
    image: redis:7
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Python код:

import os
import redis
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# PostgreSQL подключение
DB_URL = os.getenv('DATABASE_URL', 'postgresql://user:password@postgres:5432/app')
engine = create_engine(DB_URL)
SessionLocal = sessionmaker(bind=engine)

# Redis подключение
REDIS_URL = os.getenv('REDIS_URL', 'redis://redis:6379')
redis_client = redis.from_url(REDIS_URL)

# Использование
def get_cached_data(key):
    # Проверяем Redis кэш
    cached = redis_client.get(key)
    if cached:
        return cached
    
    # Получаем из БД
    session = SessionLocal()
    data = session.query(Model).first()
    session.close()
    
    # Кэшируем на 1 час
    redis_client.setex(key, 3600, str(data))
    return data

2. Telegram бот + PostgreSQL + Redis

version: '3.8'
services:
  bot:
    build: .
    environment:
      - BOT_TOKEN=your_token
      - DATABASE_URL=postgresql://user:password@db:5432/bot_db
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=bot_db
    volumes:
      - db_data:/var/lib/postgresql/data
  
  cache:
    image: redis:7-alpine
    volumes:
      - cache_data:/data

volumes:
  db_data:
  cache_data:

3. Микросервисы: API + Workers + Database

version: '3.8'
services:
  api:
    build: ./api
    ports:
      - "8000:8000"
    environment:
      - BROKER_URL=amqp://guest:guest@rabbitmq:5672/
      - DATABASE_URL=postgresql://user:password@db:5432/api_db
    depends_on:
      - db
      - rabbitmq
  
  worker:
    build: ./worker
    environment:
      - BROKER_URL=amqp://guest:guest@rabbitmq:5672/
      - DATABASE_URL=postgresql://user:password@db:5432/api_db
    depends_on:
      - db
      - rabbitmq
    deploy:
      replicas: 3  # 3 воркера
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=api_db
  
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"  # Management UI
    environment:
      - RABBITMQ_DEFAULT_USER=guest
      - RABBITMQ_DEFAULT_PASS=guest

# Порты при обращении между контейнерами:
# - api → db: db:5432
# - api → rabbitmq: rabbitmq:5672
# - worker → db: db:5432
# - worker → rabbitmq: rabbitmq:5672

Python код воркера:

# worker/tasks.py
from celery import Celery
from app.database import SessionLocal
from app.models import Order

broker_url = 'amqp://guest:guest@rabbitmq:5672/'
celery_app = Celery('tasks', broker=broker_url)

@celery_app.task
def process_order(order_id: int):
    """Обработка заказа в фоновом процессе"""
    session = SessionLocal()
    try:
        order = session.query(Order).filter(Order.id == order_id).first()
        if order:
            # Симуляция обработки
            order.status = 'processing'
            session.commit()
            
            # Длительная операция
            import time
            time.sleep(5)
            
            order.status = 'completed'
            session.commit()
    finally:
        session.close()

# api/routes.py
from fastapi import FastAPI
from worker.tasks import process_order

app = FastAPI()

@app.post('/orders')
def create_order(order_data: dict):
    session = SessionLocal()
    order = Order(**order_data)
    session.add(order)
    session.commit()
    
    # Отправляем задачу в очередь
    process_order.delay(order.id)
    
    return {'order_id': order.id, 'status': 'pending'}

Явные сети (Custom Networks)

version: '3.8'
services:
  web:
    build: .
    networks:
      - frontend  # Только фронтенд сеть
      - app-network  # И app сеть
  
  api:
    build: .
    networks:
      - app-network  # Только app сеть (web не может обратиться напрямую)
  
  db:
    image: postgres:15
    networks:
      - app-network  # Только app сеть

networks:
  frontend:
    driver: bridge
  app-network:
    driver: bridge

Результат:

  • web может обратиться к api и db
  • api может обратиться к db
  • db НЕ может обратиться к web или api (не может инициировать)

Обращение к портам

version: '3.8'
services:
  app:
    build: .
    environment:
      # ❌ Неправильно (localhost только для хоста)
      - API_URL=http://localhost:8000
      # ✅ Правильно (имя сервиса)
      - API_URL=http://api:8000
  
  api:
    ports:
      - "8000:8000"  # 8000:8000 означает localhost:container

# Из контейнера app:
# - http://api:8000 — внутренний адрес
# - http://localhost:8000 — НЕ РАБОТАЕТ (localhost внутри контейнера != хоста)

Health checks и зависимости

version: '3.8'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
  
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 5s
      timeout: 2s
      retries: 5

Отладка сетевого взаимодействия

# Войти в контейнер и проверить соединение
docker compose exec web bash

# Проверить DNS разрешение
nslookup db
ping db

# Проверить соединение с портом
telnet db 5432

# Проверить все контейнеры в сети
docker network ls
docker network inspect <network_name>

# Просмотр логов
docker compose logs db
docker compose logs web

Общие ошибки

# ❌ Ошибка 1: Используем localhost
DB_URL = 'postgresql://user:password@localhost:5432/db'
# В Docker: localhost != хост машины

# ✅ Правильно
DB_URL = 'postgresql://user:password@db:5432/db'

# ❌ Ошибка 2: Обращение до ready контейнера
depends_on:
  db:  # Это ждёт только пока контейнер запустится
    - db

# ✅ Правильно: ждём healthcheck
depends_on:
  db:
    condition: service_healthy

# ❌ Ошибка 3: Неправильный порт
redis_client = redis.Redis(host='redis', port=6379)  # 6379 — порт внутри контейнера

# ✅ Правильно
redis_client = redis.from_url('redis://redis:6379')

Лучшие практики

  1. Используй имена сервисов вместо IP — DNS автоматически разрешает имена
  2. Внутренние порты — используй порты внутри контейнеров, не хоста
  3. depends_on с healthcheck — ждём реальной готовности, не просто запуска
  4. Переменные окружения — передавай адреса через env, не хардкодь
  5. Custom networks — используй для изоляции и безопасности
  6. Логирование — проверяй логи контейнеров при проблемах
  7. Volumes — персистируй данные БД

Докер Compose автоматически разрешает все проблемы с сетью, если ты используешь имена сервисов вместо IP адресов.

Как контейнеры общаются между собой в Docker Compose? | PrepBro