Скидка до 55% и 3 курса в подарок 3 дня 08 :53 :44 Выбрать курс
Код
#статьи

Что такое Docker Compose и как он работает

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

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

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

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

Содержание


Что такое 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.3

Werkzeug — это зависимость 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 и даже написали с ним простое приложение. Чтобы продолжить обучение, рекомендуем дополнительные материалы:

Больше интересного про код — в нашем телеграм-канале.  Подписывайтесь!





Изучайте IT на практике — бесплатно

Курсы за 2990 0 р.

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

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована