🐳 Раздел 14 · Вопрос #7

Что такое Docker Compose

В реальном мире приложение редко состоит из одного контейнера: бэкенду на Java нужны база данных PostgreSQL, кэш Redis, брокер сообщений Kafka и панель мониторинга.


🟢 Junior Level

30-секундный ответ

Docker Compose — это декларативный инструмент для оркестрации и одновременного запуска многоконтейнерных приложений (Multi-Container Applications) на одном сервере с помощью одного файла конфигурации compose.yaml (или docker-compose.yml).

В реальном мире приложение редко состоит из одного контейнера: бэкенду на Java нужны база данных PostgreSQL, кэш Redis, брокер сообщений Kafka и панель мониторинга.

  • Вместо того чтобы вручную выполнять десятки длинных команд docker run с настройкой портов, томов и сетей, вы описываете всю систему в YAML-файле.
  • Команда docker compose up -d одной операцией создаёт виртуальную сеть, выделяет тома и поднимает все взаимосвязанные сервисы в правильном порядке.

Наглядная аналогия из жизни

  • Docker CLI (docker run) — это управление отдельными музыкантами: вы подходите к барабанщику, гитаристу и пианисту и каждому отдельно говорите, когда начинать играть.
  • Docker Compose — это дирижёр оркестра: по единой партитуре (compose.yaml) он взмахом палочки запускает весь оркестр синхронно, следит за тем, чтобы музыканты слышали друг друга (сеть) и чтобы пианино было настроено до начала симфонии (depends_on).

Наглядный пример для Spring Boot 3 и PostgreSQL

# compose.yaml (в современных версиях поле 'version' устарело и не требуется)
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      # Сервисы обращаются друг к другу по имени сервиса 'postgres-db':
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres-db:5432/orders_db
      - SPRING_DATASOURCE_USERNAME=postgres
      - SPRING_DATASOURCE_PASSWORD=secret
    depends_on:
      postgres-db:
        condition: service_healthy # Ждём реальной готовности БД!

  postgres-db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=orders_db
      - POSTGRES_PASSWORD=secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  pgdata: # Именованный том для сохранения данных БД
# Запуск всей инфраструктуры в фоновом режиме:
docker compose up -d

# Просмотр логов бэкенда в реальном времени:
docker compose logs -f app

# Полная остановка всех сервисов и удаление сетей:
docker compose down

🟡 Middle Level

Внутренняя сеть и встроенный DNS (Embedded DNS)

Когда Docker Compose поднимает сервисы:

  1. Он автоматически создаёт изолированную виртуальную сеть типа bridge (по умолчанию project_default).
  2. Внутри этой сети активируется встроенный DNS-сервер Docker (доступный по служебному адресу 127.0.0.11).
  3. Имена сервисов в YAML становятся доменными именами: Java-приложению не нужно знать IP-адрес базы данных; вызов postgres-db:5432 автоматически резолвится в текущий внутренний IP-адрес контейнера базы данных.

ports vs expose

  • ports: ["8080:8080"]: пробрасывает порт контейнера на физический сетевой интерфейс хост-машины (Host Port Binding). Сервис становится доступен извне контейнерной сети (например, в браузере разработчика по адресу http://localhost:8080).
  • expose: ["5432"]: открывает порт только для других контейнеров внутри общей Docker-сети, не публикуя его на хосте. Это безопасный выбор для баз данных и внутренних брокеров, защищающий их от внешних атак.

Ловушка depends_on и правильный Healthcheck

Начинающие разработчики часто сталкиваются с ошибкой:

# ❌ НЕПРАВИЛЬНО: Приложение упадёт при старте!
services:
  app:
    depends_on:
      - postgres-db
  • Почему это ломается: по умолчанию depends_on гарантирует только факт запуска процесса контейнера БД.
  • Но СУБД PostgreSQL после старта выполняет инициализацию файлов и создание баз данных, что занимает 3–10 секунд.
  • Приложение Spring Boot стартует быстрее, пытается установить соединение с базой, получает Connection refused и падает с Application failed to start.
  • Решение: использовать condition: service_healthy в сочетании с директивой healthcheck: test: ["CMD-SHELL", "pg_isready"].

🔴 Senior Level

Интеграция Spring Boot 3.1+ с Docker Compose (Auto-Configuration)

В Spring Boot 3.1 появилась революционная нативная интеграция с Docker Compose (spring-boot-docker-compose):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-docker-compose</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
  1. Автоматический запуск: при выполнении команды mvn spring-boot:run в IDE Spring Boot сам находит файл compose.yaml в корне проекта и выполняет docker compose up.
  2. Динамическая инъекция Connection Details: Spring Boot самостоятельно парсит открытые порты Postgres, Redis или Kafka и динамически инжектирует JDBC URL, username и password в ApplicationContext без необходимости ручной настройки application.yml!
  3. Автоматическая остановка: при остановке приложения в IDE Spring Boot автоматически гасит контейнеры.

Сетевая сегментация безопасности (Zero Trust Networks)

В Production-окружении монолитная общая сеть является риском безопасности. Compose позволяет разделить топологию:

services:
  api-gateway:
    image: nginx:alpine
    ports: ["80:80"]
    networks: [frontend-net]

  order-service:
    image: order-service:1.0
    networks: [frontend-net, backend-net]

  postgres-db:
    image: postgres:16-alpine
    networks: [backend-net] # БД физически изолирована от Nginx!

networks:
  frontend-net:
  backend-net:

Злоумышленник, скомпрометировавший веб-сервер Nginx, физически не сможет отправить пакеты в порт 5432 базы данных PostgreSQL.


Горизонтальное масштабирование (--scale) и коллизия портов

Команда docker compose up --scale order-service=3 позволяет запустить 3 реплики сервиса.

  • Ловушка: если в compose.yaml объявлен статический маппинг ports: ["8080:8080"], запуск масштабирования упадёт с ошибкой Bind for 0.0.0.0:8080 failed: port is already allocated.
  • Решение для масштабирования: указывать только порт контейнера без привязки к порту хоста (ports: ["8080"]) для случайного выделения порта, либо использовать только внутреннюю сеть с балансировщиком нагрузки (Nginx/Traefik).

4 Tricky Questions

1. Почему команда docker compose up --scale service=3 падает с ошибкой, если у сервиса указан маппинг ports: ["8080:8080"], и как настроить балансировку?

Ответ: Сетевой стек операционной системы хоста не позволяет двум разным процессам занять один и тот же TCP-порт (8080) на одном сетевом интерфейсе (ошибка Address already in use). Чтобы масштабировать сервис:

  1. Убрать маппинг портов на хост или указать диапазон: ports: ["8080-8082:8080"] (или динамический порт "8080").
  2. Запустить перед масштабируемыми сервисами легковесный обратный прокси (Nginx или Traefik), подключённый к той же сети. Прокси принимает трафик на внешний порт 80 и балансирует входящие запросы между тремя контейнерами по их внутреннему DNS-имени сервиса (Docker DNS автоматически распределяет запросы методом Round-Robin).

2. В чём фундаментальная разница между командами docker compose down и docker compose down -v? К какой катастрофе может привести случайное добавление флага -v?

Ответ:

  • docker compose down: останавливает и удаляет запущенные контейнеры и созданные виртуальные сети, но оставляет нетронутыми именованные тома (Volumes). Данные базы данных на диске сохраняются в полной безопасности.
  • docker compose down -v (или --volumes): вместе с контейнерами безвозвратно удаляет все именованные тома, объявленные в секции volumes:! Если выполнить эту команду на сервере с рабочей базой данных, весь каталог /var/lib/postgresql/data будет мгновенно стёрт с диска без возможности восстановления.

3. Почему Java-приложение, запущенное локально в IDE на хост-машине (не в Docker), не может подключиться к БД по адресу jdbc:postgresql://postgres-db:5432/mydb, объявленному в compose.yaml?

Ответ: Доменное имя сервиса postgres-db регистрируется только во внутреннем DNS-сервере Docker (127.0.0.11), работающем исключительно внутри созданной виртуальной сети bridge. Операционная система хоста использует свой собственный системный DNS (роутер или провайдер) и не имеет доступа к DNS-таблицам Docker. Чтобы локальное приложение из IDE подключилось к БД:

  1. В секции postgres-db должен быть проброшен порт на хост: ports: ["5432:5432"].
  2. В локальном application.yml адрес должен указывать на хост: jdbc:postgresql://localhost:5432/mydb.

4. Что такое файлы оверрайдов (docker-compose.override.yml), и как они объединяются с основным файлом конфигурации?

Ответ: По умолчанию при запуске docker compose up движок автоматически ищет два файла: основной compose.yaml и файл переопределений docker-compose.override.yml, выполняя их глубокое слияние (Deep Merge):

  • Конфигурации объединяются: если в основном файле указан image, а в override указан ports: ["5432:5432"] и локальный build: ., то итоговый сервис объединит обе директивы.
  • Списки (например, ports, environment) конкатенируются.
  • Это позволяет держать в репозитории чистый базовый compose.yaml для тестовых стендов, а разработчикам локально создавать .override.yml (добавленный в .gitignore) для проброса локальных портов отладки и подключения исходников через volume.

🎯 Шпаргалка для интервью

Главное о Docker Compose

  • Что это: инструмент оркестрации многоконтейнерных систем на одном хосте.
  • Конфигурация: файл compose.yaml (секции: services, networks, volumes).
  • Сетевое взаимодействие: встроенный DNS (127.0.0.11); контейнеры общаются по имени сервиса.
  • Синхронизация старта: использовать depends_on в связке с condition: service_healthy.
  • Spring Boot 3.1+: библиотека spring-boot-docker-compose автоматически поднимает инфраструктуру при старте приложения.
  • Хранение данных: данные БД сохранять строго в именованных volumes:.
  • Ограничения: предназначен для одного хоста; не заменяет Kubernetes в production (нет multi-node, нет HPA, нет rolling update).

Docker Compose vs Kubernetes

| Критерий | Docker Compose | Kubernetes | | :— | :— | :— | | Среда применения | Локальная разработка, простые VPS | Крупный продакшен, облака | | Количество узлов | Строго 1 хост-сервер | Кластер из сотен серверов | | Автомасштабирование| ❌ Нет | 🟢 Есть (Horizontal Pod Autoscaler) | | Self-Healing | ❌ Ограничен (restart: always) | 🟢 Автоперезапуск и миграция подов | | Сложность порога входа | 🟢 Низкая (несколько часов) | 🔴 Высокая (недели обучения) |

Красные флаги на собеседовании (Чего говорить нельзя)

  • ❌ «depends_on гарантирует, что БД полностью готова к приёму запросов» — он ждёт только старта контейнера; нужен service_healthy.
  • ❌ «Docker Compose используется для управления кластером из 10 серверов в продакшене» — Compose работает только на одной машине.
  • ❌ «Чтобы удалить контейнеры, я всегда пишу docker compose down -v» — флаг -v уничтожает все данные баз данных в volumes!

Связанные вопросы