Docker Compose

Docker Compose — інструмент для запуску та керування багатьма контейнерами як єдиним застосунком. Замість десятка docker run команд — один файл docker-compose.yml.

Навіщо Compose?

Уявіть типовий веб-застосунок:

                    Без Compose

docker run -d --name db -e POSTGRES_PASSWORD=secret postgres:15
docker run -d --name redis redis:7-alpine
docker run -d --name app --link db --link redis -p 8000:8000 myapp
docker run -d --name nginx --link app -p 80:80 nginx

# А тепер перезапустіть все... чи оновіть... чи перенесіть на інший сервер 😫
                    З Compose

docker compose up -d    # Запустити все
docker compose down     # Зупинити все
docker compose logs     # Логи всіх сервісів

# Один файл — вся конфігурація ✨

Структура docker-compose.yml

# docker-compose.yml

services:              # Список сервісів (контейнерів)
  web:                 # Ім'я сервісу (буде ім'ям контейнера)
    image: nginx       # Образ для запуску
    ports:
      - "80:80"        # Маппінг портів

  db:
    image: postgres:15
    environment:       # Змінні середовища
      POSTGRES_PASSWORD: secret
    volumes:           # Томи для даних
      - db_data:/var/lib/postgresql/data

volumes:               # Оголошення іменованих томів
  db_data:

Основні директиви

services

Кожен сервіс — це окремий контейнер:

services:
  frontend:
    image: nginx:alpine

  backend:
    build: ./backend      # Побудувати з Dockerfile

  database:
    image: postgres:15

image vs build

services:
  # Використати готовий образ
  redis:
    image: redis:7-alpine

  # Побудувати з Dockerfile
  app:
    build: .              # Dockerfile в поточній директорії

  # Побудувати з кастомними параметрами
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.prod

ports

services:
  web:
    ports:
      - "80:80"           # host:container
      - "443:443"
      - "127.0.0.1:8080:8080"  # тільки localhost

environment

services:
  db:
    environment:
      # Прямі значення
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret

      # Зі змінних оточення хоста
      API_KEY: ${API_KEY}

      # Зі значенням за замовчуванням
      DEBUG: ${DEBUG:-false}

Або з файлу:

services:
  app:
    env_file:
      - .env
      - .env.local

volumes

services:
  db:
    volumes:
      # Іменований том (рекомендовано для даних)
      - db_data:/var/lib/postgresql/data

      # Bind mount (для розробки)
      - ./src:/app/src

      # Read-only
      - ./config:/etc/app/config:ro

volumes:
  db_data:    # Оголошення тому
┌────────────────────────────────────────────────────────┐
                   ТИПИ VOLUMES                         
├────────────────────────────────────────────────────────┤
                                                        
  Named Volume (db_data:/path)                          
  ├── Керується Docker                                  
  ├── Зберігається в /var/lib/docker/volumes/          
  └── Рекомендовано для production даних               
                                                        
  Bind Mount (./host/path:/container/path)             
  ├── Пряме монтування директорії хоста                
  ├── Зміни видно одразу                               
  └── Рекомендовано для розробки                       
                                                        
└────────────────────────────────────────────────────────┘

networks

services:
  frontend:
    networks:
      - frontend_net

  backend:
    networks:
      - frontend_net
      - backend_net

  db:
    networks:
      - backend_net

networks:
  frontend_net:
  backend_net:

По замовчуванню Compose створює одну мережу для всіх сервісів проєкту.

depends_on

services:
  app:
    depends_on:
      - db
      - redis
    # app запуститься після db та redis

  db:
    image: postgres:15

  redis:
    image: redis:7

Важливо: depends_on чекає тільки запуску контейнера, а не готовності сервісу! Для перевірки готовності використовуйте healthcheck:

services:
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  app:
    depends_on:
      db:
        condition: service_healthy

restart

services:
  web:
    restart: unless-stopped   # Рекомендовано для production

  worker:
    restart: always           # Завжди перезапускати

  task:
    restart: "no"             # Не перезапускати (за замовчуванням)

  cron:
    restart: on-failure       # Тільки при помилці

Команди

Запуск

# Запустити всі сервіси
docker compose up

# У фоновому режимі
docker compose up -d

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

# Запустити конкретні сервіси
docker compose up -d web db

Зупинка

# Зупинити та видалити контейнери
docker compose down

# Зупинити, видалити контейнери ТА томи
docker compose down -v

# Тільки зупинити (без видалення)
docker compose stop

Логи

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

# В реальному часі
docker compose logs -f

# Конкретного сервісу
docker compose logs -f web

# Останні 100 рядків
docker compose logs --tail 100 web

Статус

# Список запущених контейнерів
docker compose ps

# Всі (включно з зупиненими)
docker compose ps -a

Виконання команд

# Виконати команду в сервісі
docker compose exec web bash

# Одноразово запустити команду
docker compose run --rm web python manage.py migrate

Практичний приклад: WordPress + MySQL

Створимо повноцінний WordPress з базою даних.

Структура проєкту

wordpress-site/
├── docker-compose.yml
├── .env
└── data/               # Буде створено автоматично
    ├── mysql/
    └── wordpress/

docker-compose.yml

services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html

volumes:
  mysql_data:
  wordpress_data:

.env

MYSQL_ROOT_PASSWORD=supersecretroot123
MYSQL_PASSWORD=wordpresspass456

Запуск

# Створити директорію та файли
mkdir wordpress-site && cd wordpress-site
# Створити docker-compose.yml та .env як вище

# Запустити
docker compose up -d

# Перевірити
docker compose ps
docker compose logs -f

# Відкрити http://localhost:8080

Керування

# Бекап бази даних
docker compose exec db mysqldump -u root -p wordpress > backup.sql

# Зайти в MySQL
docker compose exec db mysql -u root -p

# Оновити WordPress
docker compose pull wordpress
docker compose up -d

Мережі в Compose

┌─────────────────────────────────────────────────────────────────┐
│                    Docker Compose Network                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────┐          ┌─────────────┐                     │
│   │  wordpress  │ ──────── │     db      │                     │
│   │             │  "db"    │   (mysql)   │                     │
│   │  port:8080  │          │  port:3306  │                     │
│   └─────────────┘          └─────────────┘                     │
│          │                        │                             │
│          │                        │                             │
│          └── DNS: "wordpress" ────┴── DNS: "db"                │
│                                                                 │
│   Контейнери бачать один одного за іменем сервісу!             │
└─────────────────────────────────────────────────────────────────┘

В Compose сервіси автоматично можуть звертатись один до одного за іменем:
- wordpress може підключитись до db:3306
- Не потрібно вказувати IP адреси

Типові помилки

Порт вже зайнятий

# Помилка
Error: bind: address already in use

# Знайти що займає порт
lsof -i :8080

# Змінити порт в docker-compose.yml
ports:
  - "8081:80"  # Використати інший порт

База даних не готова

# Проблема: застосунок стартує раніше за БД

# Рішення: healthcheck + condition
services:
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s
      retries: 5

  app:
    depends_on:
      db:
        condition: service_healthy

Файли не синхронізуються

# Для розробки на Linux/Mac — працює одразу
volumes:
  - ./src:/app/src

# На Windows можуть бути проблеми з правами
# Рішення: використовувати WSL2

Втрата даних після down

# Проблема
docker compose down -v  # -v видаляє томи!

# Рішення: використовувати іменовані томи та не вказувати -v
docker compose down     # Безпечно — томи залишаються

Поради

Development vs Production

# docker-compose.yml — базова конфігурація
services:
  app:
    image: myapp

# docker-compose.override.yml — для розробки (автоматично зчитується)
services:
  app:
    build: .
    volumes:
      - ./src:/app/src
    environment:
      DEBUG: "true"

# docker-compose.prod.yml — для production
services:
  app:
    restart: unless-stopped
    environment:
      DEBUG: "false"
# Розробка (використовує override автоматично)
docker compose up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

.dockerignore

Щоб не копіювати зайве при build:

# .dockerignore
.git
.env
*.md
__pycache__
node_modules
.vscode

Див. також

Шлях: infrastructure/docker-compose-intro.md

UMTC Wiki © 2026 | Ukrainian Military Tactical Communications