Docker Compose с нуля до продакшена для DevOps инженера

Курс помогает освоить Docker Compose с нуля: от базовых понятий контейнеризации до проектирования многосервисных стеков и практик эксплуатации. Вы научитесь писать compose-файлы, управлять сетями/томами, настраивать окружения и готовить приложения к деплою и поддержке.

1. Основы контейнеров и установка Docker/Compose

Основы контейнеров и установка Docker/Compose

Зачем DevOps инженеру Docker Compose

Docker Compose — инструмент, который описывает и запускает несколько контейнеров как единое приложение: веб-сервис, базу данных, кеш, очередь, миграции, прокси и т.д. Для DevOps это базовая практика, потому что Compose:

  • стандартизирует локальную разработку и тестирование
  • ускоряет поднятие стендов (dev, stage, demo)
  • помогает воспроизводимо запускать зависимости приложения
  • служит мостом к продакшен-оркестрации (Kubernetes и аналоги) через дисциплину декларативного описания
  • В следующих статьях курса мы будем писать compose.yaml, настраивать сети и тома, healthchecks, профили, секреты и стратегии деплоя. Но сначала нужно разобраться, что такое контейнеры, как работает Docker и как корректно установить Docker/Compose.

    Что такое контейнер

    Контейнер — это изолированный процесс (или набор процессов), который:

  • запускается из образа (image)
  • имеет собственную файловую систему (слой образа + слой контейнера)
  • работает в изоляции по ресурсам и окружению
  • может быть ограничен по CPU/памяти и подключён к сетям
  • Важно: контейнер — это не виртуальная машина. Контейнеры используют ядро хостовой ОС и изолируются механизмами ОС.

    !Сравнение виртуальных машин и контейнеров

    Базовые понятия Docker

    Образ (image)

    Образ — это шаблон файловой системы и метаданных, из которого создаются контейнеры. Образ обычно версионируется тегом, например nginx:1.25.

    Контейнер (container)

    Контейнер — запущенный (или остановленный) экземпляр образа. Он имеет:

  • слой записи (изменения поверх образа)
  • настройки сети
  • переменные окружения
  • подключённые тома
  • Реестр (registry)

    Реестр — хранилище образов. Самый известный — Docker Hub.

    Dockerfile

    Dockerfile описывает шаги сборки образа. В рамках этого урока мы не углубляемся в сборку, но будем использовать готовые образы, чтобы сфокусироваться на Compose.

    Том (volume)

    Том — способ хранить данные вне слоя контейнера, чтобы переживать пересоздание контейнеров (например, данные PostgreSQL).

    Как устроен Docker на практике

    Docker состоит из нескольких частей:

  • Docker Engine (демон dockerd) — управляет образами, контейнерами, сетями и томами
  • Docker CLI (docker) — клиент, который отправляет команды демону
  • Docker Compose (в современных версиях — плагин docker compose) — управляет набором сервисов по файлу compose.yaml
  • !Общая архитектура Docker: CLI, Engine, Registry и основные сущности

    Установка Docker и Docker Compose

    Ниже — рекомендуемый путь установки: официальные пакеты Docker. Это снижает риск проблем с несовместимостью версий.

    Установка на Linux

    #### Рекомендуемый вариант Используйте официальную инструкцию Docker для вашего дистрибутива:

  • Установка Docker Engine на Ubuntu
  • Установка Docker Engine на Debian
  • Установка Docker Engine на CentOS
  • Установка Docker Engine на Fedora
  • В процессе установки обратите внимание на два момента:

  • Ставьте именно Docker Engine из репозитория Docker, а не случайные старые версии из системных репозиториев.
  • Современный Compose поставляется как плагин и вызывается командой docker compose (с пробелом), а не как отдельный бинарник docker-compose.
  • #### Установка Compose plugin Если Compose не установился вместе с Engine, используйте официальную инструкцию:

  • Установка Docker Compose plugin
  • Установка на macOS

    На macOS обычно используют Docker Desktop (внутри запускается Linux VM, потому что Docker контейнеры требуют Linux-ядро):

  • Docker Desktop для Mac
  • Установка на Windows

    На Windows наиболее распространённый и удобный вариант — Docker Desktop с WSL2:

  • Docker Desktop для Windows
  • Проверка установки

    После установки проверьте версии и базовую работоспособность.

    Проверка версий

    Ожидаемо:

  • docker --version показывает версию клиента
  • docker compose version показывает версию Compose V2
  • Пробный запуск контейнера

    Если всё установлено правильно, Docker скачает образ и выведет приветственное сообщение.

    Просмотр состояния Docker Engine

    На Linux (systemd):

    На Docker Desktop (Windows/macOS) проверяйте статус через интерфейс Desktop и повторите команды docker --version и docker run.

    Постустановка и типичные настройки (Linux)

    Запуск Docker без sudo

    По умолчанию сокет Docker принадлежит root. Чтобы запускать Docker без sudo, обычно добавляют пользователя в группу docker.

  • Добавьте пользователя в группу docker:
  • Перезайдите в сессию (logout/login) или выполните:
  • Проверьте:
  • > Важно для DevOps: членство в группе docker фактически даёт привилегии, близкие к root, потому что Docker способен монтировать файловые системы и управлять низкоуровневыми настройками. Для серверов с жёсткими требованиями безопасности изучают rootless-режим.

    Rootless Docker (когда нужно)

    Rootless позволяет запускать Docker без демона от root, но имеет ограничения (сеть, порты, совместимость некоторых сценариев). Начинать обучение Compose проще на стандартной установке.

  • Rootless mode
  • Docker Compose V2: ключевая идея

    Compose описывает приложение как набор сервисов в YAML-файле (чаще всего compose.yaml). Внутри вы задаёте:

  • какие образы использовать
  • какие порты пробросить
  • какие переменные окружения нужны
  • какие тома подключить
  • как сервисы общаются по сети
  • в каком порядке ждать готовность (частично) и как перезапускать
  • В следующих уроках мы последовательно разберём структуру compose.yaml и научимся собирать практичные конфигурации для реальных DevOps-задач.

    Минимальные команды, которые нужно знать уже сейчас

  • docker pull <image> — скачать образ
  • docker images — список образов
  • docker ps и docker ps -a — список контейнеров
  • docker logs <container> — логи контейнера
  • docker exec -it <container> <command> — выполнить команду внутри контейнера
  • docker compose up -d — поднять сервисы из Compose-файла в фоне
  • docker compose down — остановить и удалить созданные контейнеры/сети
  • Частые проблемы и быстрые проверки

    Ошибка доступа к Docker daemon

    Примеры сообщений:

  • permission denied while trying to connect to the Docker daemon socket
  • Cannot connect to the Docker daemon
  • Что проверить:

  • Запущен ли сервис Docker: systemctl status docker
  • Есть ли права на сокет (Linux): добавлены ли вы в группу docker
  • На Windows/macOS: запущен ли Docker Desktop
  • Конфликт старого docker-compose

    Иногда установлен старый отдельный бинарник docker-compose, но нет Compose V2.

    Проверка:

    Для курса ориентируйтесь на docker compose (Compose V2), потому что он является текущим стандартом.

    Что дальше по курсу

    В следующей статье мы начнём писать первый compose.yaml, поднимем приложение из нескольких сервисов и разберёмся:

  • как Compose создаёт сети по умолчанию
  • как работает service discovery по именам сервисов
  • чем отличается публикация портов от внутреннего порта контейнера
  • 2. Синтаксис compose.yaml: сервисы, образы, переменные

    Синтаксис compose.yaml: сервисы, образы, переменные

    Как эта тема связана с предыдущим уроком

    В прошлом уроке вы установили Docker Engine и Docker Compose (V2) и проверили, что команды docker run и docker compose работают. Теперь мы переходим к главному артефакту Compose — файлу compose.yaml, в котором описываются сервисы (контейнеры), какие образы использовать и какие переменные окружения передавать.

    Официальная справка, к которой стоит периодически возвращаться:

  • Docker Compose file reference
  • Compose Specification
  • Где живёт compose.yaml и как его запускают

    Обычно compose.yaml кладут в корень репозитория проекта (рядом с README, src, Dockerfile и т.д.). Запуск выполняется из директории с файлом:

    Остановка и удаление созданных ресурсов:

    > В практике DevOps важно понимать, что Compose создаёт ресурсы с префиксом проекта. По умолчанию имя проекта берётся из имени директории, но его можно переопределить флагом -p.

    Минимальная структура compose.yaml

    Типовая «форма» файла:

    Здесь:

  • services — корневой раздел со списком сервисов
  • app — имя сервиса (логическое имя внутри Compose)
  • image — какой образ запустить
  • ports — публикация порта на хост
  • !Схема: как Compose описывает несколько сервисов и их связь по именам

    YAML-основа: что важно, чтобы Compose правильно прочитал файл

    Compose-файл — это YAML. На практике чаще всего ошибки связаны не с Compose, а с YAML-синтаксисом.

    Ключевые правила:

  • Отступы важны, используйте пробелы (часто 2 пробела), табы нельзя
  • Списки начинаются с -
  • Строки иногда лучше явно брать в кавычки, особенно значения, похожие на числа или логические значения
  • Пример «карта» (map) и «список» (list) в YAML:

    Раздел services: что такое сервис и почему это не то же самое, что контейнер

    Сервис в Compose — это декларация того, как должен запускаться контейнер: образ, переменные, команды, порты, тома, сети, политики рестарта.

    Когда вы делаете docker compose up, Compose создаёт и запускает контейнер(ы) на основе описания сервиса.

    Важно для дальнейших тем курса:

  • Имя сервиса становится DNS-именем в сети Compose по умолчанию
  • Поэтому один контейнер может обращаться к другому по имени сервиса (например, http://api:8080 или postgres://db:5432)
  • Образы и сборка: image и build

    В Compose сервис можно запустить либо из готового образа (image), либо собрать образ из исходников (build).

    image: запуск готового образа

    Типовой пример:

    Практические рекомендации:

  • Явно фиксируйте версию (redis:7.2), не используйте latest в повторяемых средах
  • Для более жёсткой воспроизводимости в критичных средах можно закреплять digest (это отдельная тема, но вы должны знать, что такая возможность существует)
  • build: сборка образа из Dockerfile

    Если ваш сервис — это собственное приложение, чаще всего его нужно собирать:

    Здесь:

  • context — директория, которая отправляется в сборку (обычно корень проекта)
  • dockerfile — путь к Dockerfile относительно context
  • image — имя, под которым собранный образ будет тегироваться локально (удобно для CI)
  • > Если указаны и build, и image, Compose соберёт образ и присвоит ему имя из image. Это удобно, когда вы хотите одинаковое имя образа локально и в CI.

    Таблица: быстрый выбор между image и build

    | Сценарий | Что использовать | Почему | |---|---|---| | Нужен готовый компонент (nginx, postgres, redis) | image | Быстрее, стандартизовано, версионирование тегами | | Ваш сервис из исходников | build (и часто image) | Нужна сборка под ваш код и зависимости |

    Имена сервисов и обнаружение по сети

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

    Пример: web обращается к db по имени db:

    Даже без ports сервисы увидят друг друга внутри сети. Публикация портов нужна в первую очередь для доступа с хоста (например, из браузера или с локального curl).

    Переменные окружения: environment, env_file и .env

    Переменные окружения в Compose легко перепутать, потому что есть три разных механизма.

  • environment — задаёт переменные, которые попадут в контейнер
  • env_file — загружает набор переменных из файла и тоже передаёт в контейнер
  • .env — файл, из которого Compose берёт переменные для подстановки в compose.yaml (и частично для environment, если вы используете {VAR} в файле.
  • Пример compose.yaml:

    Пример .env:

    Если вы выполните:

    Compose выведет «развёрнутую» конфигурацию, где подстановки уже выполнены. Эта команда полезна для диагностики YAML, переменных и итоговой конфигурации.

    Подстановки с дефолтами и обязательными значениями

    Compose поддерживает распространённый синтаксис подстановок:

  • {VAR?message} — если VAR не задана, вывести ошибку с message
  • Пример:

    Приоритеты: откуда в итоге берётся значение

    На практике важны два разных «мира»: подстановка значений в compose.yaml и переменные, которые попадут внутрь контейнера.

    #### Подстановка в compose.yaml ({VAR} вместо значения

    Что проверить:

  • .env лежит в той же директории, откуда вы запускаете docker compose
  • имя переменной написано одинаково (регистр важен)
  • вы не забыли кавычки там, где это важно (например, для строк с двоеточиями)
  • Ошибка: YAML «съел» тип

    Симптомы:

  • приложение получает не то значение (например, on превратился в булево)
  • Что делать:

  • используйте кавычки для значений, которые должны быть строками: "on", "123"
  • Практический пример: web + db с переменными

    Ниже пример, который часто встречается в реальных проектах:

    ``yaml services: web: image: nginx:{WEB_PORT:-8080}:80" environment: NGINX_ENTRYPOINT_QUIET_LOGS: "1"

    db: image: postgres:16 environment: POSTGRES_DB: "{POSTGRES_USER:-app}" POSTGRES_PASSWORD: "{...?...}

    Важное замечание для DevOps: переменные не равны секретам

    Передавать пароли через environment или env_file удобно, но это не лучший путь для продакшена:

  • значения могут утечь в логи, дампы, CI-вывод
  • переменные видны процессам внутри контейнера
  • В следующих уроках курса мы дойдём до более правильных способов управления чувствительными данными (секреты, внешние хранилища, политики доступа). Пока ваша цель — уверенно читать и писать базовый синтаксис Compose.

    Что дальше

    Дальше мы будем расширять compose.yaml практическими возможностями:

  • сети и тома (в том числе персистентность данных)
  • healthchecks и ожидание готовности зависимостей
  • профили, overrides, окружения dev/stage
  • подходы к приближению Compose-стендов к продакшену
  • 3. Сети, тома и хранение данных в Compose

    Сети, тома и хранение данных в Compose

    Связь с предыдущими уроками

    В прошлых статьях вы установили Docker/Compose и научились писать базовый compose.yaml: сервисы, образы, порты и переменные. Теперь переходим к трём фундаментальным темам, без которых невозможно собирать реалистичные стенды:

  • сети: как сервисы находят друг друга и как изолировать компоненты
  • тома: как сделать данные переживающими пересоздание контейнеров
  • bind mounts: как удобно подключать локальный код и конфиги (и почему это обычно не про продакшен)
  • Почему DevOps должен понимать сети и тома

    Compose часто используют для локальных и тестовых окружений, но принципы те же, что и в продакшене:

  • сеть по умолчанию и DNS-имена сервисов дают предсказуемую связность компонентов
  • тома отделяют жизненный цикл данных от жизненного цикла контейнеров
  • изоляция сетей помогает воспроизвести реальные границы доступа (frontend видит backend, но не всегда видит базу)
  • Официальные источники, которые стоит держать под рукой:

  • Compose file reference
  • Networking in Compose
  • Docker storage volumes
  • Сети в Compose

    Сеть по умолчанию и DNS по имени сервиса

    Когда вы запускаете docker compose up, Compose создаёт сеть проекта по умолчанию и подключает к ней все сервисы. Внутри этой сети появляется встроенный DNS: сервисы доступны по имени сервиса.

    Пример: api может подключаться к PostgreSQL по хосту db.

    Даже без ports сервис api увидит db внутри сети Compose.

    !Диаграмма показывает, что внутри сети Compose сервисы общаются по имени сервиса без публикации портов

    ports и внутренний доступ: что важно не перепутать

    ports публикует порт контейнера на хост (чтобы вы могли открыть сервис из браузера, curl с хоста, подключиться IDE и т.д.). Для связи между контейнерами ports обычно не нужен.

    Пример:

    Практический вывод для DevOps:

  • публикуйте наружу только то, что действительно должно быть доступно с хоста
  • если вы публикуете базу данных на хост в dev, делайте это осознанно и не переносите такую привычку в stage/prod
  • Несколько сетей: сегментация и принцип наименьших привилегий

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

    Пример: web видит api, а api видит db. При этом web не подключён к сети базы данных.

    Что это даёт:

  • проще повторять реальные границы доступа
  • меньше риск случайного подключения к приватному сервису не тем компонентом
  • проще писать firewall-логику на уровне сети (в других оркестраторах это часто переходит в network policy)
  • internal сеть: запрет внешнего выхода (полезно для изоляции)

    У сети можно включить флаг internal, чтобы контейнеры в этой сети не имели маршрута наружу через Docker NAT.

    Это не заменяет полноценные политики безопасности, но полезно как дополнительная защита и как способ приблизить стенд к реальным ограничениям.

    Aliases и дополнительные имена в сети

    Иногда нужно дать сервису дополнительное DNS-имя (например, для совместимости со старым конфигом или тестов).

    Внутри сети frontend к api можно обратиться и как api, и как api.local.

    Быстрая диагностика сетей

    Полезные команды:

    Практика:

  • смотрите, к каким сетям подключён контейнер
  • проверяйте, какие DNS-имена и алиасы реально созданы
  • Томa и хранение данных

    Почему данные пропадают без томов

    Файловая система контейнера состоит из слоёв образа и верхнего слоя записи контейнера. Когда контейнер пересоздаётся (а Compose делает это при изменениях), верхний слой исчезает. Поэтому базы данных, очереди, состояние приложений нужно хранить вне контейнера.

    Named volumes: стандартный способ персистентности

    Named volume — управляемое Docker хранилище, которое живёт отдельно от контейнера.

    Пример: PostgreSQL с томом.

    Что важно:

  • db_data создаётся и управляется Docker
  • можно удалять и пересоздавать контейнер db, данные останутся
  • том можно подключать повторно, пока вы его не удалили
  • Команды для контроля:

    Что удаляет docker compose down, а что остаётся

    По умолчанию docker compose down удаляет контейнеры и сети проекта, но не удаляет named volumes.

    Если нужно удалить и тома (например, чтобы начать с «чистой» базой), используйте:

    Практический вывод:

  • в обычной работе down безопасен для данных
  • down -v — сознательная операция, которая часто означает потерю данных
  • Bind mounts: подключение файлов и директорий с хоста

    Bind mount монтирует путь с хостовой файловой системы внутрь контейнера. Это удобно для разработки:

  • подключить исходники
  • подключить конфиги
  • подключить сертификаты или шаблоны
  • Пример: подключаем статический nginx.conf.

    Ключевые особенности:

  • ./nginx/nginx.conf должен существовать на хосте
  • :ro делает монтирование только для чтения (хорошая привычка для конфигов)
  • bind mounts зависят от структуры файлов на хосте и хуже переносятся между машинами
  • !Иллюстрация помогает понять разницу между томом Docker и bind mount

    Временные данные: tmpfs (когда нужно)

    Иногда нужно хранить данные только в памяти контейнера (например, временные файлы), чтобы они:

  • не попадали на диск
  • исчезали при остановке контейнера
  • Пример:

    Используйте tmpfs осознанно: при рестарте контейнера содержимое пропадёт.

    Права доступа и владельцы файлов в томах

    Типичная проблема: сервис в контейнере запускается не от root, а том создаётся с правами, из-за которых процесс не может писать.

    Что делать на практике:

  • смотрите, под каким пользователем работает образ (документация образа)
  • проверяйте логи контейнера и ошибки доступа
  • в dev-сценариях иногда используют временный chown через init-контейнер или команду запуска, но делайте это аккуратно
  • Практический пример: web + api + db с сегментацией сети и персистентной БД

    Ниже конфигурация, которая отражает типовой стенд: веб доступен с хоста, API доступен внутри сети, база доступна только API и хранит данные в named volume.

    На что обратить внимание:

  • web публикует порт и находится только в frontend
  • api связан с web через frontend и с db через backend
  • db изолирован в backend, плюс сеть backend помечена как internal
  • данные PostgreSQL сохраняются в db_data
  • Полезные команды для повседневной работы DevOps

  • docker compose up -d — поднять стенд
  • docker compose logs -f <service> — смотреть логи сервиса
  • docker compose exec <service> sh — зайти внутрь контейнера
  • docker compose down — убрать контейнеры и сети
  • docker compose down -v — убрать ещё и named volumes (осторожно)
  • docker volume ls и docker network ls — аудит ресурсов
  • Частые ошибки

    Ошибка: контейнеры не видят друг друга по имени

    Проверьте:

  • сервисы в одной сети
  • нет ли опечатки в имени сервиса
  • вы обращаетесь по имени сервиса и порту контейнера, а не по опубликованному порту
  • Ошибка: «потерялись данные базы»

    Чаще всего причины такие:

  • том не был подключён к правильному пути данных (для PostgreSQL это обычно /var/lib/postgresql/data)
  • был выполнен docker compose down -v
  • вы переключили имя проекта (флаг -p), и Compose создал новый набор ресурсов
  • Ошибка: bind mount перекрыл файлы образа

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

    Решение:

  • монтируйте точечно файлы, а не целые директории, где это возможно
  • используйте :ro для конфигов
  • Что дальше по курсу

    В следующих материалах мы будем приближать Compose-стенды к реальной эксплуатации:

  • проверки готовности сервисов и корректный старт зависимостей
  • профили и разные конфигурации под dev/stage
  • более безопасная работа с чувствительными данными (секреты и практики)
  • 4. Мультисервисные окружения: профили, зависимости, масштабирование

    Мультисервисные окружения: профили, зависимости, масштабирование

    Связь с предыдущими уроками

    Ранее вы научились:

  • описывать сервисы, образы и переменные в compose.yaml
  • соединять сервисы сетями и сохранять данные в томах
  • Теперь соберём это в управляемое мультисервисное окружение, где:

  • часть сервисов включается только при необходимости (профили)
  • зависимости стартуют в правильном порядке и ожидают готовности (depends_on + healthcheck)
  • отдельные сервисы можно масштабировать в несколько экземпляров (scale)
  • Официальные источники, которые стоит периодически открывать:

  • Compose file reference
  • Profiles in Compose
  • docker compose up (CLI reference)
  • !Диаграмма показывает профили, зависимости и масштабирование в одном окружении

    Профили Compose

    Зачем нужны профили

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

  • dev: приложение + база + hot reload
  • debug: добавить трассировку, UI-админки, профайлеры
  • ci: поднять зависимости и выполнить тесты
  • Идея простая:

  • сервис без профиля запускается всегда
  • сервис с profiles запускается только когда профиль включён
  • Как включать профили

  • Через флаг --profile:
  • Через переменную окружения COMPOSE_PROFILES:
  • Пример: опциональный сервис админки базы

    Практический эффект:

  • docker compose up -d поднимет только db
  • docker compose --profile debug up -d поднимет db и adminer
  • Несколько профилей на одном сервисе

    Иногда один сервис нужен в нескольких режимах:

    Сервис запустится, если включён любой из перечисленных профилей.

    Профили и публикация портов

    Типичный DevOps-паттерн:

  • в базовом режиме не публиковать опасные порты (БД, админки)
  • публиковать их только в debug профиле
  • Это снижает риск привычки переносить лишние ports в stage или прод.

    Зависимости сервисов: порядок старта и готовность

    Что на самом деле делает depends_on

    depends_on управляет порядком запуска сервисов. Но по умолчанию он не гарантирует, что зависимость уже готова принимать подключения.

    Ссылка на раздел спецификации в документации Docker:

  • depends_on
  • Простой пример порядка:

    db запустится раньше api, но PostgreSQL может ещё инициализироваться, а api уже начнёт подключаться и падать.

    Готовность через healthcheck и condition: service_healthy

    Чтобы Compose мог ждать реальную готовность, добавляют healthcheck на зависимость и используют расширенный синтаксис depends_on с condition.

    Ссылки:

  • healthcheck
  • depends_on
  • Пример для PostgreSQL:

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

  • db помечается как healthy, когда pg_isready начинает успешно отвечать
  • api запускается только после этого условия
  • Зависимость на одноразовые задачи: миграции

    Частая практика в DevOps-окружениях: выделить миграции в отдельный сервис, который:

  • стартует один раз
  • применяет миграции
  • завершается
  • После этого основной api должен запускаться только если миграции завершились успешно.

    Пример:

    Смысл service_completed_successfully:

  • api стартует только если migrate завершился с кодом 0
  • Практические советы по healthcheck

  • Проверяйте именно готовность сервиса, а не просто факт запущенного процесса.
  • Делайте интервалы и число попыток реалистичными для холодного старта.
  • Старайтесь использовать встроенные утилиты образа (например, pg_isready для PostgreSQL).
  • Масштабирование сервисов

    Что означает масштабирование в Compose

    Масштабирование в Compose обычно означает запуск нескольких реплик одного сервиса на одной Docker-ноду.

    Команда:

    В результате появятся контейнеры вроде:

  • <project>-api-1
  • <project>-api-2
  • <project>-api-3
  • Проверка:

    Ограничение: нельзя просто так масштабировать сервис с фиксированным ports

    Если у api задано:

    то при --scale api=3 произойдёт конфликт, потому что нельзя привязать один и тот же порт хоста 8080 к трём контейнерам.

    Рабочие варианты:

  • не публиковать api наружу, а принимать трафик через reverse proxy внутри сети
  • публиковать разные порты вручную (обычно неудобно)
  • публиковать случайные порты (подходит для тестов, но не для стабильных точек входа)
  • Типовой паттерн: reverse proxy + несколько реплик приложения

    Идея:

  • api доступен только внутри сети Compose
  • web публикуется на хост и распределяет трафик на api
  • Упрощённая заготовка:

    Где:

  • expose документирует внутренний порт и делает его доступным внутри сетей Compose, но не публикует на хост
  • реальный балансировщик на уровне web вы настраиваете в nginx.conf
  • > Важно: для балансировки по нескольким репликам Nginx должен резолвить адреса динамически. В Compose-сетях DNS-имя сервиса обычно резолвится в несколько IP при наличии нескольких реплик, но поведение и кеширование DNS зависят от клиента. Это нужно проверять в вашем конкретном варианте прокси и конфигурации.

    deploy.replicas и почему это не про локальный docker compose up

    В Compose-файлах можно встретить:

    Но в классическом режиме docker compose up этот механизм не является универсальным способом масштабирования.

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

  • для локального Compose используйте --scale
  • deploy чаще относится к Swarm и к подходам, которые ближе к продакшен-оркестрации
  • Практика управления окружением: команды, которые должен знать DevOps

  • docker compose up -d поднимает окружение
  • docker compose --profile debug up -d поднимает окружение с опциональными сервисами
  • docker compose up -d --scale api=3 масштабирует сервис
  • docker compose logs -f api показывает логи сервиса
  • docker compose exec api sh позволяет зайти в контейнер
  • docker compose down останавливает и удаляет контейнеры и сети проекта
  • Частые ошибки

  • Сервис стартует раньше базы и падает: добавьте healthcheck и condition: service_healthy.
  • Масштабирование не работает из-за портов: уберите фиксированный ports у масштабируемого сервиса и поставьте прокси.
  • Опциональный сервис неожиданно запустился: проверьте, не включён ли COMPOSE_PROFILES в окружении терминала.
  • Что дальше

    Следующий логичный шаг после профилей, зависимостей и масштабирования:

  • приближать окружение к эксплуатации через политики рестарта, лимиты ресурсов, логи
  • аккуратно управлять конфигурацией для dev и stage (варианты запуска, несколько файлов Compose)
  • безопаснее обращаться с чувствительными данными (секреты и практики хранения)
  • 5. DevOps-практики: healthchecks, логи, секреты и CI/CD с Compose

    DevOps-практики: healthchecks, логи, секреты и CI/CD с Compose

    Связь с предыдущими уроками

    В прошлых статьях вы научились собирать мультисервисные окружения на Docker Compose: описывать сервисы, сети и тома, включать опциональные компоненты профилями, управлять порядком старта через depends_on, а также масштабировать сервисы.

    Теперь делаем следующий шаг к эксплуатации: добавим практики, которые уменьшают флейки, упрощают диагностику и делают запуск в CI/CD предсказуемым.

    Мы разберём:

  • healthchecks: как отличать запущено от готово
  • логи: как собирать, ограничивать и удобно читать логи в Compose
  • секреты: чем отличаются от переменных окружения и как их подключать
  • CI/CD с Compose: как использовать Compose для интеграционных тестов и сборок
  • Полезные ссылки, к которым стоит возвращаться:

  • Compose file reference
  • Healthcheck в Compose
  • depends_on в Compose
  • Docker logging drivers
  • Use secrets in Compose
  • !Диаграмма показывает, как healthcheck влияет на запуск зависимого сервиса

    Healthchecks: готовность сервиса как инженерный контракт

    Почему одного depends_on недостаточно

    depends_on управляет порядком запуска контейнеров, но по умолчанию не гарантирует, что зависимость уже готова принимать подключения.

    Типичный симптом без healthcheck:

  • база данных стартовала, но ещё выполняет инициализацию
  • приложение успело попытаться подключиться, получило ошибку и ушло в рестарт
  • Как работает healthcheck

    healthcheck задаёт команду проверки, которую Docker периодически выполняет внутри контейнера.

  • если команда возвращает код 0, контейнер считается healthy
  • если команда возвращает не 0, контейнер считается unhealthy (после заданного числа попыток)
  • Практическая цель DevOps: сделать healthcheck проверкой готовности (readiness), а не просто факта, что процесс существует.

    Пример: PostgreSQL + ожидание готовности

    Что здесь важно:

  • test использует pg_isready, который специально предназначен для проверки готовности PostgreSQL
  • start_period даёт контейнеру время на холодный старт, прежде чем неудачи начнут учитываться как падение здоровья
  • depends_on.condition: service_healthy делает запуск api зависимым от состояния healthcheck
  • Пример: HTTP readiness для веб-сервиса

    Если сервис предоставляет HTTP endpoint здоровья (например, /health), healthcheck можно сделать через curl.

    Практические замечания:

  • проверяйте localhost внутри контейнера (это быстрее и не зависит от сети)
  • не делайте проверки слишком частыми без причины: это дополнительная нагрузка
  • Что делать, если у образа нет нужных утилит

    Иногда внутри образа нет curl, wget или других инструментов.

    Рабочие варианты:

  • использовать встроенные утилиты образа (лучший путь, если есть)
  • добавить проверку на уровне приложения (эндпоинт здоровья)
  • собрать свой образ с минимальным набором утилит для проверки (осознанный компромисс)
  • Рестарты: что должно происходить при сбоях

    Healthcheck сам по себе не перезапускает контейнер. Перезапуски задаются политикой restart.

    Часто используемые варианты:

  • restart: "no" — одноразовые задачи (миграции, инициализация)
  • restart: unless-stopped — типичный выбор для долгоживущих сервисов в dev/stage
  • restart: always — перезапуск всегда, даже после перезагрузки демона
  • Пример для миграций:

    Логи: читаемость, ограничение объёма и стандартные команды

    Базовые команды Compose для логов

  • docker compose logs -f — хвост всех сервисов
  • docker compose logs -f api — хвост конкретного сервиса
  • docker compose logs --since 10m — последние 10 минут
  • Для диагностики проблем старта часто полезно:

    Стандартизируйте формат логов

    Для DevOps важно, чтобы сервисы писали логи в stdout и stderr. Это облегчает:

  • локальную диагностику через docker compose logs
  • доставку в централизованные системы логирования в продакшене
  • Если приложение пишет в файл внутри контейнера, вы почти гарантированно получите:

  • неудобный доступ к логам
  • проблемы с ротацией
  • разницу поведения между dev и продом
  • Ограничение размера логов (ротация)

    По умолчанию на многих системах используется драйвер json-file. У него можно включить ротацию, чтобы логи не росли бесконечно.

    Смысл параметров:

  • max-size ограничивает размер одного файла логов
  • max-file ограничивает количество файлов в ротации
  • Рекомендация: включайте ротацию как минимум в long-running стендах (например, stage), чтобы не заполнять диск.

    Когда нужен другой logging driver

    Выбор драйвера зависит от того, куда вы отправляете логи.

    Частые варианты:

  • json-file — простая локальная диагностика, дефолт на многих установках
  • journald — удобно на системах с systemd и централизованным сбором через journal
  • драйверы для внешних систем (например, syslog) — когда это часть вашей платформы
  • Список и описание драйверов — в документации: Docker logging drivers.

    Секреты: чем они отличаются от переменных окружения

    Почему секреты нельзя хранить как обычные переменные

    В прошлых уроках мы использовали environment, env_file и .env. Это подходит для конфигурации, но плохо подходит для секретов.

    Риски подхода через переменные окружения:

  • секреты могут утечь в вывод CI, диагностические дампы и логи
  • переменные легко прочитать внутри контейнера любым процессом
  • .env часто случайно попадает в репозиторий
  • Compose-секреты дают более безопасный и дисциплинированный способ доставки секретных данных: секрет попадает в контейнер как файл (обычно в /run/secrets/...).

    Документация: Use secrets in Compose.

    Базовый пример secrets в Compose

    Структура проекта:

  • compose.yaml
  • secrets/db_password.txt.gitignore)
  • compose.yaml:

    Что важно:

  • POSTGRES_PASSWORD_FILE — это специальный механизм образа PostgreSQL: он читает пароль из файла
  • секрет монтируется в контейнер как файл /run/secrets/db_password
  • файл с секретом должен храниться вне репозитория или в защищённом хранилище CI
  • Практики хранения секретов для DevOps

    В реальных проектах обычно комбинируют:

  • секреты в менеджере секретов (Vault, облачные Secret Manager) для продакшена
  • секреты в CI как защищённые переменные или секреты окружения для пайплайнов
  • локальные секреты в файлах, которые не коммитятся
  • Compose в этом контуре часто выступает как точка сборки: секрет приходит из внешней системы и подключается к сервисам декларативно.

    CI/CD с Compose: как превратить compose.yaml в инструмент проверки

    Зачем Compose в CI

    Compose удобен как минимальный оркестратор для интеграционных тестов:

  • быстро поднять зависимости (PostgreSQL, Redis, Kafka)
  • дождаться готовности сервисов через healthcheck
  • прогнать тесты в отдельном контейнере
  • гарантированно убрать ресурсы после выполнения
  • Это приближает CI к реальности: вы тестируете систему в окружении, похожем на то, где она будет работать.

    !Схема жизненного цикла Compose-стенда в CI

    Базовый паттерн: поднять стенд, прогнать тесты, убрать стенд

    Ниже универсальная последовательность команд для CI-джобы:

    Практические замечания:

  • docker compose config заранее ловит ошибки YAML, подстановок и итоговой структуры
  • --build гарантирует актуальность локально собранных образов
  • run --rm удобен для одноразовых задач (тесты, миграции, линтер)
  • down -v в CI обычно желателен, чтобы не оставлять мусор и всегда стартовать с чистого состояния
  • Добавляем сервис test

    Пример: тесты запускаются в контейнере, который имеет доступ к сети Compose и может обращаться к api и db по DNS-именам.

    Комментарии:

  • тесты часто зависят от того, что api хотя бы стартовал (service_started), но лучше иметь у api свой healthcheck и ждать service_healthy
  • test не нужно поднимать через up; его удобно запускать через docker compose run --rm test
  • Типовая ошибка в CI: гонки готовности

    Если тесты флейкают, чаще всего причина одна из этих:

  • нет healthcheck у базы или API
  • healthcheck проверяет не готовность, а факт процесса
  • тесты стартуют через run без ожидания готовности зависимостей
  • Решение почти всегда одно: добавьте корректные healthchecks и зависимость через condition: service_healthy.

    Сборка и публикация образов (в контексте Compose)

    Compose может собирать образы через build. Для публикации в registry в CI обычно используют:

  • docker buildx build для сборки и пуша
  • тегирование по commit SHA или версии релиза
  • Официальный стартовый ориентир для GitHub Actions: Build and push Docker images.

    Практика для DevOps:

  • Compose-файлы используйте как декларацию окружения и зависимостей
  • сборку и пуш образов выстраивайте как отдельный этап пайплайна (чёткая ответственность)
  • Мини-чеклист DevOps для Compose-стендов, приближенных к эксплуатации

  • У ключевых зависимостей есть healthcheck, который проверяет именно готовность.
  • Зависимые сервисы используют depends_on с condition: service_healthy, где это критично.
  • Для долгоживущих стендов настроена ротация логов (logging.options).
  • Секреты не передаются через .env и environment, если есть возможность использовать secrets.
  • CI-джоба делает docker compose config, поднимает стенд, запускает тесты и гарантированно делает down.
  • Что дальше по курсу

    Следующие логичные темы после healthchecks, логов, секретов и CI/CD:

  • несколько Compose-файлов и overrides под dev/stage
  • политики ресурсов и приближение к продакшен-ограничениям
  • паттерны деплоя: как переносить дисциплину Compose в Kubernetes и другие оркестраторы