Что такое Docker Compose
Docker Compose нужен, когда приложение состоит не из одного контейнера, а из нескольких связанных сервисов.
Например, обычное веб-приложение может включать:
frontendилиnginx— принимает HTTP-запросы;app— выполняет бизнес-логику;db— хранит данные;redis— хранит cache или очередь задач;worker— выполняет фоновые задачи.
Запускать всё это отдельными docker run командами можно, но быстро становится неудобно.
Проблема: несколько контейнеров вручную
Представь стек из базы, backend и nginx:
docker network create demo-net
docker run -d --name db --network demo-net -e POSTGRES_PASSWORD=secret postgres:15-alpine
docker run -d --name app --network demo-net -e DATABASE_URL=postgres://postgres:secret@db:5432/postgres myapp:dev
docker run -d --name nginx --network demo-net -p 8080:80 nginx:alpine
Даже в таком маленьком примере нужно помнить:
- какую сеть создать;
- какие переменные окружения передать;
- какой контейнер должен видеть другой контейнер по имени;
- какой порт открыть наружу;
- в каком порядке всё останавливать и пересоздавать.
Если это написано только в README, легко ошибиться: один человек забыл переменную, другой запустил контейнер не в той сети, третий открыл не тот порт.
Docker Compose решает эту проблему: стек описывается в одном YAML-файле и запускается одной командой.
Что делает Compose
Compose описывает не отдельный контейнер, а маленькое приложение целиком.
В compose-файле ты описываешь желаемое состояние:
нужны такие сервисы
им нужны такие images
им нужны такие переменные
им нужны такие ports
они должны быть в такой сети
А Compose уже создаёт контейнеры, подключает их к сети проекта и применяет настройки.
Позже в курсе появится ещё одна важная тема — хранение данных, которые должны переживать пересоздание контейнера. Для этого в Compose используют volumes, но в первой статье достаточно понимать главную идею: Compose помогает описать и запускать несколько связанных сервисов как один стек.
Это важно: compose-файл — не скрипт с командами по шагам. Это декларативное описание. Ты не говоришь “сначала сделай это, потом это”. Ты описываешь, каким должен быть стек, а Compose приводит Docker к этому состоянию.
Минимальный compose-файл
Файл обычно называется compose.yaml или docker-compose.yml.
services:
web:
image: nginx:alpine
ports:
- "8080:80"
Здесь описан один сервис web.
services— список сервисов приложения;web— имя сервиса;image: nginx:alpine— какой image запускать;ports: "8080:80"— открыть порт8080на хосте и направить его на порт80внутри контейнера.
Запуск:
docker compose up -d
После этого nginx будет доступен с хоста:
curl http://localhost:8080
Пример из нескольких сервисов
Теперь пример ближе к реальному приложению:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- app
app:
image: myapp:dev
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/postgres
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
В этом файле:
webпубликует порт наружу;appвидит базу по имениdb;dbзапускает PostgreSQL с нужной переменной окружения;- Compose создаёт общую сеть проекта, поэтому сервисы могут обращаться друг к другу по именам.
Сейчас важно увидеть идею: один файл описывает весь стек и связывает сервисы, настройки, сеть и порты в одну картину.
Основные команды
В повседневной работе чаще всего нужны такие команды:
| Команда | Что делает |
|---|---|
docker compose up -d |
создать и запустить сервисы в фоне |
docker compose ps |
показать состояние сервисов проекта |
docker compose logs |
показать логи всех сервисов |
docker compose logs app |
показать логи одного сервиса |
docker compose exec app sh |
выполнить команду внутри уже запущенного сервиса |
docker compose restart app |
перезапустить один сервис |
docker compose down |
остановить стек и удалить контейнеры проекта |
Разбор docker compose up -d: docker compose читает compose-файл в текущей директории, up создаёт недостающие ресурсы и запускает сервисы, -d оставляет их работать в фоне.
Разбор docker compose down: Compose останавливает и удаляет контейнеры проекта и созданную сеть.
Проект Compose
Compose группирует контейнеры и сети в проект. Имя проекта обычно берётся из имени директории.
Если директория называется shop, ресурсы могут получить имена вроде:
shop-web-1
shop-db-1
shop_default
Имя проекта можно задать явно:
docker compose -p demo up -d
docker compose -p demo ps
docker compose -p demo down
Это полезно, когда один и тот же compose-файл нужно запустить несколько раз отдельно: например, для разных тестовых окружений.
exec и run
docker compose exec выполняет команду внутри уже запущенного контейнера сервиса:
docker compose exec db psql -U postgres
Это похоже на docker exec, только вместо имени контейнера указывается имя сервиса из compose-файла.
docker compose run создаёт новый одноразовый контейнер на основе сервиса:
docker compose run --rm app python manage.py migrate
В лабораторных для проверки состояния работающего сервиса чаще нужен exec. Для разовых задач вроде миграций или seed-команд часто используют run --rm.
Диагностика первого уровня
Если стек не стартует, не перезапускай всё наугад. Сначала посмотри состояние:
docker compose ps
docker compose logs --tail=80
docker compose logs app --tail=80
Если один сервис упал, смотри именно его:
docker compose ps app
docker compose logs app --tail=100
docker compose restart app
Compose создаёт обычные Docker containers и networks. Их можно смотреть обычными Docker-командами:
docker ps
docker network ls
Но изменять ресурсы Compose-проекта лучше через docker compose, чтобы состояние Docker не разъехалось с compose-файлом.
Когда запускать up повторно
docker compose up -d можно запускать повторно. Compose сравнит текущие контейнеры с конфигурацией и пересоздаст только то, что изменилось:
docker compose up -d
docker compose ps
Если ты изменил compose-файл, повторный up -d применит изменение. Если изменил только файл в bind mount, пересоздание может быть не нужно: контейнер уже видит файл с хоста.
В следующей статье разберём сам compose-файл как модель: что означает services, где объявлять основные секции, как читать поля сервиса и почему не все порты нужно публиковать наружу.
