Что такое Docker Compose и как он работает
2026-02-21 06:20 Diff

#статьи

  • 21 янв 2026
  • 0

Разбираемся в том, как превратить кучу сервисов приложения в единое целое.

Иллюстрация: Оля Ежак для Skillbox Media

Пишет о сетях, инструментах для разработчиков и языках программирования. Любит готовить, играть в инди‑игры и программировать на Python.

Docker Compose — инструмент для запуска и управления приложениями, состоящими из нескольких Docker-контейнеров. Он позволяет описать все компоненты ПО в одном конфигурационном файле и запустить их одной командой. Это упрощает процессы разработки, тестирования и развёртывания проектов.

В этой статье мы разберёмся, что такое Docker Compose, как он работает, чем отличается от Docker, и закрепим теорию на практике — напишем простое приложение с серверной частью и базой данных.

Содержание

Docker — это инструмент для контейнеризации, который позволяет собрать приложение вместе со всеми библиотеками, настройками и зависимостями в единый контейнер, которой можно запустить в любой системе.

Главная особенность контейнеризации в том, что контейнеры работают изолированно от операционной системы и других сервисов. Поэтому разработчикам не нужно подстраиваться под конкретное окружение, а DevOps-инженерам проще разворачивать ПО, поддерживая единый и предсказуемый способ их запуска и работы.

Docker состоит из нескольких компонентов: Docker host, Docker daemon, Docker client и других. В этой статье мы не будем подробно описывать их функциональность и особенности работы. Но советуем вам перед продолжением чтения вспомнить основы Docker.

Docker Compose — это инструмент, который упрощает описание и управление приложениями, состоящими из нескольких Docker-контейнеров. Он позволяет запускать и контролировать связанные сервисы — например, фронтенд, бэкенд и базу данных — как единое целое.

Конфигурация приложения в Docker Compose описывается в YAML-файле (обычно compose.yml). В нём указываются все сервисы и их зависимости, поэтому инфраструктура становится частью кода и может храниться в репозитории проекта. После этого компоненты приложения запускаются одной командой, что удобно для разработки, тестирования и локального запуска проектов.

В Docker Compose можно выделить три основных компонента: Services, Networks и Volumes. Рассмотрим их подробнее.

Services (сервисы) — это контейнеры в контексте Docker Compose. Их описывает compose.yml, в котором может быть несколько параметров:

  • образ контейнера (image: nginx);
  • контекст сборки (build: .);
  • информация о портах (ports: «8080:80»);
  • тома, переменные окружения, сети и другие параметры.

Каждый сервис запускается в собственном контейнере.

Networks (сети). По умолчанию Docker Compose создаёт отдельную сеть для приложения, в которой сервисы могут взаимодействовать между собой. При необходимости можно указывать дополнительные сети.

Volumes (тома) используются для хранения данных и общего доступа к ним между контейнерами. Они позволяют сохранять информацию при перезапуске сервисов и настраиваются в разделе volumes.

Docker — это непосредственно платформа для контейнеризации. С её помощью разработчики упаковывают приложения и их зависимости в изолированные среды выполнения — контейнеры.

Docker Compose — это один из компонентов платформы Docker. Он помогает управлять несколькими контейнерами как единым приложением. Всё, что нужно от пользователя, — прописать сервисы, зависимости, порты и тома в файле compose.yaml, а Docker Compose автоматизирует их запуск и взаимодействие.

Цикл работы Docker Compose включает шесть шагов: это чтение конфигурации, построение или загрузка образов, создание сетей, запуск контейнеров, управление томами и отображение портов. Разберём каждый из них.

Работа начинается с того, что Docker Compose читает compose.yaml — конфигурационный файл, который описывает все компоненты приложения:

  • сервисы (services) — отдельные компоненты многоконтейнерного ПО, например бэкенд сервера или база данных;
  • образы или сборка (build) — описание образов или указание на то, где расположен Dockerfile для сборки;
  • сети (networks) — информация о взаимодействии сервисов между собой;
  • зависимости (depends_on) — указание на то, какие сервисы должны запускаться первыми;
  • тома (volumes) — описание места хранения данных и особенностей доступа к ним;
  • порты (ports) — информация о связи контейнера с внешними сервисами.

В этом разделе мы рассмотрим, как именно выглядит конфигурационный файл и зачем нужны его отдельные элементы. В следующем разделе — практической части гайда — составим compose.yaml для простого приложения с серверной частью и базой данных.

Посмотрим, как может выглядеть compose.yaml:

# Версия формата Docker Compose version: '3.8' # Описание всех сервисов приложения services: service1: # Первый сервис build: . # Указывает, что образ строится из Dockerfile в текущей папке ports: # Связываем порт 8080 хоста с портом 8080 контейнера - "8080:8080" depends_on: # Указывает зависимость от service2 - service2 service2: # Второй сервис image: example/image:1.0 # Используем готовый образ из реестра volumes: # Подключаем том для сохранения данных - data:/app/data # Определяем тома для хранения данных volumes: data:

Этот код нам ещё понадобится. По ходу раздела мы будем возвращаться к его фрагментам, чтобы рассказать о них подробнее или дополнить.

При чтении нашего compose.yaml Docker Compose проверит его синтаксис и составит план структуры приложения. Он поймёт, что мы описали два сервиса — service1 и service2, а также их настройки и зависимости. Если в файле будут ошибки, Compose остановит работу и выведет сообщение с информацией о них.

После чтения конфигурационного файла Docker Compose определяет, какие образы нужны для каждого компонента. Для этого есть два способа:

  • Если указан build, Compose собирает образ из Dockerfile в указанной директории.
  • Если указан image, Compose загружает готовый образ из реестра, например Docker Hub.

Мы используем первый вариант, поэтому в конфигурации присутствует параметр build:

services: service1: build: . # Сборка образа из Dockerfile в текущей директории service2: image: example/image:1.0 # Загрузка готового образа Dockerfile для service1: # Используем базовый образ (например, для приложения) FROM base-image:latest # Устанавливаем рабочий каталог WORKDIR /app # Копируем файл зависимостей (если есть) COPY dependencies.txt . # Устанавливаем зависимости RUN install-dependencies # Копируем остальной код приложения COPY . . # Указываем порт EXPOSE 8080 # Команда для запуска приложения CMD ["run-app"]

Для service1 указано «build: .». Compose находит в текущей папке Dockerfile и выполняет команды оттуда, собирая образ: копирует файлы, устанавливает зависимости и запускает серверы.

Для service2 Compose видит указание image: example/image: 1.0. Он загружает образ image: 1.0 из Docker Hub или использует его локальную версию.

Чтобы сервисы начали работать, их требуется запустить в контейнерах, учитывая зависимости, указанные в depends_on. Это гарантирует, что они стартуют в правильном порядке. Например, в нашем файле service1 запускается после service2, так как зависит от него.

services: service1: build: . depends_on: # Гарантируем, что service2 запустится первым - service2 service2: image: example/image:1.0

При чтении Compose видит depends_on: — service2 в описании service1 и запускает контейнер service2 перед ним.

Каждый сервис приложения превращается в один или несколько контейнеров, в зависимости от настройки replicas, если она указана. В нашем файле мы её не используем.

Без связи друг с другом контейнеры бесполезны. Docker Compose автоматически создаёт виртуальную сеть, в которой они могут взаимодействовать. Внутри этой сети сервисы обращаются друг к другу по именам — например, первый может подключиться ко второму по хостнейму service2. Укажем это в compose.yaml:

services: service1: build: . environment: # Указываем, как подключиться к service2 - SERVICE2_URL=http://service2:8081 depends_on: - service2 service2: image: example/image:1.0

По умолчанию Compose создаёт сеть с именем <папка_проекта>_default. Внутри этой сети контейнеры доступны по именам сервисов: второй — как service2, первый — как service1. Поэтому в настройках мы указываем SERVICE2_URL=http://service2:8081: service2 — это сетевое имя контейнера.

Compose настраивает тома — хранилища данных, которые сохраняются при перезапуске или удалении контейнеров. Это позволяет не терять состояние приложения. Тома описываются в разделе volumes и подключаются к нужным компонентам.

services: service2: image: example/image:1.0 volumes: # Подключаем том для сохранения данных - data:/app/data volumes: # Определяем именованный том data:

Compose создаёт именованный том data, который хранится на хост-машине в каталоге Docker. Этот том подключается к пути /app/data внутри контейнера service2, где приложение сохраняет свои данные. Даже если контейнер будет удалён, данные в data сохранятся и будут доступны при следующем запуске.

Compose связывает порты контейнеров с портами хост-машины (вашего компьютера или сервера), чтобы приложение было доступно извне — через браузер или API-клиент.

services: service1: build: . ports: # Связываем порт 8080 хоста с портом 8080 контейнера - "8080:8080"

После запуска приложение будет доступно по адресу http://localhost:8080. Если этот порт на хост-машине занят, Compose сообщит об ошибке — убедитесь, что порт свободен.

Итоговый вид файла compose.yaml:

# Версия формата Docker Compose version: '3.8' # Описание всех сервисов services: service1: # Первый сервис build: . # Строим образ из Dockerfile ports: # Отображаем порт - "8080:8080" depends_on: # Зависимость от service2 - service2 environment: # Настройка подключения к service2 - SERVICE2_URL=http://service2:8081 service2: # Второй сервис image: example/image:1.0 # Готовый образ volumes: # Том для сохранения данных - data:/app/data # Определение томов volumes: data:

По этому алгоритму соберём простое приложение из двух компонентов: сервера и базы данных. Сервер напишем на Python, а в качестве СУБД используем MongoDB.

Создайте папку my-app. По итогу работы в ней будет четыре файла: Python-приложение, список зависимостей, Dockerfile и compose.yaml.

Мы напишем простое приложение на Flask. Оно будет подключаться к MongoDB и возвращать сообщение.

Создайте файл app.py и вставьте в него код из блока ниже. Если необходимые библиотеки не установлены, установите их заранее.

# app.py from flask import Flask from pymongo import MongoClient import os app = Flask(__name__) # Получаем URL MongoDB из переменной окружения (имя сервиса 'db' из compose.yaml) mongo_url = os.getenv('MONGO_URL', 'mongodb://db:27017/mydatabase') # Подключаемся к MongoDB client = MongoClient(mongo_url) db = client['mydatabase'] # Используем базу данных 'mydatabase' # Проверяем подключение, добавляя тестовую запись try: db.test.insert_one({'message': 'Connected to MongoDB!'}) print('Connected to MongoDB') except Exception as e: print(f'MongoDB connection error: {e}') # Обращение к адресу http://localhost:5000/ выдаст тестовую запись @app.route('/') def home(): return 'Hello from Python and MongoDB!' # Запускаем сервер if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)

Запустите приложение. Если в MongoDB появилась тестовая запись, значит, подключение прошло успешно.

Чтобы Compose автоматически устанавливал требуемые Python-библиотеки при сборке образа, создайте в каталоге my-app файл requirements.txt:

Flask==2.0.1 pymongo==3.12.0 Werkzeug==2.0.3

Werkzeug — это зависимость Flask, её не нужно отдельно импортировать в app.py.

Dockerfile описывает Docker, как собрать образ для Python-приложения. В каталоге my-app создайте файл с именем Dockerfile (обязательно с заглавной буквы и без указания расширения) и вставьте в него следующий код:

# Используем официальный образ Python версии 3.9-slim FROM python:3.9-slim # Устанавливаем рабочий каталог внутри контейнера WORKDIR /app # Копируем файл зависимостей COPY requirements.txt . # Устанавливаем зависимости RUN pip install --no-cache-dir -r requirements.txt # Копируем остальной код приложения COPY . . # Указываем порт, который будет открыт EXPOSE 5000 # Команда для запуска приложения CMD ["python", "app.py"]

Наш конфигурационный файл будет описывать два сервиса: web и db. Создайте файл compose.yaml внутри папки my-app и скопируйте следующий код:

# Версия формата Docker Compose version: '3.8' # Описание всех сервисов services: web: # Сервис для Python-приложения build: . # Строим образ из Dockerfile в текущей директории ports: # Отображаем порт 5000 хоста на порт 5000 контейнера - "5000:5000" depends_on: # Указываем, что сервис зависит от db - db environment: # Переменные окружения для подключения к MongoDB - MONGO_URL=mongodb://db:27017/mydatabase db: # Сервис для MongoDB image: mongo:5.0 # Используем официальный образ MongoDB volumes: # Подключаем том для сохранения данных - dbdata:/data/db # Определяем тома volumes: dbdata: # Имя тома

Для проверки запущенных контейнеров введите в терминале:

docker compose ps

В выводе будет отображён статус контейнеров. Должно быть Up.

Если статус другой, то что-то пошло не так. Для выявления проблемы можно изучить логи:

docker compose logs

Из папки my-app выполните команду в терминале:

docker compose --file compose.yaml up --build -d

После этого Docker Compose:

  • Построит образ для сервиса web из Dockerfile.
  • Загрузит образ mongo: 5.0 для сервиса db.
  • Создаст сеть для связи сервисов.
  • Запустит сначала контейнер db, а затем web.

Теперь откройте браузер и перейдите по адресу http://localhost:5000. Вы увидите сообщение:

Hello from Python and MongoDB!

Всё получилось!

Чтобы терминал оставался доступным для работы, запускайте Docker Compose в фоновом режиме. Иначе он будет постоянно выводить логи и блокировать ввод команд. Для этого используйте флаг -d:

docker compose up -d

Чтобы остановить и удалить контейнеры, выполните:

docker compose down

Теперь вы знаете основы работы с Docker Compose и даже написали с ним простое приложение. Чтобы продолжить обучение, рекомендуем дополнительные материалы:



Курс с трудоустройством: «Профессия Разработчик + ИИ» Узнать о курсе