Compose: вся система одной командой
compose.yml, сети и volumes, .env, healthcheck и depends_on, dev/staging, когда нужен Kubernetes
Compose: вся система одной командой
Критерий из DoD лабы 3 звучит буквально так: docker compose up
поднимает всю систему одной командой. Не «бэкенд, а базу поставьте
локально», не «сначала запустите три терминала» — одна команда и рабочий
контур.
Скелет compose.yml
services:
api:
build: ./backend
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/app
LLM_API_KEY: ${LLM_API_KEY}
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 20s
web:
build: ./frontend
ports:
- "3000:3000" # единственный порт наружу
depends_on:
api:
condition: service_healthy
db:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 10
volumes:
pgdata:Ключ version: больше не пишут — он устарел вместе со схемой Compose v1.
Файл называется compose.yml — так его и называем в курсе (Compose
понимает и compose.yaml, и старое docker-compose.yml).
Что здесь важно понимать
Сеть. Compose создаёт общую сеть, и сервисы видят друг друга по имени:
db:5432, api:8000. Никаких localhost между контейнерами — внутри
контейнера localhost это он сам.
Порты — только там, где реально нужен вход снаружи. Публиковать порт
базы «чтобы посмотреть через DBeaver» — привычка, которая однажды окажется
на публичном IP. Нужен доступ к внутреннему сервису — открой его
временно или сходи через docker compose exec.
Volumes — граница между «данные» и «мусор». Именованный том переживает
docker compose down и пересоздание контейнера; данные, лежащие в слое
контейнера, — нет. Всё, что нельзя потерять (БД, загруженные файлы), живёт
в томе. down -v удаляет тома — это единственная команда здесь, которая
стирает данные.
Переменные окружения. .env подхватывается автоматически и лежит в
.gitignore; в репозитории — .env.example со всеми ключами и
безопасными заглушками. Правило простое: переменная появилась в
compose.yml — в ту же минуту появилась в .env.example.
Healthcheck: «запущен» ≠ «готов»
Без healthcheck depends_on ждёт только запуска процесса. Постгрес при
этом ещё инициализирует кластер, и API падает на первой же миграции —
классическое «у меня после up всё сломалось, а после второго up
заработало».
Связка из двух частей: у зависимости описан healthcheck, у зависящего —
condition: service_healthy. Тогда Compose держит старт, пока проверка не
пройдёт. Полезная деталь — start_period: время на прогрев, в течение
которого провалы не считаются падениями.
Тот же healthcheck потом читает прод: перезапуск нездорового контейнера, проверка после деплоя, метрика доступности.
Dev и staging из одних файлов
Базовый compose.yml описывает систему, а различия окружений живут в
overrides — так staging в лабе 4 собирается из тех же файлов, что и dev:
docker compose up # dev (compose.override.yml подхватится сам)
docker compose -f compose.yml -f compose.staging.yml up -d # stagingДля разработки удобен docker compose watch: он пересобирает или
синхронизирует сервис при изменении файлов — правишь код, контейнер
подхватывает, без ручного up --build по кругу.
Рабочие команды на каждый день:
docker compose up -d --build # поднять, пересобрав изменившееся
docker compose logs -f api # смотреть логи одного сервиса
docker compose exec api bash # зайти внутрь
docker compose ps # статус и healthy/unhealthy
docker compose down # остановить (данные в томах целы)Грабли, на которые наступают все
- Правка
.envбез пересоздания.restartне перечитает окружение — нуженup -d(Compose пересоздаст контейнер). portsвместоexpose. Между сервисами порт публиковать не нужно: внутри сети доступны все порты.- Bind-mount поверх зависимостей. Монтируешь
.:/app— и затираешь установленные в образеnode_modules/.venv. Либо ставь их вне рабочей папки, либо исключай монтированием пустого тома. - Разные версии образов у команды. Пин конкретных тегов (
postgres:17-alpine), а неpostgres:latest.
А Kubernetes?
Не нужен. Проект курса — это один сервер и несколько контейнеров: Compose здесь не «упрощённый вариант», а адекватный инструмент. Kubernetes решает задачи, которых у вас нет: десятки сервисов, автоскейлинг под переменной нагрузкой, катящиеся обновления без даунтайма, несколько нод. Его цена — оператор, который это обслуживает.
Переезжать имеет смысл, когда одновременно совпало: нагрузка не помещается в один сервер, downtime при выкате стал стоить денег, и есть кому держать кластер. До этого момента Kubernetes в студенческом проекте — это «цена решения» без стороны выгоды. Хорошая новость: правильно описанный Compose переносится в манифесты почти механически — в том числе генерацией из архитектурной модели.