Docker Compose — установка, команды и настройка контейнеров

Docker Compose -Практический пример стека Nginx + Node.js + PostgreSQL с разбором частых ошибок

Docker Compose — инструмент для запуска многоконтейнерных приложений через один файл конфигурации. Вместо того чтобы поднимать каждый контейнер руками через docker run, вы описываете весь стек в docker compose файл формата YAML — и запускаете одной командой. Docker compose установка занимает пять минут, а экономит часы работы.

В этой статье — всё что нужно: установка, структура файла, основные команды, сети, volumes и разбор реального примера со стеком Nginx + Node.js + PostgreSQL.


Что такое Docker Compose

Обычный Docker запускает один контейнер одной командой. Реальные приложения состоят из нескольких частей: веб-сервер, бэкенд, база данных, кэш. Запускать их по одному, следить за зависимостями, настраивать сети вручную — боль.

Docker Compose решает это через концепцию services. Каждый сервис — это контейнер (или группа контейнеров). Все сервисы описываются в одном docker-compose.yml файле и управляются как единое целое.

Docker vs Docker Compose — в чём разница

Docker Docker Compose
Один контейнер за раз Несколько контейнеров одновременно
Команды в терминале Конфигурация в YAML-файле
Сети и volumes — вручную Автоматически создаёт сети и volumes
Нет понятия зависимостей depends_on — порядок запуска
Для одиночных задач Для dev-окружения и простых прод-стеков

Когда использовать Compose: локальная разработка, тестовые окружения, небольшие продакшен-стеки на одном сервере.

Когда переходить на Kubernetes: нужна высокая доступность, масштабирование на несколько серверов, сложный оркестратор.


Установка Docker Compose

Есть два варианта Docker Compose — важно не путать:

  • docker-compose (со знаком дефис) — старая standalone-утилита, написана на Python, устарела
  • docker compose (без дефиса) — современный плагин для Docker CLI, написан на Go, рекомендуется

Используйте плагин. Если где-то встречаете docker-compose в инструкциях — это значит статья старая.

Docker Compose установка на Ubuntu

Способ 1 — через официальный репозиторий Docker (рекомендуется)

Устанавливаем Docker Engine вместе с плагином Compose:

# Удалить старые версии
sudo apt remove docker docker-engine docker.io containerd runc

# Установить зависимости
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release

# Добавить GPG-ключ Docker
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Добавить репозиторий
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Установить <a title="Установка и настройка SOCKS5 proxy Dante в Docker Compose" href="https://it-apteka.com/ustanovka-i-nastrojka-socks5-proxy-dante-v-docker-compose/" target="_blank" rel="noopener" data-wpil-monitor-id="521">Docker + плагин Compose</a>
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Способ 2 — через apt (быстро, но не всегда последняя версия)

sudo apt update
sudo apt install -y docker.io docker-compose-plugin

Способ 3 — бинарник (legacy, для старых систем)

# Скачать последний релиз
sudo curl -L \
  "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \
  -o /usr/local/bin/docker-compose

# Дать права на выполнение
sudo chmod +x /usr/local/bin/docker-compose

⚠️ Этот способ даёт старый docker-compose (с дефисом). Используйте только если нет другого выхода.

Проверка установки:

# Версия плагина (современный способ)
docker compose version

# Версия standalone (если стоит старый)
docker-compose --version

Вывод должен быть примерно таким:

Docker Compose version v2.24.0

Если видите v2.x — всё хорошо. Если v1.x — пора обновляться.


Docker Compose file — структура и синтаксис

Весь стек описывается в файле docker-compose.yml (или docker-compose.yaml — оба варианта работают). Это и есть docker compose yaml конфигурация.

Основные секции файла

services:      # Контейнеры приложения
volumes:       # Постоянное хранилище данных
networks:      # Сетевая изоляция

Директива version в современных версиях Compose не нужна — она устарела начиная с Compose v2. Если встречаете version: "3.8" в старых инструкциях — это нормально, просто наследие.

Полный пример docker-compose.yml

Разворачиваем стек: Nginx → Node.js backend → PostgreSQL:

services:

  nginx:
    image: nginx:alpine
    container_name: nginx_proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - static_files:/var/www/static
    depends_on:
      - backend
    networks:
      - frontend
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: node_backend
    environment:
      - NODE_ENV=production
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_NAME=myapp
      - DB_USER=appuser
      - DB_PASS=secretpassword
    volumes:
      - ./backend:/app
      - /app/node_modules
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    container_name: postgres_db
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secretpassword
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:
  static_files:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

Разбор секций

services — основная секция. Каждый ключ внутри — отдельный сервис (контейнер). Имя сервиса становится именем хоста внутри сети Compose.

image / build — либо готовый образ из Docker Hub, либо сборка из локального Dockerfile.

ports — проброс портов: "хост:контейнер". Без этого сервис доступен только внутри сети Compose.

environment — переменные окружения. Лучше выносить секреты в .env файл и использовать ${VARIABLE}.

depends_on — порядок запуска. С condition: service_healthy ждёт пока healthcheck не пройдёт.

restart — политика перезапуска: no, always, unless-stopped, on-failure.

volumes — монтирование данных (подробнее ниже).

networks — к каким сетям подключён сервис.


Основные команды Docker Compose

Все docker compose command выполняются из папки, где лежит docker-compose.yml.

docker compose up

Запустить весь стек:

# Запустить в foreground (с выводом логов)
docker compose up

# Запустить в фоне (detached mode)
docker compose up -d

# Пересобрать образы и запустить
docker compose up -d --build

# Запустить только один сервис
docker compose up -d postgres

docker compose down

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

# Остановить контейнеры
docker compose down

# Остановить и удалить volumes (осторожно — данные удалятся!)
docker compose down -v

# Остановить и удалить образы
docker compose down --rmi all

docker compose ps

Статус контейнеров в стеке:

docker compose ps

# Расширенный вывод
docker compose ps -a

docker compose logs

# Логи всех сервисов
docker compose logs

# Логи конкретного сервиса
docker compose logs backend

# Следить за логами в реальном времени
docker compose logs -f

# Последние 100 строк
docker compose logs --tail=100 postgres

docker compose build

# Собрать все образы
docker compose build

# Собрать без кэша
docker compose build --no-cache

# Собрать конкретный сервис
docker compose build backend

Остальные полезные команды

# Перезапустить сервис
docker compose restart backend

# Выполнить команду внутри контейнера
docker compose exec backend sh
docker compose exec postgres psql -U appuser -d myapp

# Запустить одноразовую команду в новом контейнере
docker compose run --rm backend npm run migrate

# Остановить без удаления
docker compose stop

# Посмотреть конфигурацию после подстановки переменных
docker compose config

# Версия
docker compose version

Docker Compose контейнеры и сервисы

Каждый docker compose service — это шаблон для создания контейнера. По умолчанию из одного сервиса создаётся один docker compose container.

Имя контейнера формируется автоматически: имя_проекта-имя_сервиса-номер. Например: myapp-backend-1. Проект — это имя папки, где лежит docker-compose.yml. Можно задать явно:

docker compose -p myproject up -d

Масштабирование сервисов

# Запустить 3 экземпляра backend
docker compose up -d --scale backend=3

Через файл (современный способ):

services:
  backend:
    image: myapp/backend
    deploy:
      replicas: 3

⚠️ При масштабировании нельзя использовать фиксированный container_name и проброс конкретного порта хоста — будет конфликт.


Docker Compose network

По умолчанию Compose создаёт одну сеть для всего стека — все сервисы в ней видят друг друга по имени сервиса. Это и есть DNS-resolution внутри Compose.

# Из контейнера backend обращаемся к postgres вот так:
# host: postgres (имя сервиса, не IP)
# port: 5432 (внутренний порт, не пробрасываемый)

Кастомные сети

Разделение на сети — best practice для изоляции. В нашем примере выше:

  • frontend — nginx и backend: фронт видит бэк
  • backend — backend и postgres: бэк видит базу
  • nginx не видит postgres напрямую — изоляция
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true   # Нет доступа в интернет из этой сети

Подключение к внешней сети

networks:
  shared_network:
    external: true   # Сеть создана вне Compose, подключаемся к ней
# Создать сеть заранее
docker network create shared_network

Docker Compose volumes

Без volumes данные живут только пока живёт контейнер. Удалили контейнер — данные исчезли. Docker compose volumes решают эту проблему.

Типы монтирования

Named volume — для данных БД и постоянного хранилища:

services:
  postgres:
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:   # Объявляем именованный volume

Данные хранятся в /var/lib/docker/volumes/имя_проекта_postgres_data/. Docker управляет этим сам.

Bind mount — для разработки, когда нужно монтировать локальную папку:

services:
  backend:
    volumes:
      - ./backend:/app        # Локальная папка → внутрь контейнера
      - /app/node_modules     # Анонимный volume для node_modules

Bind mount позволяет видеть изменения кода в реальном времени без пересборки образа.

Read-only mount — для конфигов:

volumes:
  - ./nginx.conf:/etc/nginx/nginx.conf:ro

Как не потерять данные БД

Три правила:

  • Всегда используйте named volume для данных PostgreSQL, MySQL, MongoDB
  • Никогда не делайте docker compose down -v в продакшене без бэкапа
  • Регулярно бэкапьте volume отдельно от бэкапа контейнера
# Бэкап PostgreSQL из Compose
docker compose exec postgres pg_dump -U appuser myapp &gt; <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/" target="_blank"  rel="noopener" title="Резервное копирование" data-wpil-keyword-link="linked"  data-wpil-monitor-id="534">backup</a>.sql

# Восстановление
docker compose exec -T postgres psql -U appuser myapp &lt; backup.sql

Практический пример — разворачиваем стек Nginx + Node.js + PostgreSQL

Поднимаем продакшен-стек с нуля. Структура проекта:

myapp/
├── docker-compose.yml
├── docker-compose.dev.yml
├── .env
├── nginx/
│   └── nginx.conf
└── backend/
    └── Dockerfile

Шаг 1 — Создаём .env файл с секретами:

# .env
POSTGRES_DB=myapp
POSTGRES_USER=appuser
POSTGRES_PASSWORD=supersecret
NODE_ENV=production

Шаг 2 — Production docker-compose.yml:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - backend
    networks:
      - frontend
    restart: unless-stopped

  backend:
    build: ./backend
    environment:
      - NODE_ENV=${NODE_ENV}
      - DB_HOST=postgres
      - DB_NAME=${POSTGRES_DB}
      - DB_USER=${POSTGRES_USER}
      - DB_PASS=${POSTGRES_PASSWORD}
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:

networks:
  frontend:
  backend:

Шаг 3 — Dev-конфигурация docker-compose.dev.yml (override):

services:
  backend:
    build:
      context: ./backend
      target: development
    environment:
      - NODE_ENV=development
    volumes:
      - ./backend:/app
      - /app/node_modules
    command: npm run dev

  postgres:
    ports:
      - "5432:5432"   # В dev пробрасываем порт наружу для подключения из IDE

Шаг 4 — Запуск:

# Продакшен
docker compose up -d

# Разработка (объединяет оба файла)
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d

Шаг 5 — Проверка:

# Статус контейнеров
docker compose ps

# Логи
docker compose logs -f

# Проверить что база поднялась
docker compose exec postgres pg_isready -U appuser

Шаг 6 — Перезапуск после обновления кода:

# Пересобрать и перезапустить только backend
docker compose up -d --build backend

Шаг 7 — Полная остановка:

# Остановить без потери данных
docker compose down

# Остановить и удалить всё включая volumes (осторожно!)
docker compose down -v

Частые ошибки Docker Compose

Порт уже занят

Error: Bind for 0.0.0.0:80 failed: port is already allocated
# Найти что занимает порт
sudo ss -tlnp | grep :80
# Или
sudo lsof -i :80

# Остановить процесс или изменить порт в docker-compose.yml

network not found

Error: network myapp_frontend declared as external, but could not be found
# Создать сеть вручную
docker network create myapp_frontend

# Или убрать external: true из конфига

Неправильный YAML — самая частая причина «ничего не работает»

# Проверить синтаксис файла
docker compose config

# Типичные ошибки:
# - Табы вместо пробелов
# - Неправильные отступы
# - Кавычки не закрыты

permission denied при bind mount

# Проблема: контейнер запускается под другим пользователем
# Решение 1: задать uid/gid
services:
  backend:
    user: "1000:1000"

# Решение 2: изменить права на хосте
sudo chown -R 1000:1000 ./backend

Контейнер перезапускается в цикле

# Посмотреть почему падает
docker compose logs backend

# Проверить exit code
docker compose ps -a

depends_on не помогает — сервис стартовал, но не готов

depends_on по умолчанию ждёт только запуска контейнера, не его готовности. Используйте healthcheck + condition: service_healthy — пример в файле выше.

Конфликт версий образа

# Всегда указывайте тег образа явно
image: postgres:16-alpine   # Хорошо
image: postgres              # Плохо — может обновиться и сломать всё

# Обновить образы принудительно
docker compose pull
docker compose up -d

Чек-лист Docker Compose

  • ☐ Установлен Docker Compose v2 (docker compose version)
  • ☐ Секреты вынесены в .env файл, не захардкожены в yml
  • .env добавлен в .gitignore
  • ☐ Для БД используется named volume, не bind mount
  • ☐ Настроен healthcheck для БД
  • depends_on с condition: service_healthy для зависимых сервисов
  • ☐ Образы указаны с явным тегом (postgres:16, не postgres:latest)
  • ☐ Настроен restart: unless-stopped для продакшена
  • ☐ Проверен конфиг командой docker compose config
  • ☐ Сети разделены для изоляции сервисов
  • ☐ Настроен бэкап данных из volumes

Заключение

Docker Compose — стандарт для локальной разработки и простых продакшен-стеков. Один файл описывает весь стек, одна команда его поднимает. Это удобно, воспроизводимо и легко переносится между окружениями.

Используйте Docker Compose когда: одна машина, несколько сервисов, нужна простота и скорость.

Переходите на Kubernetes когда: нужна высокая доступность, несколько серверов, сложный auto-scaling или вы уже чувствуете потолок Compose.

Сохраните статью в закладки — команды и примеры пригодятся.

over_dude
Author: over_dude

Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх