Docker Compose: как запускать многоконтейнерные приложения
Практика3 минРедакция Innovialabs
Коротко
Docker Compose описывает все контейнеры проекта в одном YAML-файле и поднимает их одной командой docker compose up. Сервисы видят друг друга по имени, данные хранятся в volumes, настройки — в .env.
Что такое Docker Compose
Docker Compose — инструмент, который описывает несколько контейнеров в одном файле compose.yaml и управляет ими как единым приложением. Вместо трёх длинных команд docker run с флагами вы пишете конфигурацию один раз и запускаете всё через docker compose up.
Типичный набор для веб-проекта: приложение, база данных (PostgreSQL) и кэш (Redis). Соберём его по шагам.
Шаг 1: сервис приложения
services:
app:
build: .
ports:
- "3000:3000"
env_file: .env
build: .— собрать образ из Dockerfile в текущей папке.ports— пробросить порт контейнера на хост:хост:контейнер.env_file— загрузить переменные окружения из файла.
Шаг 2: база данных и volume
db:
image: postgres:16
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 5s
retries: 5
volumes:
db-data:
image— готовый официальный образ, собирать ничего не нужно.${...}— значения подставляются из файла.envрядом с compose-файлом.- Именованный volume
db-dataхранит данные вне контейнера: пересоздание контейнера их не удалит. healthcheckпроверяет, что база действительно готова принимать подключения.$$экранирует$, чтобы переменную раскрыл shell внутри контейнера.
Шаг 3: кэш
cache:
image: redis:7
Порт наружу не пробрасываем: Redis нужен только приложению.
Шаг 4: depends_on и порядок запуска
Добавьте в сервис app:
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
Обычный depends_on гарантирует только порядок старта контейнеров, а не готовность сервиса. Условие service_healthy ждёт, пока healthcheck базы пройдёт успешно. Приложению всё равно полезно уметь переподключаться к базе — это надёжнее.
Сети: как сервисы находят друг друга
Compose автоматически создаёт для проекта общую сеть. Внутри неё каждый сервис доступен по имени. Поэтому в .env приложения пишем хост db, а не localhost:
POSTGRES_USER=app
POSTGRES_PASSWORD=change-me
POSTGRES_DB=app
DATABASE_URL=postgres://app:change-me@db:5432/app
REDIS_URL=redis://cache:6379
localhost внутри контейнера указывает на сам контейнер — частая причина ошибки «connection refused». Файл .env добавьте в .gitignore, а в репозитории держите .env.example.
Отдельные сети через ключ networks нужны, когда сервисы надо изолировать друг от друга, например фронтенд от базы.
Команды на каждый день
| Команда | Что делает |
|---|---|
docker compose up -d | Запустить всё в фоне |
docker compose up -d --build | Пересобрать образы и запустить |
docker compose ps | Список сервисов и их статус |
docker compose logs -f app | Логи сервиса в реальном времени |
docker compose exec db psql -U app | Выполнить команду в работающем контейнере |
docker compose down | Остановить и удалить контейнеры и сеть |
docker compose down -v | То же, плюс удалить volumes — данные базы пропадут |
Частые ошибки
localhostвместо имени сервиса в строке подключения.- Пароли прямо в
compose.yaml, закоммиченные в Git. down -v«на всякий случай» — и потеря локальной базы.- Проброс порта базы наружу на сервере без необходимости.
- Ожидание, что
depends_onбез healthcheck дождётся готовности базы.