compose-stack — практический stack на Docker Compose
Полная система из пяти сервисов: FastAPI, PostgreSQL, Redis, background worker и сервис миграций. Пример собирает вместе всё, что разбиралось в разделах 06–09 курса.
Относится к разделу 09. Docker Compose, урок 9.7. Практический stack.
Быстрый старт
make up # создаст .env и пароль, соберёт и запустит
curl -s localhost:8000/readyz | python3 -m json.tool
make logs # логи всех сервисов
make clean # полный сброс, включая данные
Без make:
cp .env.example .env
head -c 24 /dev/urandom | base64 | tr -d '\n' > secrets/db_password.txt
chmod 600 secrets/db_password.txt
docker compose up -d --build
Файла secrets/db_password.txt в репозитории нет и быть не должно — каталог исключён в .gitignore. Он создаётся локально при первом запуске; без него docker compose up завершится ошибкой secret "db_password" not found, и это правильное поведение: Compose не создаёт молча то, чем не владеет.
Состав
| Сервис | Роль | Сети | Публикуется |
|---|---|---|---|
api | FastAPI: HTTP-интерфейс, ставит задачи в очередь | edge, backend | Да (только в dev) |
worker | Разбирает очередь, пишет результаты в базу | backend | Нет |
migrate | Одноразовая задача: схема базы | backend | Нет |
db | PostgreSQL 17 | backend | Нет (в dev — на loopback) |
cache | Redis 8: очередь задач | backend | Нет (в dev — на loopback) |
tools | Отладочный container, профиль tools | backend | Нет |
Один образ compose-stack:local обслуживает три роли — api, worker и migrate. Роль задаётся командой в compose.yaml, поэтому код и зависимости у них гарантированно совпадают.
Файлы конфигурации
| Файл | Когда применяется |
|---|---|
compose.yaml | Всегда. Определения сервисов без настроек окружения |
compose.override.yaml | Автоматически при docker compose up — окружение разработки |
compose.prod.yaml | Только явно: -f compose.yaml -f compose.prod.yaml |
docker compose up -d # разработка
docker compose -f compose.yaml -f compose.prod.yaml up -d # production
docker compose --profile tools up -d # плюс отладочный container
Флаг -f отменяет автоматический compose.override.yaml — поэтому production не подхватит локальные настройки разработчика.
Проектные решения
Миграции — отдельный сервис. api и worker зависят от него через condition: service_completed_successfully. Провал миграции не даёт им запуститься вовсе: приложение не увидит базу со старой схемой.
Две сети, backend — internal. У базы и очереди нет маршрута наружу. api состоит в обеих сетях и служит единственным мостом.
Пароль — Compose secret, не переменная окружения. Он приходит файлом в /run/secrets/db_password и не появляется ни в docker inspect, ни в /proc/<pid>/environ. Приложение читает его по соглашению _FILE, как это делают официальные образы.
Liveness не зависит от базы. HEALTHCHECK обращается к /healthz, который знает только о самом процессе. Проверка через /readyz при недоступности базы пометила бы все реплики unhealthy одновременно и вызвала бы лавину перезапусков.
stop_grace_period подобран по длительности работы. У worker — 45 секунд, потому что задача может идти до 30; у базы — 30 секунд на закрытие файлов. Значение по умолчанию в 10 секунд дало бы SIGKILL и код 137.
Worker использует BLMOVE, а не BLPOP. Задача атомарно перемещается в список tasks:processing и не теряется при падении процесса. Обработчик идемпотентен: повторная обработка не создаёт дубликат.
Виртуальное окружение в /opt/venv. Оно вне /app, поэтому bind mount кода в разработке его не скрывает — классическая причина ModuleNotFoundError.
Проверка
./check.sh
Скрипт проверяет девять групп утверждений: порядок миграций, изоляцию сетей, отсутствие пароля в метаданных, работу очереди от постановки до записи в базу, независимость liveness от базы, graceful shutdown worker'а, отличия production-конфигурации и работу профиля. Возвращает ненулевой код при любом расхождении.
Тесты
make test # прогон в стадии сборки, без кэша
python3 -m pytest tests/ -q # локально
Unit-тесты покрывают разбор конфигурации, формирование DSN и чтение секретов — всё, что проверяемо без поднятых сервисов. Проверка взаимодействия сервисов относится к integration-тестам и выполняется скриптом check.sh.
Что осталось за рамками
- Reverse proxy. В production
apiслушает без публикации портов; предполагается, что трафик пускает внешний прокси. - Управление секретами. Compose secret вне Swarm — это bind mount обычного файла: на диске host пароль лежит открытым текстом. Ротация требует пересоздания container'а.
- Резервное копирование. Volume
db-dataпереживаетdocker compose down, но неdown -v. Схема копирования — в уроке 7.6. - Ограничение числа повторов задачи. Задача, падающая всегда, будет возвращаться в очередь бесконечно. В production нужна очередь «мёртвых» задач.
Требуемые версии
| Компонент | Версия |
|---|---|
| Docker Engine | 28.x или 29.x |
| Docker Compose | v2 (плагин) |
| Образы | python:3.13-slim, postgres:17-alpine, redis:8-alpine |