Реальные веб-приложения практически никогда не состоят из единственного изолированного процесса. Типовой стек требует совместной работы нескольких независимых сервисов: веб-сервера Node.js / Next.js, реляционной базы данных PostgreSQL, кэширующего слоя Redis и объектного хранилища.
Запуск подобной архитектуры через базовый CLI Docker требует ручного выполнения десятков громоздких команд: создание пользовательской сети (docker network create), создание дисковых томов (docker volume create), запуск каждого контейнера с десятками параметров окружения, паролей и портов в строгой последовательности. Любая опечатка приводит к рассинхронизации стека.
Для решения этой задачи применяется инструмент декларативной оркестрации — Docker Compose. Согласно современной спецификации Compose Specification, весь мультиконтейнерный стек описывается в едином файле конфигурации compose.yaml (или docker-compose.yml). Одной декларативной командой docker compose up -d разработчик поднимает полную изолированную инфраструктуру со встроенным DNS-резолвингом, автоматической сетью, проверками готовности сервисов (Healthchecks) и сохранением данных (Volumes).
Взглянем на внутреннюю архитектуру сети и взаимодействия сервисов внутри Docker Compose:
+-------------------------------------------------------------------------+
| АРХИТЕКТУРА СТЕКА DOCKER COMPOSE |
+-------------------------------------------------------------------------+
| ХОСТ (Компьютер разработчика / Сервер): |
| Браузер / Curl обращается к порту хоста: http://localhost:3000 |
| │ |
| ──┼───────────────────────────┼──────────────────────────────────────── |
| │ ▼ (-p 3000:3000) |
| │ ИЗОЛИРОВАННАЯ СЕТЬ DOCKER BRIDGE (app-network): |
| │ |
| │ ┌─────────────────────────┐ ┌───────────────────────────┐ |
| │ │ СЕРВИС 1: app (Node.js) │ │ СЕРВИС 2: db (PostgreSQL) │ |
| │ │ Контейнер: app-1 │ │ Контейнер: db-1 │ |
| │ │ IP: 172.20.0.3 │ │ IP: 172.20.0.2 │ |
| │ │ │ │ │ |
| │ │ Подключение к БД: │ DNS │ Порт 5432 открыт │ |
| │ │ host: "db" ───────────┼────────►│ ТОЛЬКО ВНУТРИ СЕТИ │ |
| │ │ (НЕ localhost!) │ │ (Не виден снаружи!) │ |
| │ └─────────────────────────┘ └─────────────┬─────────────┘ |
| │ │ |
| ──┼────────────────────────────────────────────────────┼─────────────── |
| │ ХОСТОВОЕ ХРАНИЛИЩЕ ДАННЫХ (Docker Volume): ▼ |
| │ postgres_data ◄──────────────────────────────────┘ |
| │ (/var/lib/docker/volumes/my_stack_postgres_data/_data) |
| │ (Данные переживают перезапуск и пересоздание контейнера!) |
+-------------------------------------------------------------------------+Два ключевых правила ментальной модели Docker Compose:
127.0.0.11). Имя сервиса в файле compose.yaml (например, db) автоматически становится доменным именем сетевого хоста. Приложение Node.js подключается к базе данных по адресу db:5432, а не localhost:5432. Попытка подключиться к localhost изнутри контейнера app означает обращение к самому контейнеру приложения, где PostgreSQL отсутствует./var/lib/postgresql/data, гарантируя сохранность записей БД.Docker Compose валидирует YAML-манифест, подставляет значения переменных окружения из локального файла .env и строит граф зависимостей.
Движок создает выделенную виртуальную сеть (например, project_default), изолирующую стек от других проектов на этом же сервере.
Docker проверяет наличие тома postgres_data. Если он отсутствует, создается новая защищенная область хранения на диске хоста.
Запускается контейнер db. Утилита pg_isready регулярно опрашивает сокет PostgreSQL до получения статуса healthy.
Благодаря условию condition: service_healthy, сервис app стартует только после того, как база данных полностью готова к приему TCP-сессий.
Приложение Node.js упаковано в контейнер и запускается через Docker Compose вместе с PostgreSQL (сервис назван 'db'). Какую строку подключения должен использовать ORM (Prisma / TypeORM) внутри Node.js?
docker compose up -d, получая полностью рабочее окружение с базой данных, миграциями и кэшем без необходимости вручную устанавливать PostgreSQL на свой ноутбук.docker compose down -v.-p branch-name).5432 на внешний сетевой интерфейс хоста, оставаясь доступной исключительно веб-приложению через внутреннюю защищенную сеть Docker.Современный стандарт Compose Specification не требует указания устаревшей директивы version: '3.8':
services:
# 1. Сервис реляционной базы данных
db:
image: postgres:17-alpine
container_name: local-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-wpskills_db}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_dev_pass}
volumes:
# Постоянный том для сохранения данных БД
- postgres_data:/var/lib/postgresql/data
networks:
- internal-net
# Проверка готовности СУБД к приему клиентских сессий
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-wpskills_db}"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
# 2. Сервис веб-приложения Node.js
app:
build:
context: .
dockerfile: Dockerfile
container_name: local-app
restart: unless-stopped
ports:
# Проброс порта наружу: порт_хоста:порт_контейнера
- "3000:3000"
environment:
NODE_ENV: development
PORT: 3000
# Обратите внимание: имя хоста в URL — это имя сервиса 'db'
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres_dev_pass}@db:5432/${POSTGRES_DB:-wpskills_db}?schema=public
depends_on:
db:
# Приложение ждет успешного прохождения healthcheck базы данных!
condition: service_healthy
networks:
- internal-net
# Декларация именованных томов
volumes:
postgres_data:
name: fullstack_postgres_volume
# Декларация изолированной пользовательской сети
networks:
internal-net:
driver: bridgeimport { Pool } from 'pg';
// Переменная DATABASE_URL передается через секцию environment в compose.yaml
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
throw new Error('DATABASE_URL не определена в переменных окружения');
}
export const dbPool = new Pool({
connectionString,
max: 10, // Максимальный размер пула соединений
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
export async function checkDatabaseHealth(): Promise<boolean> {
try {
const client = await dbPool.connect();
const result = await client.query('SELECT NOW() as current_time;');
client.release();
console.log('[Database] Успешное подключение к PostgreSQL:', result.rows[0].current_time);
return true;
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
console.error('[Database Error] Сбой подключения к БД:', msg);
return false;
}
}# 1. Запуск всех сервисов в фоновом режиме со сборкой образов при необходимости
docker compose up -d
# 2. Просмотр статуса сервисов и результатов проверок (State / Health)
docker compose ps
# 3. Чтение потоковых логов конкретного сервиса с отслеживанием (follow)
docker compose logs -f app
# 4. Выполнение интерактивной команды внутри работающего контейнера
# Вход в терминальный клиент psql внутри сервиса базы данных:
docker compose exec db psql -U postgres -d wpskills_db
# 5. Перезапуск отдельного сервиса (например, после изменения конфигурации)
docker compose restart app
# 6. Остановка и удаление контейнеров и сетей с сохранением данных в томах
docker compose down
# 7. ПОЛНАЯ очистка со сбросом баз данных (ВНИМАНИЕ: удаляет volumes!)
docker compose down -vCompose регистрирует виртуальный мост bridge и запускает локальный DNS-резолвер для стека.
Изоляция исключает конфликты имен сервисов между разными проектами на одном сервере.
Движок связывает каталог хоста /var/lib/docker/volumes/ с путем /var/lib/postgresql/data в контейнере СУБД.
Если директория уже содержит данные от предыдущего запуска, инициализация базы пропускается.
Контейнер db переходит в статус starting. Каждые 5 секунд утилита pg_isready проверяет открытие локального сокета.
При получении кода возврата 0 ядро Compose переводит сервис в статус healthy.
Снимается блокировка condition: service_healthy. Сервер Node.js безопасно накатывает миграции и открывает порт 3000.
Полностью исключены аварийные падения бэкенда при медленном старте дисковой подсистемы базы.
Разработчик создал таблицы в базе данных PostgreSQL и затем выполнил команду `docker compose down` (без флагов). Что произойдет с данными при следующем вызове `docker compose up -d`?
Классическая ошибка:
// ОШИБКА внутри контейнера Node.js:
const dbHost = 'localhost'; // Попытка подключиться к самому себе!Внутри сетевого пространства имен контейнера адрес localhost указывает на петлевой интерфейс 127.0.0.1 самого приложения, где база данных не запущена.
Решение: Всегда указывайте имя сервиса из compose.yaml: const dbHost = 'db';.
Конфигурация:
# АНТИПАТТЕРН:
depends_on:
- dbДиректива depends_on в базовом виде ждет только факта запуска процесса контейнера базы данных. Однако PostgreSQL после старта выполняет инициализацию файлов и восстановление WAL, во время которых отвергает сетевые подключения. Node.js стартует на 2 секунды раньше готовности БД и падает с ошибкой ECONNREFUSED.
Решение: Всегда настраивайте healthcheck и используйте условие condition: service_healthy.
Флаг -v (--volumes) инструктирует Docker удалить все именованные тома, описанные в файле. Выполнение этой команды на боевом сервере приводит к мгновенному безвозвратному удалению всех файлов базы данных и медиа-вложений пользователей.
Решение: В скриптах деплоя и production-инструкциях флаг -v должен быть категорически запрещен.
Жесткое прописывание паролей POSTGRES_PASSWORD: mySuperSecretPassword в тексте YAML-файла приводит к утечке секретов при публикации кода в GitHub или GitLab.
Решение: Переносите все чувствительные параметры в отдельный файл .env, который обязательно добавляется в .gitignore, а в репозитории оставляйте безопасный шаблон .env.example.