Урок 7 из 8 · 25 мин · обновлено

Docker Compose: несколько контейнеров одним файлом

Как описать сайт, базу данных и веб-интерфейс в одном файле compose.yaml и запускать их одной командой: сервисы, порты, тома, сборка своего образа, файл .env для паролей и команды up, ps, logs и down.

Зачем Compose

В прошлом уроке стенд из базы и Adminer потребовал нескольких длинных команд: создать сеть, запустить базу с томом и паролем, запустить Adminer с портом. Через неделю их не вспомнить, а коллеге придётся пересылать инструкцию.

Docker Compose описывает все контейнеры проекта в одном файле compose.yaml. Весь стенд поднимается одной командой — у вас, у коллеги и на сервере одинаково. Compose входит в Docker Desktop. На Linux его ставят пакетом docker-compose-plugin из того же репозитория, что и Docker Engine.

Проверьте:

Терминал
docker compose version

Команда пишется через пробел: docker compose. Старый вариант через дефис docker-compose — это прежняя, отдельная программа, сейчас её не используют.

Первый compose.yaml

Вернитесь в папку my-site из урока 4 — там уже лежат index.html и Dockerfile. Создайте рядом файл compose.yaml:

YAML
services:
  web:
    build: .
    ports:
      - "8080:80"

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

  adminer:
    image: adminer
    ports:
      - "8081:8080"
    depends_on:
      - db

volumes:
  pgdata:

В файле три сервиса — три контейнера:

  • web — ваш сайт: build: . собирает образ из Dockerfile в этой папке;
  • db — PostgreSQL с паролем и томом, без портов наружу;
  • adminer — веб-интерфейс к базе на порту 8081.

depends_on задаёт порядок: сначала запустить db, потом adminer. Внизу объявлен том pgdata.

В YAML важны отступы: вложенность показывают пробелами, табуляция не допускается. Самая частая ошибка в compose.yaml — съехавший отступ.

Запуск и остановка

Терминал
docker compose up -d

Compose соберёт образ сайта, скачает недостающие образы, создаст сеть и том и запустит три контейнера. Откройте http://localhost:8080 — сайт, и http://localhost:8081 — Adminer. В Adminer укажите сервер db: сервисы одного файла сами попадают в общую сеть и находят друг друга по имени сервиса.

Остальные команды:

Терминал
docker compose ps         # контейнеры проекта
docker compose logs db    # логи сервиса db
docker compose logs -f    # логи всех сервисов в реальном времени
docker compose stop       # остановить, не удаляя
docker compose down       # остановить и удалить контейнеры и сеть

down не трогает тома: при следующем up данные базы на месте. Удалить вместе с томами — docker compose down -v. Это стирает данные, пользуйтесь осознанно.

Compose называет всё по имени папки проекта: контейнер my-site-db-1, том my-site_pgdata. Поэтому это новый том, а не pgdata из прошлых уроков — база в нём пустая.

Изменили код — пересоберите

Поменяйте текст в index.html и выполните:

Терминал
docker compose up -d --build

--build пересобирает образы сервисов с build. Compose пересоздаст только контейнер web — база и Adminer продолжат работать.

Пароли — в .env

Файл compose.yaml обычно лежит в репозитории вместе с кодом. Пароль в нём увидит каждый, у кого есть доступ к репозиторию. Пароли выносят в файл .env рядом с compose.yaml:

Текст
POSTGRES_PASSWORD=secret

А в compose.yaml пишут ссылку на переменную:

YAML
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Compose сам прочитает .env и подставит значение. Добавьте .env в .gitignore, а в репозиторий положите пример .env.example с пустыми значениями — так коллеги поймут, какие переменные нужно задать.

Перезапуск после сбоя

Если программа упала или компьютер перезагрузился, контейнер останется остановленным. Чтобы Docker поднимал его сам, добавьте сервису политику перезапуска:

YAML
    restart: unless-stopped

Контейнер будет перезапускаться всегда, кроме случаев, когда вы остановили его сами.

Попробуйте сами. Перенесите пароль базы из compose.yaml в .env, добавьте сервису db политику restart: unless-stopped и поднимите стенд. Создайте в Adminer таблицу, выполните docker compose down, затем снова up -d и убедитесь, что таблица на месте.

Решение

Файл .env:

Текст
POSTGRES_PASSWORD=secret

Сервис db в compose.yaml:

YAML
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

Команды:

Терминал
docker compose up -d
# создали таблицу в Adminer: сервер db, пользователь postgres
docker compose down
docker compose up -d

Таблица на месте: down удалил контейнеры, но не том. Проверить, что Compose подставил пароль, можно командой docker compose config — она покажет итоговый файл со значениями.

Если сначала запустили базу с одним паролем, а потом поменяли его в .env, войти с новым паролем не получится. Пароль задаётся только при первом создании базы и хранится в томе. Для учебного стенда проще удалить том — docker compose down -v — и поднять заново.

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Чем Docker Compose удобнее нескольких команд docker run?

  2. Как сервис adminer найдёт базу из сервиса db в том же compose.yaml?

  3. Что сделает docker compose down -v?

  4. Где хранить пароль базы данных для compose.yaml?