1. Анатомия docker-compose.yml: Декларативное описание инфраструктуры и синтаксис версий
Анатомия docker-compose.yml: Декларативное описание инфраструктуры и синтаксис версий
Представьте, что вам нужно запустить современное веб-приложение. Вам потребуется база данных PostgreSQL, кэш Redis, бэкенд на Python, фронтенд на React и, возможно, Nginx в качестве прокси-сервера. Если вы будете запускать каждый компонент через docker run, вам придется вручную создавать сети, прописывать длинные цепочки аргументов для проброса портов и следить за тем, чтобы контейнеры «видели» друг друга по именам. Одна ошибка в консольной команде — и вся связка рассыпается. Docker Compose решает эту проблему, переводя управление инфраструктурой из режима «ручного набора команд» в режим «описания желаемого состояния».
Декларативный подход против императивного
В системном администрировании и DevOps существуют два принципиально разных способа управления ресурсами. Императивный подход — это последовательность действий: «создай сеть», «запусти контейнер А», «подключи его к сети». Если на втором шаге произойдет сбой, система останется в промежуточном, неопределенном состоянии.
Docker Compose реализует декларативный подход. Вы описываете в файле docker-compose.yml, как должна выглядеть ваша система в финале. Инструмент сам анализирует текущее состояние хоста, сравнивает его с описанием в файле и выполняет ровно те действия, которые необходимы для достижения цели. Если контейнер уже запущен и его конфигурация не менялась, Compose его не тронет. Если же вы изменили версию образа в файле, Compose пересоздаст только этот конкретный сервис.
Этот файл становится «единым источником истины» (Single Source of Truth). Он хранится в Git вместе с кодом приложения, что позволяет любому разработчику развернуть идентичное окружение одной командой docker-compose up -d.
Структура файла и иерархия объектов
Файл docker-compose.yml использует формат YAML, который критичен к отступам. Ошибка в два пробела может полностью изменить логику описания или сделать файл невалидным. На верхнем уровне структуры всегда находятся четыре ключевых раздела:
Рассмотрим базовый скелет:
Здесь web-app — это не имя контейнера в системе Docker, а название сервиса внутри Compose. Docker Compose добавит к нему префикс проекта (обычно имя папки) и порядковый номер, создав реальное имя контейнера, например, myapp_web-app_1.
Эволюция синтаксиса: от версий к Спецификации
История версий Docker Compose часто вводит в заблуждение новичков. Долгое время выбор версии в начале файла был критически важен.
* Версия 1: Устарела. В ней не было разделов networks и volumes, а связи между контейнерами настраивались через механизм links, который сейчас считается избыточным.
* Версия 2.x: Ввела поддержку сетей и томов. Она была ориентирована на запуск на одном хосте.
* Версия 3.x: Была разработана для совместимости с Docker Swarm (режим оркестрации кластеров). В ней появились директивы deploy, но исчезли некоторые тонкие настройки ресурсов, доступные во второй версии.
Сегодня ситуация упростилась. Docker представил Compose Specification. Теперь поле version является информационным. Современный плагин docker compose (написанный на Go, в отличие от старого скрипта на Python docker-compose) игнорирует номер версии и стремится поддерживать все доступные параметры. Однако для обеспечения совместимости со старыми инструментами или CI/CD-пайплайнами хорошей практикой остается указание version: '3.8'.
Секция Services: детальный разбор
Каждый элемент в разделе services — это инструкция по запуску контейнера. Рассмотрим наиболее важные параметры, которые определяют поведение микросервиса.
Сборка против готового образа
У вас есть два пути: использовать готовый образ из реестра (например, Docker Hub) или собирать свой «на лету».
> Директива image указывает на готовый артефакт. Если образа нет локально, Docker попытается его скачать.
> Директива build указывает путь к Dockerfile.
Использование context важно: это папка, содержимое которой будет отправлено демону Docker для сборки. Если вы укажете корень проекта, сборка может занять вечность, так как Docker будет копировать все файлы, включая тяжелые папки node_modules или .git.
Порты и проброс трафика
Синтаксис портов часто путают. Правило всегда одно: HOST:CONTAINER.
* "8080:80" — порт 80 внутри контейнера будет доступен как 8080 на вашей физической машине.
* "127.0.0.1:5432:5432" — база данных будет доступна только с вашего локального компьютера, но не из внешней сети. Это критически важно для безопасности.
Важно понимать разницу между ports и expose. ports открывает доступ миру (или вашему хосту), а expose лишь декларирует, что сервис слушает определенный порт внутри внутренней сети Docker Compose. Другие сервисы в той же сети смогут достучаться до него и без ports.
Переменные окружения
Контейнеры должны быть универсальными. Мы не «зашиваем» пароль от базы данных в код, а передаем его через переменные окружения. В Compose это делается двумя способами:
.env), где хранятся настройки.yaml services: web: build: . depends_on: db: condition: service_healthy db: image: postgres healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 yaml x-python-common: &python-common logging: driver: "json-file" options: max-size: "10m" environment: DEBUG: "true"
services: auth-service: <<: *python-common build: ./auth
order-service:
<<: *python-common
build: ./orders
``
Префикс x- сообщает Compose, что это пользовательское поле, которое не нужно пытаться интерпретировать как стандартную директиву. Символ & создает ссылку, а * вставляет её содержимое. Это мощный инструмент для поддержания чистоты конфигурации в больших проектах.
Границы ответственности Docker Compose
Важно понимать, где заканчивается роль Compose. Это инструмент для локальной оркестрации и простых сред развертывания. Он отлично справляется с задачей «поднять всё одной командой на одной машине».
Однако Compose не умеет: * Автоматически масштабировать сервисы в зависимости от нагрузки на CPU (автоскейлинг). * Следить за состоянием контейнеров на разных физических серверах (для этого нужен Kubernetes или Docker Swarm). * Управлять сложными секретами и сертификатами в промышленном масштабе.
Тем не менее, знание анатомии docker-compose.yml` — это фундамент. Даже в Kubernetes концепции разделения на сервисы, сети и тома остаются прежними, меняется лишь масштаб и синтаксис описания. Умение грамотно структурировать декларативный файл позволяет минимизировать «дрифт конфигураций», когда на компьютере разработчика приложение работает, а на сервере — нет.
Проектируя свой первый стек, всегда стремитесь к минимализму: используйте официальные образы (желательно на базе Alpine Linux для экономии места), четко разграничивайте внутренние и внешние порты и не забывайте про Healthchecks. Это превратит ваш локальный стенд из хрупкой конструкции в надежную среду, готовую к любым экспериментам.