Представьте, что вам нужно запустить веб-сервер в контейнере. Сначала хватает одной команды docker run. Но вскоре у неё появляются параметры: образ, порт, путь к файлам сайта. Их приходится либо помнить, либо искать в истории терминала.
Docker Compose решает эту практическую задачу: параметры запуска хранятся в compose.yaml рядом с проектом. В этом руководстве мы разберём файл построчно, запустим Nginx, откроем страницу на http://127.0.0.1:8080/ и убедимся, что сервер действительно отвечает. В конце остановим контейнер и посмотрим, как искать неисправность, если что-то пошло не так.
Для начала возьмём один сервис. На нём хорошо видно, где заканчивается конфигурация Compose и начинается работа Docker Engine. К нескольким сервисам можно переходить уже с этим пониманием.
Понадобятся установленный и запущенный Docker Engine (или Docker Desktop), плагин Compose, текстовый редактор и curl. Пример рассчитан на терминал Linux/macOS; на Windows используйте Docker Desktop и терминал с доступными docker и curl. Проверьте окружение:
docker compose version
docker info
Первая команда показывает, доступен ли Compose. Вторая проверяет соединение с Docker Engine. Если docker info не работает, сначала исправьте это: корректный YAML не запустит остановленный Docker. Для первого получения образа Nginx также понадобится доступ к реестру.
Docker Compose: что это и как работает
Docker Compose — инструмент для описания и запуска приложения из одного или нескольких контейнерных сервисов. compose.yaml задаёт желаемую конфигурацию, команда docker compose читает её и взаимодействует с Docker Engine, который создаёт и запускает контейнеры. Это соответствует модели приложения Compose.
Полезно сразу различать три понятия. Образ — подготовленная основа контейнера, в нашем случае Nginx. Сервис — описание его запуска: образ, порты, каталоги и прочие настройки. Проект объединяет сервисы и связанные ресурсы. Даже один сервис web образует Compose-проект.
Compose не запускает контейнеры вместо Docker Engine: он читает конфигурацию и передаёт движку параметры запуска. Когда появится база данных или второй сервер, их можно будет описать рядом и управлять ими одной командой.
А вот Dockerfile решает другую задачу: описывает сборку собственного образа. Compose может запускать такой образ, но в нашем примере он уже готов, поэтому Dockerfile не понадобится.
Используйте современную команду docker compose с пробелом. В старых примерах встречается docker-compose. Если изменить конфигурацию, повторный docker compose up -d применит её и при необходимости пересоздаст контейнер — подробнее об этом в справке up.
Как устроен compose.yaml
Имя compose.yaml — предпочтительный вариант имени Compose-файла. Поддерживаются и другие варианты, но для нового примера достаточно одного файла в корне проекта. Поле version для современного Compose считается устаревшим и в этом примере не требуется. Это описано в Compose Specification и справке по version.
Наш compose.yaml будет таким:
name: xnova-compose-demo
services:
web:
image: nginx:stable-alpine@sha256:0e52bd054126cdc040b1276c0af347aeb02a41b9cfe305d6094767ac30e97949
ports:
- "127.0.0.1:8080:80"
volumes:
- ./site:/usr/share/nginx/html:ro
services.web задаёт сервис с именем web; image выбирает образ. Публикация 127.0.0.1:8080:80 направляет запросы на порт 8080 локального хоста в порт 80 контейнера. Каталог ./site монтируется в каталог статических файлов Nginx только для чтения. Параметры сервисов описаны в официальной справке.
Тег nginx:stable-alpine оставлен в строке для читаемости, а суффикс @sha256:… закрепляет фактическое содержимое образа. Для проверенного запуска этот digest разрешился в Nginx 1.30.6 на linux/arm64. На другой архитектуре Docker использует соответствующий вариант того же multi-platform образа.
Верхнеуровневое name даёт проекту устойчивое имя xnova-compose-demo. Оно помогает отличить его контейнеры и сеть от ресурсов других проектов. По умолчанию Compose выбирает имя из каталога, если его не переопределили; документация name описывает этот приоритет.
У строки ports три части: IP интерфейса хоста, порт хоста и порт внутри контейнера. Мы намеренно используем 127.0.0.1, а не открываем 8080 на всех интерфейсах. Это ограничивает доступ локальным хостом в обычной конфигурации, но не обещает полной изоляции: в Docker Engine до версии 28.0.0 существовало исключение для соседей по L2-сети. Не используйте такой стенд для размещения секретов.
volumes здесь задаёт bind mount: Docker подключает каталог site с вашей машины внутрь контейнера. Флаг ro запрещает запись из контейнера в этот каталог. Это не именованный том Docker: файлы остаются в site и после удаления контейнера. Отступы имеют значение, поскольку YAML описывает вложенные элементы.
Схема связывает поля файла с фактическим маршрутом запроса и файла страницы:
Docker Compose: пример приложения из двух файлов
Соберём небольшой проект: один файл опишет запуск контейнера, второй будет содержать страницу. На Linux/macOS создайте каталог и перейдите в него:
mkdir -p compose-demo/site
cd compose-demo
Структура проекта выглядит так:
compose-demo/
├── compose.yaml
└── site/
└── index.html
В compose.yaml сохраните конфигурацию из предыдущего раздела. А в site/index.html добавьте саму страницу:
<!doctype html>
<html lang="ru">
<head><meta charset="utf-8"><title>Compose demo</title></head>
<body><h1>Compose работает</h1></body>
</html>
Каталог должен называться site, файл — index.html, без дополнительного расширения .txt. Путь ./site в Compose-файле отсчитывается от каталога, где лежит этот файл.
Официальное описание образа Nginx использует каталог /usr/share/nginx/html для размещения статических файлов. Именно сюда Compose подключит нашу папку. Nginx прочитает HTML непосредственно из неё: пересобирать образ для изменения текста страницы не нужно.
Если не хочется создавать файлы вручную, их можно взять из проверенной версии примера XNova. Там же лежат тесты и workflow, который запускает Compose в GitHub Actions.
Как запустить приложение и проверить результат
Перейдите в каталог compose-demo, где находится compose.yaml. Сначала проверьте описание сервиса:
docker compose config -q
echo $?
docker compose config -q проверяет конфигурацию и ничего не печатает при успешной проверке. Команда echo $? сразу после неё покажет код завершения в Unix-терминале: здесь ожидается 0. Если проверка не прошла, исправьте YAML до запуска контейнеров. Чтобы увидеть модель, которую прочитал Compose, выполните docker compose config без -q. Подробности — в документации команды.
Теперь можно запустить сервер:
docker compose up -d
docker compose ps
При первом запуске Docker может скачать образ. up -d создаёт и запускает контейнер в фоне, а ps показывает его состояние и опубликованный порт. Но статус Up ещё не означает, что веб-сервер готов отвечать. Проверим его запросом:
curl -fsS http://127.0.0.1:8080/
Если всё работает, вы увидите HTML из файла site/index.html, в том числе строку:
<h1>Compose работает</h1>
Отдельно проверьте HTTP-статус:
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
Ожидается 200. Можно открыть тот же адрес в браузере на этом компьютере. Если соединение не установлено или статус другой, переходите к разделу диагностики.
При технической проверке этого материала Docker Engine 24.0.6 и Compose 2.22.0 запустили Nginx 1.30.6 на linux/arm64; запрос вернул исходный HTML и HTTP 200. Это результат конкретного запуска; на вашем компьютере его нужно подтвердить собственным запросом, а не только успешным up -d.
Команды Docker Compose для повседневной работы
Когда страница открылась, пригодятся ещё несколько команд. docker compose ps показывает состояние контейнеров проекта. Если нужно понять, что происходило внутри Nginx, посмотрите последние сообщения сервиса:
docker compose logs --tail=30 web
Логи помогают при диагностике, но сами по себе не доказывают, что сервер отвечает по HTTP.
Если вы хотите ненадолго остановить контейнер и затем запустить тот же экземпляр, используйте docker compose stop и docker compose start. В отличие от них, down удаляет контейнеры и созданную Compose сеть.
Пока контейнер запущен, поменяйте заголовок в site/index.html и повторите curl -fsS http://127.0.0.1:8080/. Если текст изменился в ответе, это показывает действие bind mount. После проверки верните исходный HTML.
Закончив работу, выполните:
docker compose down
docker compose ps
После down в ps не должно остаться контейнеров этого проекта. При обычном down локальная папка site и скачанный образ Nginx сохраняются. Не добавляйте -v без необходимости: этот параметр удаляет также тома согласно правилам down. В нашем примере вместо именованного тома используется bind mount.
Почему приложение не открылось
Если страница не отвечает, полезнее искать неисправность по порядку, чем менять сразу несколько настроек.
- Команда
docker composeне найдена. Проверьтеdocker compose versionи установку Compose plugin. Когда команда заработает, отдельно убедитесь, чтоdocker infoможет связаться с Docker Engine. - Нет соединения с Docker Engine. Проверьте, запущен ли Docker Desktop/Engine и есть ли у пользователя права доступа. Только после этого повторяйте
up -d. docker compose config -qзавершился с ошибкой. Посмотрите отступы YAML, уровниservices,ports,volumesи кавычки вокруг значения порта. Исправьте файл и повторите проверку конфигурации.- Порт 8080 занят. Замените публикацию на
"127.0.0.1:8081:80", выполнитеdocker compose up -dи откройтеhttp://127.0.0.1:8081/. Порт80внутри контейнера менять не нужно. - Контейнер запущен, но страница не та или не открывается. Посмотрите
docker compose ps,docker compose logs --tail=30 webи наличие файлаsite/index.htmlрядом сcompose.yaml. Если контейнер завершился, его можно увидеть командойdocker compose ps --all.
После исправления повторите короткую цепочку: проверка YAML → запуск контейнера → HTTP-запрос. Для последнего используйте нужный порт: 8080 либо 8081, если вы меняли публикацию. Закончив диагностику, остановите проект через docker compose down.
Когда нужен Compose и что изучать дальше
Вы уже прошли весь путь: описали сервис в compose.yaml, проверили конфигурацию, запустили контейнер, получили HTTP-ответ и завершили работу. Такой подход пригодится для локальной разработки и тестовых стендов, где параметры запуска должны оставаться рядом с кодом.
В нашем примере всего один готовый образ и папка с HTML. Мы не собирали собственный образ, не подключали базу данных, не настраивали HTTPS, секреты и устойчивость к сбоям. Успешный запрос к локальной странице не означает, что конфигурация готова к production.
Следующий полезный шаг — добавить второй сервис, например PostgreSQL, и разобраться, как контейнеры находят друг друга, что означает готовность сервиса и как сохраняются данные. Но главная основа уже есть: теперь понятно, что описывает Compose, что делает Docker Engine и какой проверкой отделить работающий сервер от просто запущенного контейнера.
Источники
- Назначение Compose, модель проекта и предпочтительное имя compose.yaml
- Compose Specification: сервисы и состав файла
- Устаревшее поле version и назначение name
- Поля сервисов, публикация портов и bind mounts
- config -q: проверка разрешённой конфигурации
- up -d: создание и запуск сервисов
- down: остановка и удаление ресурсов проекта
- logs: просмотр журналов контейнеров сервиса
- ps: состояние контейнеров и опубликованных портов проекта
- stop: остановка контейнеров без удаления
- start: повторный запуск существующих контейнеров
- Публикация портов на 127.0.0.1 и оговорка для Docker Engine до 28.0.0
- Официальный пример размещения статического HTML в контейнере Nginx
- Проверенный снимок companion-проекта с Compose-файлом, HTML, тестами и CI