Материал открыт для чтения

Войдите, чтобы сохранять прогресс и результаты.

Войти
Docker Compose: Многосервисные приложения

Compose-файл: services и основные поля

чтение ~7 минУрок 2 из 16

Compose-файл: services и основные поля

module docker compose Image04

В прошлой статье была главная идея: Compose запускает не один контейнер, а стек из нескольких сервисов. Теперь разберём сам YAML-файл.

Compose-файл состоит из секций верхнего уровня. Чаще всего ты увидишь такие:

Секция Что описывает
services какие контейнерные роли есть в приложении: web, app, db, worker
networks какие сети есть у проекта и какие сервисы через них видят друг друга

В этой статье главное — секция services и базовые поля сервиса. Сети затронем на уровне формы записи, а хранение данных через volumes разберём позже на практическом примере с PostgreSQL.

Service — это роль, container — это экземпляр

Сервис в Compose — это описание роли.

Например:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

web — не имя одного конкретного контейнера в вечном смысле. Это роль: “контейнер nginx, который публикует HTTP наружу”.

Когда ты запускаешь:

docker compose up -d

Compose создаёт конкретный контейнер для сервиса web. Если потом сервис пересоздать, контейнер может получить новый ID, но роль web в compose-файле остаётся той же.

Это важно для логов и команд:

docker compose ps
docker compose logs web
docker compose exec web sh

Ты работаешь с сервисом web, а Compose сам находит нужный контейнер проекта.

Минимальный сервис

Минимально сервису нужен источник image:

services:
  web:
    image: nginx:alpine

image означает: “возьми готовый образ из локального Docker cache или registry”.

Если image нужно собрать из Dockerfile, используют build:

services:
  app:
    build: .

Такой вариант означает: “собери image из Dockerfile в текущей директории и запусти сервис из него”.

Более явная форма:

services:
  app:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod

context — директория, которую Docker отправит в сборку. dockerfile — какой Dockerfile использовать внутри этого context.

Когда выбирать:

Ситуация Поле
запускаешь готовый nginx, postgres, redis image
запускаешь своё приложение из исходников build
в CI уже собрали image и положили в registry image: registry.example.com/team/app:1.2.3

Поля сервиса, которые чаще всего нужны

Пример сервиса с типичными полями:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    environment:
      APP_ENV: dev
    depends_on:
      - app
    restart: unless-stopped

Что здесь происходит:

Поле Простое объяснение
image какой image запускать
ports какие порты открыть с хоста внутрь контейнера
environment какие переменные окружения передать процессу внутри контейнера
depends_on какие сервисы стартовать раньше
restart когда Docker должен перезапускать контейнер

Важно: эти поля описывают разные стороны сервиса. ports — сеть снаружи, environment — настройки приложения, depends_on — порядок старта.

Ports: публиковать только входные точки

ports:
  - "8080:80"

Левая часть — порт хоста. Правая часть — порт внутри контейнера.

localhost:8080 на хосте -> порт 80 внутри контейнера web

Если сервис нужен только соседним контейнерам, порт наружу публиковать не нужно. Например, база данных обычно не должна быть доступна с хоста:

services:
  app:
    image: myapp
    environment:
      DATABASE_HOST: db

  db:
    image: postgres:15-alpine

app сможет обратиться к db:5432 внутри сети Compose, даже если у db нет секции ports.

Практическое правило:

  • публикуй наружу frontend, nginx, API gateway или debug UI;
  • не публикуй наружу базы, очереди и внутренние worker-сервисы без причины.

Так меньше конфликтов портов и меньше случайно открытых внутренних сервисов.

Environment: настройки внутри контейнера

environment:
  APP_ENV: dev
  DATABASE_URL: postgres://app:secret@db:5432/app

Эти значения попадают в окружение процесса внутри контейнера. Приложение может прочитать их как обычные environment variables.

Пример: backend берёт DATABASE_URL, чтобы понять, к какой базе подключаться. В Compose для соседнего сервиса используется имя сервиса db, а не localhost.

postgres://app:secret@db:5432/app
                    ^^
                    имя сервиса db

Подробно .env, env_file и подстановку переменных разберём в отдельной статье про конфигурацию. Здесь важно только не смешивать смысл: environment — это то, что попадёт внутрь контейнера.

Networks под сервисом и на верхнем уровне

По умолчанию Compose создаёт одну сеть проекта и подключает к ней все сервисы. Для простых стеков этого достаточно.

Но иногда нужно разделить доступ:

services:
  web:
    image: nginx:alpine
    networks:
      - frontend

  app:
    image: myapp
    networks:
      - frontend
      - backend

  db:
    image: postgres:15-alpine
    networks:
      - backend

networks:
  frontend:
  backend:

Здесь:

  • web видит app, потому что оба в frontend;
  • app видит db, потому что оба в backend;
  • web не видит db, потому что у них нет общей сети.

Это простая изоляция внутри одного Docker host. Она не заменяет полноценную security-модель, но помогает не делать базу видимой всем сервисам проекта.

depends_on — порядок старта, не готовность

services:
  app:
    depends_on:
      - db

Эта запись означает: сначала запусти контейнер db, потом контейнер app.

Но это не означает, что PostgreSQL уже готов принимать подключения. Контейнер базы может быть running, а сама база ещё инициализирует каталог данных.

Поэтому приложение всё равно должно уметь повторять подключение к базе. Позже в курсе будет отдельная статья про healthcheck и depends_on.condition, где разберём готовность сервиса подробнее.

Restart policy

restart: "no"
restart: on-failure
restart: unless-stopped
restart: always

Для лабораторных часто можно оставить default. Для долгоживущих локальных сервисов обычно используют unless-stopped.

Но restart policy не исправляет ошибку приложения. Если приложение падает из-за неправильного пароля к базе, Docker может перезапускать контейнер снова и снова, но причина останется той же.

Проверка:

docker compose ps
docker compose logs app --tail=50

Если видишь постоянные restart'ы, сначала читай logs, а не увеличивай restart policy.

Проверка итоговой конфигурации

Перед запуском полезно посмотреть, что Compose реально понял из YAML:

docker compose config

Эта команда:

  • проверяет синтаксис;
  • подставляет переменные;
  • объединяет override-файлы;
  • показывает итоговую конфигурацию.

Это особенно полезно, когда в файле есть .env, несколько compose-файлов или переменные вроде ${APP_PORT:-8080}.

Как читать compose-файл

Читай файл не сверху вниз как роман, а по вопросам:

  1. Какие сервисы есть в services?
  2. Какой сервис является входной точкой снаружи?
  3. Какие ports опубликованы на хост?
  4. Какие сервисы общаются только внутри сети Compose?
  5. Есть ли данные, которые должны жить дольше контейнера?
  6. Какие переменные окружения переданы приложению?
  7. Есть ли depends_on, и не перепутана ли готовность с порядком старта?

Так compose-файл становится картой системы: видно, где вход, где внутренние зависимости, где данные и какие настройки управляют поведением сервисов.

Материал изучен?

Войдите, чтобы сохранить прогресс и перейти к практической лаборатории.

Войти и продолжить