Что такое Docker Compose и как он работает
Разбираемся в том, как превратить кучу сервисов приложения в единое целое.
Docker Compose — инструмент для запуска и управления приложениями, состоящими из нескольких Docker-контейнеров. Он позволяет описать все компоненты ПО в одном конфигурационном файле и запустить их одной командой. Это упрощает процессы разработки, тестирования и развёртывания проектов.
В этой статье мы разберёмся, что такое Docker Compose, как он работает, чем отличается от Docker, и закрепим теорию на практике — напишем простое приложение с серверной частью и базой данных.
Содержание
- Что такое Docker
- Что такое Docker Compose и из чего он состоит
- Как он работает
- Создание проекта с Docker Compose
- Дополнительные материалы для изучения
Что такое Docker
Docker — это инструмент для контейнеризации, который позволяет собрать приложение вместе со всеми библиотеками, настройками и зависимостями в единый контейнер, которой можно запустить в любой системе.
Главная особенность контейнеризации в том, что контейнеры работают изолированно от операционной системы и других сервисов. Поэтому разработчикам не нужно подстраиваться под конкретное окружение, а DevOps-инженерам проще разворачивать ПО, поддерживая единый и предсказуемый способ их запуска и работы.
Docker состоит из нескольких компонентов: Docker host, Docker daemon, Docker client и других. В этой статье мы не будем подробно описывать их функциональность и особенности работы. Но советуем вам перед продолжением чтения вспомнить основы Docker.
Читайте также:
Что такое Docker Compose
Docker Compose — это инструмент, который упрощает описание и управление приложениями, состоящими из нескольких Docker-контейнеров. Он позволяет запускать и контролировать связанные сервисы — например, фронтенд, бэкенд и базу данных — как единое целое.
Конфигурация приложения в Docker Compose описывается в YAML-файле (обычно compose.yml). В нём указываются все сервисы и их зависимости, поэтому инфраструктура становится частью кода и может храниться в репозитории проекта. После этого компоненты приложения запускаются одной командой, что удобно для разработки, тестирования и локального запуска проектов.
Основные компоненты Docker Compose
В 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 — это непосредственно платформа для контейнеризации. С её помощью разработчики упаковывают приложения и их зависимости в изолированные среды выполнения — контейнеры.
Docker Compose — это один из компонентов платформы Docker. Он помогает управлять несколькими контейнерами как единым приложением. Всё, что нужно от пользователя, — прописать сервисы, зависимости, порты и тома в файле compose.yaml, а Docker Compose автоматизирует их запуск и взаимодействие.
Как работает 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:Создаём проект с Docker Compose
По этому алгоритму соберём простое приложение из двух компонентов: сервера и базы данных. Сервер напишем на 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.3Werkzeug — это зависимость Flask, её не нужно отдельно импортировать в app.py.
Создаём Dockerfile
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"]Создаём конфигурационный файл Docker Compose
Наш конфигурационный файл будет описывать два сервиса: 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 и даже написали с ним простое приложение. Чтобы продолжить обучение, рекомендуем дополнительные материалы:
- Официальная документация к Docker. Основной источник актуальной информации про Docker Compose и Docker в целом.
- Книга «Docker. Вводный курс» Шона П. Кейна и Карла Маттиаса. Книга подробно описывает инструменты Docker и использование контейнеров для развёртывания приложений, включая интеграцию с облачными сервисами и Kubernetes.
- Курс «Docker для начинающих + практический опыт» на Stepik. Все базовые знания по Docker с лекциями, домашними заданиями и проектами для закрепления теории.
Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!