Установка docker-compose — полный гайд

Золотые правила автообновления Docker
Слушайте, товарищи контейнеризаторы! Сегодня я, ваш покорный слуга с пятнадцатилетним стажем в окопах системного администрирования, расскажу вам про установку Docker Compose так, что даже ваша бабушка сможет развернуть полноценный стек приложений. Ну, почти.Docker Compose — это как швейцарский нож для оркестрации контейнеров. Только вместо штопора и ножниц у вас YAML-файл и куча контейнеров, которые слушаются вас как примерные солдаты. За годы работы я видел всё: от джуниоров, запускающих базы данных в продакшене без персистентных volume, до DevOps-инженеров, которые пишут docker-compose.yml на 3000 строк. Поверьте, после этого гайда вы будете где-то посередине — в хорошем смысле.

Что такое Docker Compose и зачем он нужен вашей несчастной душе

Представьте: вы разрабатываете веб-приложение. Вам нужен веб-сервер (nginx, например), бэкенд на Node.js, PostgreSQL для данных, Redis для кэша и, допустим, Elasticsearch для поиска. Раньше вы бы запускали каждый сервис отдельной командой docker run с километровыми параметрами, которые никто не может запомнить. Это как жонглировать бензопилами — технически возможно, но зачем?

Docker Compose решает эту проблему элегантно. Вы описываете всю инфраструктуру в одном YAML-файле, и одной командой поднимаете весь стек. Красота! Я помню времена, когда приходилось писать bash-скрипты на 500 строк для оркестрации контейнеров. Тёмные это были времена, друзья мои.

Основные фишки Docker Compose:

  • Декларативное описание инфраструктуры (YAML рулит, хоть и бесит своими отступами)
  • Автоматическое создание сетей между контейнерами
  • Управление зависимостями сервисов
  • Возможность масштабирования сервисов
  • Переменные окружения и секреты
  • Volumes для персистентности данных (используйте их, умоляю!)

Системные требования и подготовка к установке

Прежде чем мы начнём колдовать с установкой, давайте проверим, готова ли ваша машина к приёму гостя. Docker Compose работает на Linux, macOS и Windows. Лично я фанат Linux (кто бы сомневался), но покажу процесс для всех платформ, потому что не все ещё прозрели.

Минимальные требования:

  • Docker Engine версии 19.03.0 или выше (если у вас старше — обновляйтесь, это не Windows XP держать)
  • 64-битная операционная система
  • Минимум 2 ГБ оперативной памяти (хотя я рекомендую 4 ГБ для комфортной работы)
  • Пара гигабайт свободного дискового пространства
  • Права sudo/root для установки (без них никуда)

Важный момент: Docker Compose версии 2.x теперь идёт как плагин для Docker CLI, а не отдельная утилита. Это значит, что команды изменились с docker-compose на docker compose (без дефиса). Да, я знаю, что вы уже привыкли к старому синтаксису и ваши пальцы автоматически печатают дефис. Добро пожаловать в мир постоянных изменений!

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

Перед установкой Compose убедимся, что Docker Engine уже установлен. Если нет — сначала установите его. Это базовое требование, друзья. Проверяем:

docker --version
docker ps

Если видите версию Docker и список контейнеров (пусть даже пустой) — отлично! Если вылетает ошибка «command not found» — идите устанавливать Docker Engine. Это отдельная песня, которую я спою в другой раз.

Также проверьте, что Docker daemon запущен:

sudo systemctl status docker

Должно быть «active (running)». Если нет — запускаем:

sudo systemctl start docker
sudo systemctl enable docker

Команда enable нужна, чтобы Docker стартовал автоматически при загрузке системы. Поверьте, вы не хотите каждый раз после ребута вспоминать, почему ваши контейнеры не поднялись.

Установка Docker Compose на Linux (Ubuntu/Debian)

Начнём с самой популярной платформы в серверном мире. Ubuntu и Debian — это рабочие лошадки дата-центров. Установка здесь проще простого.

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

Если вы устанавливали Docker через официальный репозиторий, то Compose, скорее всего, уже установлен как плагин. Проверяем:

docker compose version

Видите версию? Поздравляю, можете пропустить этот раздел и идти пить кофе. Нет? Тогда устанавливаем плагин:

# Обновляем список пакетов
sudo apt update

# Устанавливаем плагин Docker Compose
sudo apt install docker-compose-plugin

# Проверяем установку
docker compose version

Вуаля! Должны увидеть что-то вроде «Docker Compose version v2.24.5». Цифры могут отличаться — главное, чтобы оно работало.

Способ 2: Ручная установка бинарника (для тех, кто любит контроль)

Этот способ даёт вам больше контроля над версией. Полезно, когда нужна конкретная версия или когда репозитории отстают от жизни.

# Создаём директорию для плагинов Docker CLI
mkdir -p ~/.docker/cli-plugins/

# Скачиваем последнюю версию Docker Compose
DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)

curl -SL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64" -o ~/.docker/cli-plugins/docker-compose

# Делаем файл исполняемым
chmod +x ~/.docker/cli-plugins/docker-compose

# Проверяем
docker compose version

Этот скрипт автоматически определяет последнюю версию через GitHub API и скачивает её. Я люблю такие решения — они избавляют от необходимости лезть на GitHub и искать актуальную версию руками.

Способ 3: Установка старой версии docker-compose (legacy)

Если вам по каким-то причинам нужна старая версия 1.x (например, для совместимости со старыми скриптами), можете установить её через pip или прямым скачиванием:

# Через pip (требуется Python)
sudo apt install python3-pip
sudo pip3 install docker-compose

# Или прямое скачивание
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Проверка
docker-compose --version

Обратите внимание на дефис в команде! Это старый синтаксис. Но лучше переходите на версию 2.x — будущее за ней.

Установка Docker Compose на CentOS/RHEL/Fedora

Red Hat-based дистрибутивы — тоже популярная штука, особенно в корпоративном секторе. Процесс установки немного отличается, но логика та же.

# Для CentOS/RHEL 8 и выше
sudo dnf update
sudo dnf install docker-compose-plugin

# Для более старых версий или если нужен больший контроль
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Создаём символическую ссылку для удобства
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

# Проверяем
docker compose version

Для Fedora процесс идентичен, только вместо yum используется dnf (что, по сути, и есть в новых версиях CentOS).

Установка Docker Compose на macOS

Пользователи macOS, вы избалованы. Docker Desktop для Mac уже включает Docker Compose из коробки. Просто установите Docker Desktop с официального сайта, и всё будет работать. Но если вы хотите обновить Compose отдельно или у вас кастомная установка:

# Если используете Homebrew (а вы должны его использовать)
brew install docker-compose

# Или ручная установка
mkdir -p ~/.docker/cli-plugins
curl -SL "https://github.com/docker/compose/releases/latest/download/docker-compose-darwin-x86_64" -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose

# Проверка
docker compose version

Для Mac на процессорах Apple Silicon (M1/M2/M3) используйте версию для arm64:

curl -SL "https://github.com/docker/compose/releases/latest/download/docker-compose-darwin-aarch64" -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose

Да, Apple снова всех удивили своими процессорами. Но надо признать — они шустрые!

Установка Docker Compose на Windows

Windows-пользователи, не расстраивайтесь. Docker Desktop для Windows также включает Compose. Скачиваете Docker Desktop, устанавливаете, и готово. Но если вам нужно больше контроля:

Откройте PowerShell от имени администратора и выполните:

# Создаём директорию для плагинов
mkdir -p "$env:USERPROFILE\.docker\cli-plugins"

# Скачиваем Docker Compose
Invoke-WebRequest "https://github.com/docker/compose/releases/latest/download/docker-compose-windows-x86_64.exe" -OutFile "$env:USERPROFILE\.docker\cli-plugins\docker-compose.exe"

# Проверка
docker compose version

Если используете WSL2 (Windows Subsystem for Linux 2) — рекомендую! — то можете следовать инструкциям для Linux внутри вашего WSL-дистрибутива.

Настройка прав доступа и добавление пользователя в группу Docker

Вот здесь начинается магия Linux-администрирования. По умолчанию Docker требует sudo для всех команд. Это безопасно, но неудобно. Каждый раз вводить пароль — так себе удовольствие.

Добавляем текущего пользователя в группу docker:

# Создаём группу docker (если её ещё нет)
sudo groupadd docker

# Добавляем пользователя в группу
sudo usermod -aG docker $USER

# Применяем изменения (без перелогина)
newgrp docker

# Проверяем
docker ps

Теперь можете запускать Docker-команды без sudo. Красота! Но помните: пользователи в группе docker фактически имеют root-доступ к системе через контейнеры. Не добавляйте туда кого попало.

Важное замечание по безопасности: Если вы параноик (и правильно делаете), можете оставить sudo и использовать алиасы для упрощения:

# Добавьте в ~/.bashrc или ~/.zshrc
alias doco='sudo docker compose'
alias docker='sudo docker'

Теперь вместо sudo docker compose up пишете просто doco up. Профит!

Проверка установки и первый запуск

Момент истины! Проверяем, что всё установилось корректно:

# Проверка версии
docker compose version

# Должно вывести что-то вроде:
# Docker Compose version v2.24.5

# Посмотрим справку
docker compose --help

Если видите справку с перечнем команд — поздравляю, установка прошла успешно! Если нет — перечитайте инструкции для вашей ОС или проверьте логи ошибок.

Частые проблемы при установке:

  • Permission denied: Не хватает прав. Используйте sudo или добавьте пользователя в группу docker
  • Command not found: Бинарник не в PATH или установился не туда. Проверьте пути
  • Cannot connect to Docker daemon: Docker daemon не запущен. Стартуйте его через systemctl
  • Version incompatibility: Docker Engine слишком старый. Обновите его до версии 19.03.0+

Структура и синтаксис docker-compose.yml

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

Основная структура файла:

version: '3.8'  # Версия формата Compose-файла

services:  # Описание сервисов (контейнеров)
  service-name:
    image: image-name:tag
    # или
    build: ./path/to/dockerfile
    ports:
      - "host-port:container-port"
    volumes:
      - host-path:container-path
    environment:
      - ENV_VAR=value
    networks:
      - network-name
    depends_on:
      - other-service

networks:  # Определение сетей
  network-name:
    driver: bridge

volumes:  # Определение томов
  volume-name:
    driver: local

Ключевые секции:

  • version: Версия синтаксиса Compose. Используйте 3.8 или выше
  • services: Описание контейнеров, которые будут запущены
  • networks: Сетевая конфигурация (опционально)
  • volumes: Именованные тома для персистентных данных (опционально)

Важно: В YAML используются пробелы, НЕ табы! Стандартный отступ — 2 пробела. Перепутаете — получите синтаксическую ошибку и полчаса дебага. Я знаю, о чём говорю.

Практический пример 1: Простой веб-сервер Nginx

Начнём с чего-то простого. Развернём Nginx для отдачи статического сайта. Создаём файл docker-compose.yml:

version: '3.8'

services:
  web:
    image: nginx:alpine
    container_name: my-nginx
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped

Что здесь происходит:

  • image: nginx:alpine — используем лёгкий образ Nginx на Alpine Linux
  • container_name — даём контейнеру понятное имя
  • ports — пробрасываем порт 80 контейнера на порт 8080 хоста
  • volumes — монтируем локальную папку html в контейнер (ro = read-only)
  • restart: unless-stopped — автоматический перезапуск контейнера

Создаём содержимое:

# Создаём директорию для HTML-файлов
mkdir html

# Создаём простую страницу
echo '<h1>Hello from Docker Compose!</h1>' > html/index.html

# Запускаем
docker compose up -d

# Проверяем
curl http://localhost:8080

Открываем браузер, идём на http://localhost:8080 — должны увидеть приветствие. Если видите — вы прекрасны!

Полезные команды для управления:

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

# Просмотр логов
docker compose logs -f web

# Перезапуск сервиса
docker compose restart web

# Просмотр статуса
docker compose ps

Флаг -d (detached) запускает контейнеры в фоновом режиме. Без него вы увидите логи в реальном времени, но терминал будет занят.

Практический пример 2: WordPress с MySQL

Поднимем что-то посложнее — полноценный WordPress с базой данных. Это классический пример многоконтейнерного приложения.

version: '3.8'

services:
  db:
    image: mysql:8.0
    container_name: wordpress-db
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: mega_secret_password_123
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppassword
    restart: unless-stopped
    networks:
      - wordpress-network

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    container_name: wordpress-app
    ports:
      - "8000:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppassword
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress_data:/var/www/html
    restart: unless-stopped
    networks:
      - wordpress-network

networks:
  wordpress-network:
    driver: bridge

volumes:
  db_data:
  wordpress_data:

Фишки этого compose-файла:

  • depends_on — WordPress стартует только после MySQL
  • Именованные volumes (db_data, wordpress_data) — данные сохранятся даже после удаления контейнеров
  • Отдельная сеть — контейнеры могут общаться по именам сервисов
  • Переменные окружения — передача конфигурации в контейнеры

Запускаем:

docker compose up -d

# Смотрим логи
docker compose logs -f

# Проверяем статус
docker compose ps

Идём в браузер на http://localhost:8000 и проходим процесс установки WordPress. Вся конфигурация и данные сохранятся в именованных томах.

Лайфхак: Храните пароли в файле .env, а не прямо в docker-compose.yml:

# Создаём файл .env
cat > .env << EOF
MYSQL_ROOT_PASSWORD=mega_secret_password_123
MYSQL_PASSWORD=wppassword
EOF

# В docker-compose.yml используем переменные:
environment:
  MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
  MYSQL_PASSWORD: ${MYSQL_PASSWORD}

Не забудьте добавить .env в .gitignore! Пароли в публичных репозиториях — это прямой путь к увольнению.

Практический пример 3: Fullstack приложение (React + Node.js + PostgreSQL + Redis)

Теперь развернём полноценный стек для современного веб-приложения. Это то, с чем вы будете работать в реальных проектах.

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: app-postgres
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppassword
      POSTGRES_DB: appdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: app-redis
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: app-backend
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://appuser:apppassword@postgres:5432/appdb
      REDIS_URL: redis://redis:6379
      JWT_SECRET: ${JWT_SECRET}
    ports:
      - "3001:3000"
    volumes:
      - ./backend:/app
      - /app/node_modules
    networks:
      - app-network
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: app-frontend
    depends_on:
      - backend
    environment:
      REACT_APP_API_URL: http://localhost:3001
    ports:
      - "3000:80"
    networks:
      - app-network
    restart: unless-stopped

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

networks:
  app-network:
    driver: bridge

volumes:
  postgres_data:
  redis_data:

Это уже серьёзная конфигурация! Разберём ключевые моменты:

  • healthcheck — проверка готовности сервиса перед стартом зависимых контейнеров
  • condition: service_healthy — бэкенд стартует только когда БД и Redis здоровы
  • build — собираем образы из Dockerfile вместо использования готовых
  • volumes для node_modules — предотвращает конфликты между хостом и контейнером
  • Nginx как reverse proxy — распределяет трафик между фронтом и бэком

Пример Dockerfile для бэкенда (backend/Dockerfile):

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

Пример конфигурации Nginx (nginx/nginx.conf):

events {
    worker_connections 1024;
}

http {
    upstream backend {
        server backend:3000;
    }

    server {
        listen 80;

        location /api {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location / {
            proxy_pass http://frontend:80;
            proxy_set_header Host $host;
        }
    }
}

Запуск и мониторинг:

# Собираем образы и запускаем
docker compose up -d --build

# Смотрим логи всех сервисов
docker compose logs -f

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

# Проверяем здоровье сервисов
docker compose ps

# Масштабируем бэкенд (3 инстанса)
docker compose up -d --scale backend=3

Это практически production-ready конфигурация. Добавьте SSL-сертификаты через Let’s Encrypt и мониторинг — получите боевую систему.

Практический пример 4: Мониторинг стек (Prometheus + Grafana + Node Exporter)

Любой уважающий себя сисадмин знает: если не мониторишь — не контролируешь. Давайте поднимем полноценный стек мониторинга. Это то, что должно быть в каждом production-окружении.

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
    ports:
      - "9090:9090"
    networks:
      - monitoring
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    depends_on:
      - prometheus
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin123}
      GF_INSTALL_PLUGINS: grafana-piechart-panel
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    ports:
      - "3000:3000"
    networks:
      - monitoring
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($|/)'
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    ports:
      - "9100:9100"
    networks:
      - monitoring
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    privileged: true
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - "8080:8080"
    networks:
      - monitoring
    restart: unless-stopped

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
      - alertmanager_data:/alertmanager
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'
    ports:
      - "9093:9093"
    networks:
      - monitoring
    restart: unless-stopped

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:
  alertmanager_data:

Конфигурация Prometheus (prometheus/prometheus.yml):

global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

Запускаем и настраиваем:

# Создаём директории для конфигов
mkdir -p prometheus grafana/provisioning/datasources alertmanager

# Создаём конфиг Prometheus (показан выше)
cat > prometheus/prometheus.yml << 'EOF'
# ... конфиг из примера выше ...
EOF

# Автоматическое добавление Prometheus как источника данных в Grafana
cat > grafana/provisioning/datasources/prometheus.yml << 'EOF'
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
EOF

# Запускаем стек
docker compose up -d

# Проверяем статус
docker compose ps

Теперь открываем:

  • Prometheus UI: http://localhost:9090
  • Grafana: http://localhost:3000 (admin/admin123)
  • Node Exporter metrics: http://localhost:9100/metrics
  • cAdvisor: http://localhost:8080

В Grafana импортируйте готовые дашборды:

  • Node Exporter Full: Dashboard ID 1860
  • Docker Container & Host Metrics: Dashboard ID 10619
  • Docker Containers: Dashboard ID 893

Это базовый стек мониторинга, который покажет вам всё: CPU, память, диск, сеть, метрики контейнеров. Я использую такую конфигурацию на всех своих серверах. Красивые графики — это не только удобно, но и помогает вовремя заметить проблемы.

Практический пример 5: Development окружение с hot-reload (Python Flask + PostgreSQL + pgAdmin)

Последний пример — комфортная среда разработки. С автоматической перезагрузкой при изменении кода, удобным GUI для базы данных и всеми плюшками для девелопмента.

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    container_name: dev-postgres
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: devdb
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d
    ports:
      - "5432:5432"
    networks:
      - dev-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U devuser"]
      interval: 5s
      timeout: 5s
      retries: 5

  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: dev-pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@dev.local
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    ports:
      - "5050:80"
    networks:
      - dev-network
    depends_on:
      - db
    restart: unless-stopped

  app:
    build:
      context: ./app
      dockerfile: Dockerfile.dev
    container_name: dev-flask-app
    environment:
      FLASK_APP: app.py
      FLASK_ENV: development
      FLASK_DEBUG: 1
      DATABASE_URL: postgresql://devuser:devpass@db:5432/devdb
      PYTHONUNBUFFERED: 1
    volumes:
      - ./app:/app
      - /app/__pycache__
      - /app/.pytest_cache
    ports:
      - "5000:5000"
    networks:
      - dev-network
    depends_on:
      db:
        condition: service_healthy
    command: flask run --host=0.0.0.0 --reload
    stdin_open: true
    tty: true

  redis:
    image: redis:7-alpine
    container_name: dev-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_dev_data:/data
    networks:
      - dev-network

  mailhog:
    image: mailhog/mailhog:latest
    container_name: dev-mailhog
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI
    networks:
      - dev-network

networks:
  dev-network:
    driver: bridge

volumes:
  postgres_dev_data:
  pgadmin_data:
  redis_dev_data:

Dockerfile для разработки (app/Dockerfile.dev):

FROM python:3.11-slim

WORKDIR /app

# Устанавливаем зависимости для разработки
RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# Копируем requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Устанавливаем инструменты для разработки
RUN pip install --no-cache-dir \
    flask-debugtoolbar \
    pytest \
    pytest-cov \
    black \
    flake8 \
    ipython

EXPOSE 5000

# Для hot-reload не указываем CMD - будет в docker-compose

Пример простого Flask приложения (app/app.py):

from flask import Flask, jsonify
import psycopg2
import os

app = Flask(__name__)

def get_db_connection():
    return psycopg2.connect(os.getenv('DATABASE_URL'))

@app.route('/')
def index():
    return jsonify({
        'message': 'Hello from Flask!',
        'environment': os.getenv('FLASK_ENV'),
        'debug': os.getenv('FLASK_DEBUG')
    })

@app.route('/db-check')
def db_check():
    try:
        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute('SELECT version();')
        version = cur.fetchone()[0]
        cur.close()
        conn.close()
        return jsonify({'database': 'connected', 'version': version})
    except Exception as e:
        return jsonify({'database': 'error', 'message': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Файл requirements.txt:

Flask==3.0.0
psycopg2-binary==2.9.9
python-dotenv==1.0.0
redis==5.0.1

Полезные команды для разработки:

# Запуск с пересборкой
docker compose up -d --build

# Просмотр логов приложения в реальном времени
docker compose logs -f app

# Выполнение команд внутри контейнера
docker compose exec app python manage.py db migrate
docker compose exec app pytest
docker compose exec app black .

# Подключение к базе данных
docker compose exec db psql -U devuser -d devdb

# Дамп базы данных
docker compose exec db pg_dump -U devuser devdb > <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/"   title="Резервное копирование" data-wpil-keyword-link="linked"  data-wpil-monitor-id="45">backup</a>.sql

# Восстановление из дампа
docker compose exec -T db psql -U devuser devdb &amp;amp;amp;amp;amp;amp;lt; <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/"   title="Резервное копирование" data-wpil-keyword-link="linked"  data-wpil-monitor-id="247">backup</a>.sql

# Интерактивная оболочка Python в контейнере
docker compose exec app python

# Запуск тестов
docker compose exec app pytest -v --cov=.

# Остановка без удаления томов (данные сохранятся)
docker compose down

# Полная очистка (удаление всех данных)
docker compose down -v

Фишки этой конфигурации для разработки:

  • Hot-reload — изменения в коде применяются автоматически без перезапуска
  • pgAdmin — веб-интерфейс для работы с PostgreSQL (http://localhost:5050)
  • MailHog — перехватывает все email-сообщения для тестирования (http://localhost:8025)
  • Volume для кода — редактируете на хосте, изменения видны в контейнере
  • Исключение кэшей — __pycache__ и .pytest_cache не синхронизируются
  • stdin_open + tty — позволяет использовать debugger (pdb, ipdb)

Это идеальное окружение для разработки. Всё изолировано, легко воспроизводится на другой машине, и вы можете сломать что угодно без последствий для хост-системы.

Продвинутые техники работы с Docker Compose

Использование множественных Compose-файлов

Можно комбинировать несколько compose-файлов. Это удобно для разделения базовой конфигурации и окружений:

# Базовый файл docker-compose.yml
version: &amp;amp;amp;amp;amp;#039;3.8&amp;amp;amp;amp;amp;#039;
services:
  app:
    image: myapp:latest
    environment:
      - NODE_ENV=production

# Переопределения для разработки docker-compose.dev.yml
version: &amp;amp;amp;amp;amp;#039;3.8&amp;amp;amp;amp;amp;#039;
services:
  app:
    build: .
    volumes:
      - ./src:/app/src
    environment:
      - NODE_ENV=development
      - DEBUG=true

# Запуск с комбинацией файлов
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

Переменные окружения и подстановка

Docker Compose поддерживает мощную систему переменных:

# Файл .env
APP_PORT=8080
DB_PASSWORD=secret123
IMAGE_TAG=v1.2.3

# В docker-compose.yml
services:
  app:
    image: myapp:${IMAGE_TAG}
    ports:
      - &amp;amp;amp;amp;amp;quot;${APP_PORT}:80&amp;amp;amp;amp;amp;quot;
    environment:
      DB_PASS: ${DB_PASSWORD}

# Значения по умолчанию
services:
  app:
    image: myapp:${IMAGE_TAG:-latest}

Профили для условного запуска сервисов

Profiles позволяют запускать только нужные сервисы:

services:
  app:
    image: myapp
    # Запускается всегда

  debug-tools:
    image: nicolaka/netshoot
    profiles: [&amp;amp;amp;amp;amp;quot;debug&amp;amp;amp;amp;amp;quot;]
    # Запускается только с --profile debug

  load-testing:
    image: grafana/k6
    profiles: [&amp;amp;amp;amp;amp;quot;testing&amp;amp;amp;amp;amp;quot;]
    # Запускается только с --profile testing

# Запуск с профилем
docker compose --profile debug up
docker compose --profile testing up

Healthchecks и зависимости

Правильная настройка healthcheck гарантирует, что сервисы стартуют в правильном порядке:

services:
  db:
    image: postgres
    healthcheck:
      test: [&amp;amp;amp;amp;amp;quot;CMD-SHELL&amp;amp;amp;amp;amp;quot;, &amp;amp;amp;amp;amp;quot;pg_isready -U postgres&amp;amp;amp;amp;amp;quot;]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  app:
    depends_on:
      db:
        condition: service_healthy
        # Опции: service_started, service_healthy, service_completed_successfully

Ограничение ресурсов

Важно для продакшена — ограничиваем потребление ресурсов:

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: &amp;amp;amp;amp;amp;#039;2.0&amp;amp;amp;amp;amp;#039;
          memory: 2G
        reservations:
          cpus: &amp;amp;amp;amp;amp;#039;0.5&amp;amp;amp;amp;amp;#039;
          memory: 512M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

Логирование

Настройка драйверов логирования предотвратит заполнение диска:

services:
  app:
    image: myapp
    logging:
      driver: &amp;amp;amp;amp;amp;quot;json-file&amp;amp;amp;amp;amp;quot;
      options:
        max-size: &amp;amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;amp;quot;
        max-file: &amp;amp;amp;amp;amp;quot;3&amp;amp;amp;amp;amp;quot;
        labels: &amp;amp;amp;amp;amp;quot;production&amp;amp;amp;amp;amp;quot;

Или используйте централизованное логирование:

services:
  app:
    logging:
      driver: &amp;amp;amp;amp;amp;quot;syslog&amp;amp;amp;amp;amp;quot;
      options:
        syslog-address: &amp;amp;amp;amp;amp;quot;tcp://logserver:514&amp;amp;amp;amp;amp;quot;
        tag: &amp;amp;amp;amp;amp;quot;myapp&amp;amp;amp;amp;amp;quot;

Безопасность Docker Compose

Безопасность — это не опция, это необходимость. Вот чек-лист, который я использую:

1. Никогда не храните секреты в docker-compose.yml

# ПЛОХО - секреты в файле
environment:
  DB_PASSWORD: super_secret_123

# ХОРОШО - используйте переменные или Docker Secrets
environment:
  DB_PASSWORD: ${DB_PASSWORD}

# ЕЩЁ ЛУЧШЕ - Docker Secrets (для Swarm)
secrets:
  - db_password

services:
  db:
    secrets:
      - db_password

2. Используйте непривилегированные пользователи

# В Dockerfile
RUN addgroup -g 1000 appgroup &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; \
    adduser -D -u 1000 -G appgroup appuser

USER appuser

# В docker-compose.yml
services:
  app:
    user: &amp;amp;amp;amp;amp;quot;1000:1000&amp;amp;amp;amp;amp;quot;

3. Оптимизация слоёв Docker

# ПЛОХО - каждый RUN создаёт слой
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y &lt;a class=&quot;wpil_keyword_link&quot; href=&quot;https://it-apteka.com/tag/git/&quot;   title=&quot;Git&quot; data-wpil-keyword-link=&quot;linked&quot;  data-wpil-monitor-id=&quot;227&quot;&gt;git&lt;/a&gt;
RUN rm -rf /var/lib/apt/lists/*

# ХОРОШО - один слой
RUN apt-get update &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; \
    apt-get install -y curl git &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; \
    rm -rf /var/lib/apt/lists/*

4. BuildKit для ускорения сборки

# Включаем BuildKit
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1

# В docker-compose.yml
services:
  app:
    build:
      context: .
      cache_from:
        - myapp:latest

5. Используйте tmpfs для временных файлов

services:
  app:
    tmpfs:
      - /tmp
      - /var/cache
    # Быстрее чем volume, но данные теряются при перезапуске

6. Tune параметры Docker daemon

# /etc/docker/daemon.json
{
  &amp;amp;amp;amp;amp;quot;log-driver&amp;amp;amp;amp;amp;quot;: &amp;amp;amp;amp;amp;quot;json-file&amp;amp;amp;amp;amp;quot;,
  &amp;amp;amp;amp;amp;quot;log-opts&amp;amp;amp;amp;amp;quot;: {
    &amp;amp;amp;amp;amp;quot;max-size&amp;amp;amp;amp;amp;quot;: &amp;amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;amp;quot;,
    &amp;amp;amp;amp;amp;quot;max-file&amp;amp;amp;amp;amp;quot;: &amp;amp;amp;amp;amp;quot;3&amp;amp;amp;amp;amp;quot;
  },
  &amp;amp;amp;amp;amp;quot;storage-driver&amp;amp;amp;amp;amp;quot;: &amp;amp;amp;amp;amp;quot;overlay2&amp;amp;amp;amp;amp;quot;,
  &amp;amp;amp;amp;amp;quot;storage-opts&amp;amp;amp;amp;amp;quot;: [
    &amp;amp;amp;amp;amp;quot;overlay2.override_kernel_check=true&amp;amp;amp;amp;amp;quot;
  ]
}

Docker Compose vs Kubernetes

Вечный вопрос: когда переходить на Kubernetes? Вот моё мнение после работы с обоими:

Используйте Docker Compose если:

  • Приложение работает на одном сервере
  • Команда маленькая (до 5-10 разработчиков)
  • Нет необходимости в автоматическом масштабировании
  • Development и staging окружения
  • Простые микросервисные архитектуры
  • Хочется простоты и быстрого старта

Переходите на Kubernetes если:

  • Нужно запускать на нескольких серверах (кластер)
  • Требуется автоматическое масштабирование
  • Сложная микросервисная архитектура (50+ сервисов)
  • Нужны rolling updates без downtime
  • Требуется advanced networking и service mesh
  • Есть DevOps-команда, способная поддерживать K8s

Не гонитесь за хайпом. Kubernetes — мощный инструмент, но сложный. Если Docker Compose покрывает ваши нужды — используйте его. Я знаю компании, которые успешно работают на Compose в продакшене с миллионами пользователей.

Золотое правило: используйте простейший инструмент, который решает вашу задачу. Сложность должна быть оправдана.

Миграция с docker-compose v1 на v2

Если вы всё ещё используете старый docker-compose (с дефисом), вот как безболезненно мигрировать:

Основные изменения:

# Старая команда
docker-compose up -d

# Новая команда
docker compose up -d

# Создайте алиас для привычки
alias docker-compose=&amp;amp;amp;amp;amp;#039;docker compose&amp;amp;amp;amp;amp;#039;

Проверка совместимости конфигурации:

# Проверяем docker-compose.yml на совместимость
docker compose config

# Конвертируем старый формат в новый
docker compose convert &amp;amp;amp;amp;amp;gt; docker-compose-v2.yml

Обновление версии формата:

# Старый формат (deprecated)
version: &amp;amp;amp;amp;amp;#039;2.4&amp;amp;amp;amp;amp;#039;

# Новый формат (рекомендуется)
version: &amp;amp;amp;amp;amp;#039;3.8&amp;amp;amp;amp;amp;#039;
# Или вообще без version (для Compose v2)

Изменения в синтаксисе:

# v1: extends больше не поддерживается
# Используйте YAML anchors вместо этого

# Пример с YAML anchors
x-common-variables: &amp;amp;amp;amp;amp;amp;common-vars
  ENVIRONMENT: production
  LOG_LEVEL: info

services:
  app1:
    environment:
      &amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;lt;: *common-vars
      APP_NAME: app1
  
  app2:
    environment:
      &amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;lt;: *common-vars
      APP_NAME: app2

Автоматизация и скрипты

Автоматизируйте рутинные задачи. Вот набор скриптов, которые я использую постоянно:

Скрипт для развёртывания:

#!/bin/bash
# deploy.sh

set -e

echo &amp;amp;amp;amp;amp;quot;🚀 Starting deployment...&amp;amp;amp;amp;amp;quot;

# Pull latest changes
git pull origin main

# Pull latest images
echo &amp;amp;amp;amp;amp;quot;📦 Pulling images...&amp;amp;amp;amp;amp;quot;
docker compose pull

# Backup database
echo &amp;amp;amp;amp;amp;quot;💾 Creating database backup...&amp;amp;amp;amp;amp;quot;
./scripts/backup-db.sh

# Deploy with zero-downtime
echo &amp;amp;amp;amp;amp;quot;🔄 Deploying services...&amp;amp;amp;amp;amp;quot;
docker compose up -d --remove-orphans --force-recreate

# Wait for health checks
echo &amp;amp;amp;amp;amp;quot;🏥 Waiting for health checks...&amp;amp;amp;amp;amp;quot;
sleep 10

# Check status
docker compose ps

# Show logs
echo &amp;amp;amp;amp;amp;quot;📋 Recent logs:&amp;amp;amp;amp;amp;quot;
docker compose logs --tail=50

echo &amp;amp;amp;amp;amp;quot;✅ Deployment complete!&amp;amp;amp;amp;amp;quot;

Скрипт для очистки:

#!/bin/bash
# cleanup.sh

echo &amp;amp;amp;amp;amp;quot;🧹 Cleaning up Docker resources...&amp;amp;amp;amp;amp;quot;

# Remove stopped containers
docker compose down --remove-orphans

# Remove unused images
docker image prune -a -f

# Remove unused volumes (CAREFUL!)
read -p &amp;amp;amp;amp;amp;quot;Remove unused volumes? (y/N): &amp;amp;amp;amp;amp;quot; confirm
if [[ $confirm == [yY] ]]; then
    docker volume prune -f
fi

# Remove unused networks
docker &amp;amp;amp;amp;lt;a class=&amp;amp;amp;amp;quot;wpil_keyword_link&amp;amp;amp;amp;quot; href=&amp;amp;amp;amp;quot;https://it-apteka.com/category/networks/&amp;amp;amp;amp;quot;   title=&amp;amp;amp;amp;quot;Сети&amp;amp;amp;amp;quot; data-wpil-keyword-link=&amp;amp;amp;amp;quot;linked&amp;amp;amp;amp;quot;  data-wpil-monitor-id=&amp;amp;amp;amp;quot;51&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;network&amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;gt; prune -f

# Show disk usage
echo &amp;amp;amp;amp;amp;quot;💿 Current disk usage:&amp;amp;amp;amp;amp;quot;
docker system df

echo &amp;amp;amp;amp;amp;quot;✅ Cleanup complete!&amp;amp;amp;amp;amp;quot;

Скрипт для мониторинга:

#!/bin/bash
# monitor.sh

watch -n 2 &amp;amp;amp;amp;amp;amp;#039;
echo &amp;amp;amp;amp;amp;amp;quot;=== Container Status ===&amp;amp;amp;amp;amp;amp;quot;
docker compose ps

echo &amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;amp;quot;=== Resource Usage ===&amp;amp;amp;amp;amp;amp;quot;
docker stats --no-stream --format &amp;amp;amp;amp;amp;amp;quot;table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}&amp;amp;amp;amp;amp;amp;quot;

echo &amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;amp;quot;=== <a class="wpil_keyword_link" href="https://it-apteka.com/category/networks/"   title="Сети" data-wpil-keyword-link="linked"  data-wpil-monitor-id="245">Network</a> ===&amp;amp;amp;amp;amp;amp;quot;
docker network ls | grep $(basename $(pwd))

echo &amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;amp;quot;=== Volumes ===&amp;amp;amp;amp;amp;amp;quot;
docker volume ls | grep $(basename $(pwd))
&amp;amp;amp;amp;amp;amp;#039;

Makefile для удобства:

# Makefile
.PHONY: help build up down logs shell backup deploy clean

help:
	@echo &amp;amp;amp;amp;amp;quot;Available commands:&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make build   - Build images&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make up      - Start services&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make down    - Stop services&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make logs    - Show logs&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make shell   - Open shell in app&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make backup  - Backup database&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make deploy  - Deploy to production&amp;amp;amp;amp;amp;quot;
	@echo &amp;amp;amp;amp;amp;quot;  make clean   - Clean up resources&amp;amp;amp;amp;amp;quot;

build:
	docker compose build --no-cache

up:
	docker compose up -d

down:
	docker compose down

logs:
	docker compose logs -f --tail=100

shell:
	docker compose exec app /bin/bash

backup:
	./scripts/backup-db.sh

deploy:
	./scripts/deploy.sh

clean:
	./scripts/cleanup.sh

restart:
	docker compose restart

ps:
	docker compose ps

stats:
	docker stats

Теперь вместо длинных команд пишете просто:

make up
make logs
make deploy

Красота и элегантность!

Интеграция с CI/CD системами

Docker Compose отлично интегрируется с популярными CI/CD системами. Примеры для основных платформ:

GitHub Actions:

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Copy files to server
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: &amp;amp;amp;amp;amp;quot;.&amp;amp;amp;amp;amp;quot;
          target: &amp;amp;amp;amp;amp;quot;/app&amp;amp;amp;amp;amp;quot;
      
      - name: Deploy with Docker Compose
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /app
            docker compose pull
            docker compose up -d --remove-orphans
            docker compose ps

GitLab CI/CD:

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  DOCKER_DRIVER: overlay2

build:
  stage: build
  script:
    - docker compose build
    - docker compose push

test:
  stage: test
  script:
    - docker compose -f docker-compose.test.yml up --abort-on-container-exit
    - docker compose -f docker-compose.test.yml down

deploy:
  stage: deploy
  script:
    - docker compose pull
    - docker compose up -d --remove-orphans
  only:
    - main
  environment:
    name: production

Jenkins Pipeline:

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        COMPOSE_PROJECT_NAME = &amp;amp;amp;amp;amp;#039;myapp&amp;amp;amp;amp;amp;#039;
    }
    
    stages {
        stage(&amp;amp;amp;amp;amp;#039;Checkout&amp;amp;amp;amp;amp;#039;) {
            steps {
                checkout scm
            }
        }
        
        stage(&amp;amp;amp;amp;amp;#039;Build&amp;amp;amp;amp;amp;#039;) {
            steps {
                sh &amp;amp;amp;amp;amp;#039;docker compose build&amp;amp;amp;amp;amp;#039;
            }
        }
        
        stage(&amp;amp;amp;amp;amp;#039;Test&amp;amp;amp;amp;amp;#039;) {
            steps {
                sh &amp;amp;amp;amp;amp;#039;docker compose -f docker-compose.test.yml up --abort-on-container-exit&amp;amp;amp;amp;amp;#039;
            }
        }
        
        stage(&amp;amp;amp;amp;amp;#039;Deploy&amp;amp;amp;amp;amp;#039;) {
            when {
                branch &amp;amp;amp;amp;amp;#039;main&amp;amp;amp;amp;amp;#039;
            }
            steps {
                sh &amp;amp;amp;amp;amp;#039;&amp;amp;amp;amp;amp;#039;&amp;amp;amp;amp;amp;#039;
                    docker compose pull
                    docker compose up -d --remove-orphans
                    docker compose ps
                &amp;amp;amp;amp;amp;#039;&amp;amp;amp;amp;amp;#039;&amp;amp;amp;amp;amp;#039;
            }
        }
    }
    
    post {
        always {
            sh &amp;amp;amp;amp;amp;#039;docker compose -f docker-compose.test.yml down || true&amp;amp;amp;amp;amp;#039;
        }
    }
}

Работа с секретами и sensitive data

Безопасность данных — это святое. Никогда, слышите, НИКОГДА не коммитьте секреты в Git. Вот правильные способы работы с чувствительными данными:

1. Environment файлы (.env)

# .env.example (коммитим в Git как шаблон)
DB_PASSWORD=change_me
API_KEY=your_key_here
JWT_SECRET=generate_strong_secret

# .env (НЕ коммитим, добавляем в .gitignore)
DB_PASSWORD=prod_super_secret_123
API_KEY=sk_live_abc123xyz
JWT_SECRET=complex_random_string_here

# .gitignore
.env
.env.local
.env.production
*.key
*.pem

2. Docker Secrets (для Docker Swarm)

# Создаём секрет
echo &amp;amp;amp;amp;amp;quot;my_secret_password&amp;amp;amp;amp;amp;quot; | docker secret create db_password -

# В docker-compose.yml для Swarm
version: &amp;amp;amp;amp;amp;#039;3.8&amp;amp;amp;amp;amp;#039;

secrets:
  db_password:
    external: true

services:
  db:
    image: postgres
    secrets:
      - db_password
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

3. Использование внешних secrets managers

# Пример с AWS Secrets Manager
#!/bin/bash
export DB_PASSWORD=$(aws secretsmanager get-secret-value \
    --secret-id prod/db/password \
    --query SecretString \
    --output text)

docker compose up -d

# Пример с HashiCorp Vault
export VAULT_ADDR=&amp;amp;amp;amp;amp;#039;https://vault.example.com&amp;amp;amp;amp;amp;#039;
export DB_PASSWORD=$(vault kv get -field=password secret/prod/db)

docker compose up -d

4. Encrypted files с git-crypt

# Установка git-crypt
sudo apt install git-crypt

# Инициализация
cd your-repo
git-crypt init

# .gitattributes
.env filter=git-crypt diff=git-crypt
secrets/** filter=git-crypt diff=git-crypt

# Теперь .env будет зашифрован в Git

5. SOPS (Secrets OPerationS)

# Установка SOPS
wget https://github.com/mozilla/sops/releases/download/v3.8.1/sops_3.8.1_amd64.deb
sudo dpkg -i sops_3.8.1_amd64.deb

# Шифрование файла
sops -e secrets.yml &amp;amp;amp;amp;amp;gt; secrets.enc.yml

# Расшифровка при деплое
sops -d secrets.enc.yml &amp;amp;amp;amp;amp;gt; .env
docker compose up -d
rm .env

Распространённые ошибки и как их избежать

За годы работы я видел все возможные ошибки. Вот топ-10 самых популярных косяков:

1. Отсутствие персистентных volumes

# ПЛОХО - данные теряются при пересоздании контейнера
services:
  db:
    image: postgres

# ХОРОШО - данные сохраняются
services:
  db:
    image: postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

2. Использование latest тега в продакшене

# ПЛОХО
image: nginx:latest

# ХОРОШО
image: nginx:1.25.3-alpine

3. Игнорирование healthchecks

# БЕЗ healthcheck сервис может стартовать раньше готовности
# С healthcheck - запуск только когда сервис готов
healthcheck:
  test: [&amp;amp;amp;amp;amp;quot;CMD&amp;amp;amp;amp;amp;quot;, &amp;amp;amp;amp;amp;quot;curl&amp;amp;amp;amp;amp;quot;, &amp;amp;amp;amp;amp;quot;-f&amp;amp;amp;amp;amp;quot;, &amp;amp;amp;amp;amp;quot;http://localhost/health&amp;amp;amp;amp;amp;quot;]
  interval: 30s
  timeout: 10s
  retries: 3

4. Хардкод значений вместо переменных

# ПЛОХО
ports:
  - &amp;amp;amp;amp;amp;quot;8080:80&amp;amp;amp;amp;amp;quot;
environment:
  DB_HOST: localhost

# ХОРОШО
ports:
  - &amp;amp;amp;amp;amp;quot;${APP_PORT:-8080}:80&amp;amp;amp;amp;amp;quot;
environment:
  DB_HOST: ${DB_HOST}

5. Отсутствие логирования и мониторинга

# Всегда настраивайте ротацию логов
logging:
  driver: &amp;amp;amp;amp;amp;quot;json-file&amp;amp;amp;amp;amp;quot;
  options:
    max-size: &amp;amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;amp;quot;
    max-file: &amp;amp;amp;amp;amp;quot;3&amp;amp;amp;amp;amp;quot;

6. Запуск контейнеров от root

# Создавайте непривилегированных пользователей
RUN adduser -D -u 1000 appuser
USER appuser

7. Неправильные зависимости между сервисами

# depends_on без condition просто ждёт старта, не готовности
# Используйте healthcheck
depends_on:
  db:
    condition: service_healthy

8. Игнорирование .dockerignore

# .dockerignore
node_modules
.git
.env
*.log
.DS_Store
coverage
dist

9. Отсутствие resource limits

deploy:
  resources:
    limits:
      cpus: &amp;amp;amp;amp;amp;#039;2&amp;amp;amp;amp;amp;#039;
      memory: 2G

10. Коммит секретов в Git

# ВСЕГДА добавляйте в .gitignore
.env
.env.*
!.env.example
*.key
*.pem
secrets/

Полезные инструменты и плагины

Расширьте свой арсенал этими инструментами:

1. Lazydocker — TUI для Docker

# Установка
curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash

# Запуск
lazydocker

Удобный интерфейс для управления контейнерами, просмотра логов и статистики. Must-have для ежедневной работы.

2. Dive — анализатор Docker образов

# Установка
wget https://github.com/wagoodman/dive/releases/download/v0.11.0/dive_0.11.0_linux_amd64.deb
sudo dpkg -i dive_0.11.0_linux_amd64.deb

# Анализ образа
dive myapp:latest

Показывает слои образа и помогает оптимизировать размер.

3. Hadolint — линтер для Dockerfile

# Установка
sudo wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
sudo chmod +x /usr/local/bin/hadolint

# Проверка Dockerfile
hadolint Dockerfile

4. docker-compose-viz — визуализация архитектуры

# Создаёт диаграмму из docker-compose.yml
docker run --rm -it --name dcv -v $(pwd):/input pmsipilot/docker-compose-viz render -m image docker-compose.yml

5. Portainer — веб-интерфейс для Docker

docker volume create portainer_data

docker run -d -p 9000:9000 -p 8000:8000 \
    --name portainer --restart always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data \
    portainer/portainer-ce:latest

Открываете http://localhost:9000 и получаете полноценный GUI для управления Docker.

Шпаргалка по командам Docker Compose

Держите эту шпаргалку под рукой:

# === Основные команды ===

# Запуск сервисов
docker compose up                    # В foreground
docker compose up -d                 # В background (detached)
docker compose up --build            # С пересборкой образов
docker compose up --force-recreate   # Пересоздать контейнеры

# Остановка и удаление
docker compose down                  # Остановить и удалить контейнеры
docker compose down -v               # + удалить volumes
docker compose stop                  # Только остановить (не удалять)
docker compose start                 # Запустить остановленные

# Перезапуск
docker compose restart               # Все сервисы
docker compose restart web           # Конкретный сервис

# === Информация и мониторинг ===

docker compose ps                    # Статус сервисов
docker compose ps -a                 # Включая остановленные
docker compose logs                  # Логи всех сервисов
docker compose logs -f               # С отслеживанием
docker compose logs --tail=100 web   # Последние 100 строк
docker compose top                   # Процессы в контейнерах
docker compose stats                 # Статистика ресурсов

# === Выполнение команд ===

docker compose exec web bash         # Интерактивная оболочка
docker compose exec web ls -la       # Выполнить команду
docker compose exec -T web cat file  # Без TTY (для pipe)
docker compose run web python test.py # Одноразовый контейнер

# === Сборка и конфигурация ===

docker compose build                 # Собрать образы
docker compose build --no-cache      # Без кэша
docker compose pull                  # Обновить образы
docker compose push                  # Отправить в registry
docker compose config                # Показать итоговую конфигурацию
docker compose config --services     # Список сервисов

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

docker compose up -d --scale web=3   # 3 инстанса web
docker compose up -d --scale web=1   # Вернуть к 1

# === Volumes и &amp;amp;amp;lt;a class=&amp;amp;amp;quot;wpil_keyword_link&amp;amp;amp;quot; href=&amp;amp;amp;quot;https://it-apteka.com/category/networks/&amp;amp;amp;quot;   title=&amp;amp;amp;quot;Сети&amp;amp;amp;quot; data-wpil-keyword-link=&amp;amp;amp;quot;linked&amp;amp;amp;quot;  data-wpil-monitor-id=&amp;amp;amp;quot;60&amp;amp;amp;quot;&amp;amp;amp;gt;сети&amp;amp;amp;lt;/a&amp;amp;amp;gt; ===

docker compose down -v               # Удалить volumes
docker volume ls                     # Список volumes
docker network ls                    # Список сетей

# === Разное ===

docker compose version               # Версия Compose
docker compose pause                 # Приостановить контейнеры
docker compose unpause               # Возобновить
docker compose kill                  # Убить контейнеры (SIGKILL)
docker compose rm                    # Удалить остановленные контейнеры
docker compose images                # Список используемых образов
docker compose port web 80           # Узнать проброшенный порт

Сохраните эту шпаргалку и держите под рукой. Со временем команды запомнятся, но в начале она очень помогает.

Заключение и следующие шаги

Поздравляю! Вы прошли путь от установки до продвинутых техник работы с Docker Compose. Это мощный инструмент, который значительно упростит вашу жизнь.

Что дальше? Вот мои рекомендации:

1. Практикуйтесь — Создайте свой проект, поднимите стек для вашего приложения. Только практика даёт настоящее понимание.

2. Изучите официальную документацию — https://docs.docker.com/compose/ содержит море полезной информации, которую я не смог уместить в одну статью.

3. Исследуйте готовые примеры — Awesome Compose (https://github.com/docker/awesome-compose) — репозиторий с десятками готовых примеров для разных стеков.

4. Автоматизируйте — Создавайте скрипты, Makefile’ы, интегрируйте с CI/CD. Автоматизация освобождает время для более интересных задач.

5. Делитесь опытом — Помогайте другим, пишите документацию для ваших проектов, делитесь конфигурациями.

Помните: Docker Compose — это инструмент. Не самоцель, а средство для достижения цели. Используйте его с умом, не усложняйте без необходимости, и он будет верно служить вам годами.

За годы работы я перешёл от bash-скриптов на сотни строк к элегантным docker-compose.yml на пару десятков. Это эволюция. Вы тоже пройдёте этот путь.

Удачи в контейнеризации! Пусть ваши сервисы всегда будут в состоянии «healthy», а логи — читаемыми. 🚀

P.S. Если что-то пошло не так — не паникуйте, читайте логи, гуглите ошибки и помните: docker compose down && docker compose up решает 80% проблем. Остальные 20% — это docker compose down -v && docker compose up (но будьте осторожны с -v).

Полезные ссылки:

  • Официальная документация Docker Compose: https://docs.docker.com/compose/
  • Compose Specification: https://compose-spec.io/
  • Docker Hub для поиска образов: https://hub.docker.com/
  • Awesome Compose примеры: https://github.com/docker/awesome-compose
  • Best practices: https://docs.docker.com/develop/dev-best-practices/
  • Security scanning: https://docs.docker.com/scout/
Поделитесь:

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

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

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