Что такое Docker Compose

Docker Compose - это средство автоматизации работы с Docker. В то время как все задачи по работе с докером можно решить непосредственно через утилиту docker, подавляющее большинство этих задач будет проще реализовать с помощью Docker Compose.
Docker Compose - так же один из первых шагов к управлению вашими сервисами в парадигме IaC (Infrastructure as Code). Когда для того чтобы выкатить что-либо вы используете не набор команд а-ля docker run с кучей параметров, и при этом не одну команду docker run, а кучу таких команд для того чтобы выкатить каждый контейнер. А запускаете весь набор контейнеров нужных для работы вашего приложения с помощью одной только команды docker compose up -d, которая прочитает файл compose.yaml, интерпретирует его и поднимет все описанные там сервисы.
Установка Docker Compose
Если следовать официальной справке то Docker Compose устанавливается сразу же вместе с Docker.

Что такое Docker
Более подробно про Docker и его установку можно прочитать на этой странице
Что такое DockerСтруктура compose.yaml
-rw-rw-r-- 1 gitlab-runner gitlab-runner 3.5K Jul 4 11:37 README.md
drwxrwxr-x 9 gitlab-runner gitlab-runner 4.0K Jul 5 10:53 app
-rw-rw-r-- 1 gitlab-runner gitlab-runner 1.4K Jul 4 11:37 compose.dev.yaml
-rw-rw-r-- 1 gitlab-runner gitlab-runner 3.8K Jul 4 18:07 compose.yaml
drwxrwxr-x 5 gitlab-runner gitlab-runner 4.0K Jul 4 11:37 storage
drwxrwxr-x 4 gitlab-runner gitlab-runner 4.0K Jul 4 11:37 tests
Приоритетным именем файла, следуя рекомендациям разработчиков Docker, на данный момент является compose.yaml. Имя docker-compose.yaml тоже распознаётся docker compose, но считается устаревшим форматом имени.
Пример compose.yaml для Nginx+PostgreSQL
Это лишь сферический в вакууме пример
services:
web:
image: nginx:1.27
container_name: app-web
ports:
- "80:80" # nginx смотрит наружу на 80-й порт хоста
volumes:
- ./html:/usr/share/nginx/html # само содержимое сайта, которое будет храниться в папке html возле compose.yaml
- ./nginx.conf:/etc/nginx/nginx.conf:ro # конфиг nginx (read-only), который будет лежать также рядом с compose.yaml
depends_on: # ждём пока запустится сервис db перед запуском сервиса web
- db
environment:
- NGINX_HOST=myapp.local
restart: unless-stopped
networks:
- appnet
db:
image: postgres:17 # фиксированная версия PostgreSQL
container_name: app-db
ports:
- "5432:5432" # проброс для внешних клиентов (pgAdmin, DBeaver)
environment:
POSTGRES_USER: appuser # создаст пользователя и БД при первом запуске
POSTGRES_PASSWORD: ${DB_PASSWORD} # пароль через переменную окружения хоста
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data # данные хранятся в именованном томе, а не в контейнере
restart: unless-stopped
networks:
- appnet
volumes:
pgdata: # именованный том — переживёт удаление контейнера
networks:
appnet:
driver: bridge
Ключевые моменты:
- depends_on: - db
- Гарантирует, что контейнер db запустится до web. Важно: depends_on ждёт старта контейнера, но не готовности PostgreSQL принимать соединения.
- POSTGRES_USER / PASSWORD / DB
- Официальные переменные образа postgres. При первом запуске контейнер создаст указанного пользователя и базу. Если том `pgdata` уже содержит данные — переменные игнорируются.
- ${DB_PASSWORD}
- Синтаксис подстановки. Compose возьмёт значение из переменной окружения хоста, либо из файла `.env` рядом с `compose.yaml`. Пароль не хранится в yaml — это принципиально важно для безопасности.
- volumes: pgdata
- В отличие от bind-mount вольюмов, как в сервисе web, именованный том управляется Docker'ом. Данные базы не потеряются при `docker compose down`, пока вы явно не удалите том командой `docker compose down -v`, даже если вы удалите все файлы, которые лежат в папке вместе с compose.yaml.
- Общая сеть appnet
- Оба контейнера находятся в одной bridge-сети. Nginx может обращаться к PostgreSQL по имени сервиса: `db:5432` (Docker Compose автоматически создаёт DNS-записи для имён сервисов, доступные внутри этой сети).
Как работать с compose.yaml
Чтобы поднять сервисы
Нужно перейти в папку с compose.yaml и выполнить команду docker compose up -d. Ключ -d нужен для того чтобы docker compose не забрал у нас контроль над консолью.

Если мы будем запускать компоуз командой docker compose up, мы увидим логи запуска, и мы будем их видеть всё время пока сервисы работают. Проблема лишь в том что работают они только пока компоуз контролирует вывод в консоль. Если мы нажмем Ctrl+C - мы вернём себе контроль над консолью, но сервисы остановятся. -d позволит нам запустить компоуз в фоне, где он будет работать до тех пор пока мы его принудительно не погасим.
Если мы хотим запустить компоуз в фоне и сразу же подключиться к потоку логов, мы можем использовать команду docker compose up -d && docker compose logs -f. В этом случае Ctrl+C остановит вывод логов в консоль, а все сервисы продолжат работать пока не будут остановлены принудительно. У docker compose есть множество флагов. Рассмотренные нами это лишь капля в море.
Чтобы посмотреть логи
Чтобы посмотреть логи всех сервисов запущенных через docker compose, находясь в одной папке с компоузом, мы можем выполнить команду docker compose logs, это выведет все накопленные компоузом логи.
Чтобы посмотреть последние N строк логов, можно использовать команду docker compose logs --tail N, где N - это количество последних строк логов которые мы хотим увидеть, например docker compose logs --tail 10
Чтобы подключиться к потоку логов компоуза, мы можем использовать команду docker compose logs -f. Это сначала выведет все накопленные компоузом логи и как только дойдёт до конца, подключится к потоку логов и будет выводить на экран все новые появляющиеся записи.
Но если компоуз накопил тысячи и десятки тысяч логов, мы умрём ждать пока наконец подключимся к потоку и увидим поступающие логи, на этот случай пригодится команда например docker compose logs -f --tail 10. Она выведет последние N строк и сразу же подключится к потоку. Не придётся сидеть и ждать пока docker compose сперва выведет все логи, и только потом начнёт показывать актуальные.
Если мы хотим увидеть логи конкретного сервиса, а не всех вообще, по аналогии с вышеприведёнными командами, мы можем использовать имя сервиса чьи логи хотим увидеть, например docker compose logs web. Аналогично для конкретного сервиса можно использовать ключи -f, --tail и многие другие

Чтобы посмотреть какие сервисы запущенны
Чтобы посмотреть какие сервисы запущенные из текущего compose.yaml у нас сейчас бегают на докерноде, можно использовать команду docker compose ps. Она покажет все сервисы находящиеся непосредственно в статусе Running. Если же мы хотим увидеть ещё и упавшие или выключенные сервисы, мы можем использовать команду docker compose ps -a .
Чтобы посмотреть вообще ВСЕ сервисы что у нас бегают на докерноде, можно использовать команду docker ps. Аналогично чтобы увидеть все сервисы включая упавшие, можно использовать команду docker ps -a
Внутреннее взаимодействие между сервисами
Между контейнерами одного компоуза, сервисы могут обращаться друг к другу по автоматически созданному Docker, внутреннему DNS имени. Например из контейнера web, контейнер db будет доступен просто по имени db.
Остановка сервисов docker compose
Остановка сервисов выполняется командой docker compose stop, она лишь остановит контейнеры но не удалит их. Чтобы остановить сервисы и удалить контейнеры, нужно использовать команду docker compose down. Она погасит и удалит контейнеры, при этом вольюмы, как созданные с помощью docker (pgdata) так и прокинутые просто из папки (./html, ./nginx.conf), останутся нетронутыми. Чтобы удалить и контейнеры и docker волюьмы, пригодится команда docker compose down -v. Вольюмы прокинутые из папок в любом случае придётся удалить руками.

Одна ошибка и ты ошибся
Не смотря на то что вольюмы прокинутые внутрь контейнеров переживают команду docker compose down, нужно относиться к этой команде очень внимательно, и перечитывать compose.yaml перед её применением. Потому что если какие-то данные которые должны быть сохранены, по недосмотру не сохраняются в volume, или внешнюю папку прокинутую внутрь контейнера - они живут только внутри контейнера. А это значит что подобные данные не переживут убийство контейнера командой docker compose down.
Зависимость между сервисами
Пример директивы depends_on описанный в compose.yaml выше - лишь гарантирует что сервис web запустится после сервиса db. При этом компоуз не будет дожидаться закончил ли сервис db инициализацию и т.д. Чтобы компоуз дождался запуска и инициализации какого-либо сервиса и только потом запускал зависимый от него сервис, нужно использовать механизм хелсчеков (healthcheck) и различные условия.
Docker Compose v2 против v1
- Docker Compose v2 - Это плагин для Docker CLI, обращение к нему происходит с помощью команды
docker compose(без тире), это актуальный способ работы с компоузом. - Docker Compose v1 - это отдельный бинарник, обращение к нему происходит с помощью команды
docker-compose(с тире), это устаревший (deprecated) бинарник.
Best Practices
В продакшене крайне не рекомендуется использовать :latest теги у докер имиджей. При разработке compose.yaml, необходимо указывать конкретную версию имиджа под которую писался манифест. Потому что со временем у используемого имиджа могут измениться различные параметры, переменные среды которые он принимает и т.д. И в один прекрасный день при перезапуске компоуз может не взлететь.
Секреты лучше выносить в .env файлы которые хранятся локально возле compose.yaml и никогда не попадают наружу. Это неплохая практика на старте, но в идеале хранить секреты лучше в сервисах типа HashiСorp Vault . HashiСorp Vault к слову можно развернуть точно так же через Docker Compose.
В случае же использования .env файла, который идеален в начале нашего пути, его не нужно коммитить в git, чтобы не дескредитировать чувствительные данные и стоит добавить в .gitignore в случае если вы уже используете репозитории в своей работе.
