До основного вмісту

Docker Compose: запуск стеку застосунків

VDS / VPS · 11.02.2025 · 5 хв читання
Ілюстрація до статті «Docker Compose: запуск стеку застосунків»

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

version: '3.9'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./www:/var/www/html:ro
    depends_on:
      php:
        condition: service_healthy
    networks:
      - app-net

  php:
    image: php:8.2-fpm-alpine
    volumes:
      - ./www:/var/www/html
    healthcheck:
      test: ["CMD", "php-fpm", "-t"]
      interval: 10s
      timeout: 5s
      retries: 3
    networks:
      - app-net

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASS}
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-net

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASS}
    networks:
      - app-net

volumes:
  mysql-data:

networks:
  app-net:

Файл .env для змінних

DB_ROOT_PASS=supersecret
DB_NAME=myapp
DB_USER=appuser
DB_PASS=dbpassword
REDIS_PASS=redispass

Команди керування

docker compose up -d        # Запустити у фоновому режимі
docker compose down         # Зупинити
docker compose logs -f      # Логи
docker compose ps           # Статус сервісів
docker compose exec php sh  # Увійти в контейнер
💡 Використовуйте іменовані томи (volumes) для персистентних даних — вони зберігаються при docker compose down.

Розбір типових помилок

Помилка Bind for 0.0.0.0:80 failed: port is already allocated означає, що порт 80 вже зайнятий — або іншим контейнером, або системним Nginx/Apache, запущеним напряму на хості.

Error response from daemon: driver failed programming external connectivity on endpoint nginx:
Bind for 0.0.0.0:80 failed: port is already allocated

Знайдіть, хто тримає порт: ss -tlnp | grep :80. Якщо це системний Nginx — зупиніть і вимкніть його автозапуск (systemctl disable --now nginx) перед тим, як піднімати контейнерний стек з тим самим портом, інакше конфлікт повториться після кожного перезавантаження сервера.

Контейнер php постійно перезапускається (Restarting (1) X seconds ago) — перш ніж гадати, погляньте на логи самого контейнера, а не лише статус:

docker compose logs php --tail=50

Часта причина для PHP-FPM — синтаксична помилка в примонтованому коді або відсутнє розширення, якого немає в базовому образі php:8.2-fpm-alpine; Alpine-образи мінімальні і не включають багато розширень за замовчуванням, їх потрібно ставити явно через окремий Dockerfile.

Порівняння способів передачі секретів

Приклад вище зберігає паролі в .env — це прийнятно для розробки, але для продакшену такий файл має бути поза git і мати права 600, інакше будь-який доступ до диска сервера рівносильний витоку всіх паролів одразу.

СпосібДе зберігаєтьсяРизик
Паролі прямо в docker-compose.ymlУ самому файлі, часто в gitВисокий — секрет витікає разом з репозиторієм
Файл .env поруч з composeОкремий файл, не в образіСередній — потрібен .gitignore, але секрет все ще plaintext на диску
Docker secrets (Swarm)Зашифровано в Raft-лозі кластераНизький, але потребує режиму Swarm

Коли Docker Compose не підходить

  • Потрібно розподілити сервіси між кількома фізичними серверами — Compose керує контейнерами лише на одному хості, для кластера потрібен Docker Swarm або Kubernetes.
  • Потрібне автоматичне масштабування сервісу під навантаженням — Compose запускає фіксовану кількість реплік, яку потрібно змінювати вручну командою --scale, авто-скейлінгу тут немає.
  • Продакшен з жорсткими вимогами до відмовостійкості — при падінні хоста всі контейнери Compose падають разом з ним, без оркестратора з кількома вузлами відновлення повністю ручне.
  • Потрібен zero-downtime деплой нової версії сервісу — docker compose up -d під час оновлення образу перестворює контейнер з коротким простоєм, для дійсно безшовного оновлення потрібен окремий механізм на кшталт rolling update у Swarm чи Kubernetes.

Перевірка результату

  1. Переконайтесь, що всі сервіси запущені і здорові: docker compose ps має показувати статус running, а у php — додатково (healthy).
  2. Перевірте, що сайт справді відповідає через Nginx: curl -I http://localhost має повернути 200 або очікуваний код редиректу.
  3. Перевірте, що дані MySQL переживають перестворення контейнерів: docker compose down && docker compose up -d, потім переконайтесь, що таблиці на місці — завдяки іменованому тому mysql-data.
  4. Перегляньте логи на помилки одразу після старту: docker compose logs --tail=100 не повинен містити повторюваних трейсів винятків.

Для глибшого налаштування самого Docker на VDS — стаття про встановлення і налаштування Docker. Якщо потрібен власний registry для приватних образів замість Docker Hub — див. статтю про приватний Docker registry.

Часті питання

Що робити, якщо порт 80 вже зайнятий системним Nginx, коли піднімаю стек через Docker Compose?

Потрібно вирішити, який сервіс слухатиме порт: або зупинити і вимкнути автозапуск системного Nginx перед запуском контейнерів, або змінити прокидання портів у compose-файлі на інший зовнішній порт. Якщо просто перезапустити стек без цього рішення, конфлікт портів повернеться під час кожного перезавантаження хоста, бо системна служба знову підніметься раніше за контейнери.

Чи достатньо зберігати паролі у файлі .env для продакшену?

Формально працює, але з застереженнями: файл має лежати поза git і мати суворі права доступу, інакше будь-хто, хто отримає доступ до диска сервера, отримує одразу всі секрети у відкритому вигляді. Для серйознішого захисту існує режим Docker Swarm із зашифрованим зберіганням секретів — але він вимагає переходу в цей режим, а не просто додавання файлу .env поруч із compose.

Чи може Docker Compose сам збільшувати кількість контейнерів сервісу під навантаженням?

Ні, Compose запускає фіксовану кількість реплік, задану в конфігу чи передану прапорцем масштабування вручну, і сам не реагує на зростання навантаження. Для автоматичного масштабування під реальним навантаженням потрібен оркестратор на кшталт Kubernetes чи режим Swarm із відповідними правилами — звичайний Compose для цього не призначений.

Як оновити образ сервісу в робочому стеку без простою для відвідувачів сайту?

Стандартна команда оновлення образу перестворює контейнер з коротким перериванням обслуговування — це не справжній zero-downtime деплой. Для дійсно безшовного оновлення потрібен окремий механізм на кшталт rolling update, доступний у режимі Swarm чи в Kubernetes, де старий контейнер знімається з балансування лише тоді, коли новий повністю готовий приймати трафік.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці