Что такое 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 Linuxcontainer_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 &amp;amp;amp;amp;amp;gt; &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/rezervnoe-kopirovanie/&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;45&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;backup&amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;gt;.sql # Восстановление из дампа docker compose exec -T db psql -U devuser devdb &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;#039;3.8&amp;amp;amp;amp;#039;
services:
app:
image: myapp:latest
environment:
- NODE_ENV=production
# Переопределения для разработки docker-compose.dev.yml
version: &amp;amp;amp;amp;#039;3.8&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;quot;${APP_PORT}:80&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;quot;debug&amp;amp;amp;amp;quot;]
# Запускается только с --profile debug
load-testing:
image: grafana/k6
profiles: [&amp;amp;amp;amp;quot;testing&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;quot;CMD-SHELL&amp;amp;amp;amp;quot;, &amp;amp;amp;amp;quot;pg_isready -U postgres&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;#039;2.0&amp;amp;amp;amp;#039;
memory: 2G
reservations:
cpus: &amp;amp;amp;amp;#039;0.5&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;quot;json-file&amp;amp;amp;amp;quot;
options:
max-size: &amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;quot;
max-file: &amp;amp;amp;amp;quot;3&amp;amp;amp;amp;quot;
labels: &amp;amp;amp;amp;quot;production&amp;amp;amp;amp;quot;
Или используйте централизованное логирование:
services:
app:
logging:
driver: &amp;amp;amp;amp;quot;syslog&amp;amp;amp;amp;quot;
options:
syslog-address: &amp;amp;amp;amp;quot;tcp://logserver:514&amp;amp;amp;amp;quot;
tag: &amp;amp;amp;amp;quot;myapp&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; \
adduser -D -u 1000 -G appgroup appuser
USER appuser
# В docker-compose.yml
services:
app:
user: &amp;amp;amp;amp;quot;1000:1000&amp;amp;amp;amp;quot;
3. Оптимизация слоёв Docker
# ПЛОХО - каждый RUN создаёт слой
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y <a class="wpil_keyword_link" href="https://it-apteka.com/tag/git/" title="Git" data-wpil-keyword-link="linked" data-wpil-monitor-id="227">git</a>
RUN rm -rf /var/lib/apt/lists/*
# ХОРОШО - один слой
RUN apt-get update &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; \
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;quot;log-driver&amp;amp;amp;amp;quot;: &amp;amp;amp;amp;quot;json-file&amp;amp;amp;amp;quot;,
&amp;amp;amp;amp;quot;log-opts&amp;amp;amp;amp;quot;: {
&amp;amp;amp;amp;quot;max-size&amp;amp;amp;amp;quot;: &amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;quot;,
&amp;amp;amp;amp;quot;max-file&amp;amp;amp;amp;quot;: &amp;amp;amp;amp;quot;3&amp;amp;amp;amp;quot;
},
&amp;amp;amp;amp;quot;storage-driver&amp;amp;amp;amp;quot;: &amp;amp;amp;amp;quot;overlay2&amp;amp;amp;amp;quot;,
&amp;amp;amp;amp;quot;storage-opts&amp;amp;amp;amp;quot;: [
&amp;amp;amp;amp;quot;overlay2.override_kernel_check=true&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;#039;docker compose&amp;amp;amp;amp;#039;
Проверка совместимости конфигурации:
# Проверяем docker-compose.yml на совместимость docker compose config # Конвертируем старый формат в новый docker compose convert &amp;amp;amp;amp;gt; docker-compose-v2.yml
Обновление версии формата:
# Старый формат (deprecated) version: &amp;amp;amp;amp;#039;2.4&amp;amp;amp;amp;#039; # Новый формат (рекомендуется) version: &amp;amp;amp;amp;#039;3.8&amp;amp;amp;amp;#039; # Или вообще без version (для Compose v2)
Изменения в синтаксисе:
# v1: extends больше не поддерживается
# Используйте YAML anchors вместо этого
# Пример с YAML anchors
x-common-variables: &amp;amp;amp;amp;amp;common-vars
ENVIRONMENT: production
LOG_LEVEL: info
services:
app1:
environment:
&amp;amp;amp;amp;lt;&amp;amp;amp;amp;lt;: *common-vars
APP_NAME: app1
app2:
environment:
&amp;amp;amp;amp;lt;&amp;amp;amp;amp;lt;: *common-vars
APP_NAME: app2
Автоматизация и скрипты
Автоматизируйте рутинные задачи. Вот набор скриптов, которые я использую постоянно:
Скрипт для развёртывания:
#!/bin/bash # deploy.sh set -e echo &amp;amp;amp;amp;quot;🚀 Starting deployment...&amp;amp;amp;amp;quot; # Pull latest changes git pull origin main # Pull latest images echo &amp;amp;amp;amp;quot;📦 Pulling images...&amp;amp;amp;amp;quot; docker compose pull # Backup database echo &amp;amp;amp;amp;quot;💾 Creating database backup...&amp;amp;amp;amp;quot; ./scripts/backup-db.sh # Deploy with zero-downtime echo &amp;amp;amp;amp;quot;🔄 Deploying services...&amp;amp;amp;amp;quot; docker compose up -d --remove-orphans --force-recreate # Wait for health checks echo &amp;amp;amp;amp;quot;🏥 Waiting for health checks...&amp;amp;amp;amp;quot; sleep 10 # Check status docker compose ps # Show logs echo &amp;amp;amp;amp;quot;📋 Recent logs:&amp;amp;amp;amp;quot; docker compose logs --tail=50 echo &amp;amp;amp;amp;quot;✅ Deployment complete!&amp;amp;amp;amp;quot;
Скрипт для очистки:
#!/bin/bash
# cleanup.sh
echo &amp;amp;amp;amp;quot;🧹 Cleaning up Docker resources...&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;quot;Remove unused volumes? (y/N): &amp;amp;amp;amp;quot; confirm
if [[ $confirm == [yY] ]]; then
docker volume prune -f
fi
# Remove unused networks
docker &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;51&amp;amp;amp;quot;&amp;amp;amp;gt;network&amp;amp;amp;lt;/a&amp;amp;amp;gt; prune -f
# Show disk usage
echo &amp;amp;amp;amp;quot;💿 Current disk usage:&amp;amp;amp;amp;quot;
docker system df
echo &amp;amp;amp;amp;quot;✅ Cleanup complete!&amp;amp;amp;amp;quot;
Скрипт для мониторинга:
#!/bin/bash
# monitor.sh
watch -n 2 &amp;amp;amp;amp;amp;#039;
echo &amp;amp;amp;amp;amp;quot;=== Container Status ===&amp;amp;amp;amp;amp;quot;
docker compose ps
echo &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;quot;=== Resource Usage ===&amp;amp;amp;amp;amp;quot;
docker stats --no-stream --format &amp;amp;amp;amp;amp;quot;table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}&amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;quot;
echo &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;quot;
docker network ls | grep $(basename $(pwd))
echo &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;quot;
echo &amp;amp;amp;amp;amp;quot;=== Volumes ===&amp;amp;amp;amp;amp;quot;
docker volume ls | grep $(basename $(pwd))
&amp;amp;amp;amp;amp;#039;
Makefile для удобства:
# Makefile .PHONY: help build up down logs shell backup deploy clean help: @echo &amp;amp;amp;amp;quot;Available commands:&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make build - Build images&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make up - Start services&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make down - Stop services&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make logs - Show logs&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make shell - Open shell in app&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make backup - Backup database&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make deploy - Deploy to production&amp;amp;amp;amp;quot; @echo &amp;amp;amp;amp;quot; make clean - Clean up resources&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;quot;.&amp;amp;amp;amp;quot;
target: &amp;amp;amp;amp;quot;/app&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;#039;myapp&amp;amp;amp;amp;#039;
}
stages {
stage(&amp;amp;amp;amp;#039;Checkout&amp;amp;amp;amp;#039;) {
steps {
checkout scm
}
}
stage(&amp;amp;amp;amp;#039;Build&amp;amp;amp;amp;#039;) {
steps {
sh &amp;amp;amp;amp;#039;docker compose build&amp;amp;amp;amp;#039;
}
}
stage(&amp;amp;amp;amp;#039;Test&amp;amp;amp;amp;#039;) {
steps {
sh &amp;amp;amp;amp;#039;docker compose -f docker-compose.test.yml up --abort-on-container-exit&amp;amp;amp;amp;#039;
}
}
stage(&amp;amp;amp;amp;#039;Deploy&amp;amp;amp;amp;#039;) {
when {
branch &amp;amp;amp;amp;#039;main&amp;amp;amp;amp;#039;
}
steps {
sh &amp;amp;amp;amp;#039;&amp;amp;amp;amp;#039;&amp;amp;amp;amp;#039;
docker compose pull
docker compose up -d --remove-orphans
docker compose ps
&amp;amp;amp;amp;#039;&amp;amp;amp;amp;#039;&amp;amp;amp;amp;#039;
}
}
}
post {
always {
sh &amp;amp;amp;amp;#039;docker compose -f docker-compose.test.yml down || true&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;quot;my_secret_password&amp;amp;amp;amp;quot; | docker secret create db_password -
# В docker-compose.yml для Swarm
version: &amp;amp;amp;amp;#039;3.8&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;#039;https://vault.example.com&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;gt; secrets.enc.yml # Расшифровка при деплое sops -d secrets.enc.yml &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;quot;CMD&amp;amp;amp;amp;quot;, &amp;amp;amp;amp;quot;curl&amp;amp;amp;amp;quot;, &amp;amp;amp;amp;quot;-f&amp;amp;amp;amp;quot;, &amp;amp;amp;amp;quot;http://localhost/health&amp;amp;amp;amp;quot;] interval: 30s timeout: 10s retries: 3
4. Хардкод значений вместо переменных
# ПЛОХО
ports:
- &amp;amp;amp;amp;quot;8080:80&amp;amp;amp;amp;quot;
environment:
DB_HOST: localhost
# ХОРОШО
ports:
- &amp;amp;amp;amp;quot;${APP_PORT:-8080}:80&amp;amp;amp;amp;quot;
environment:
DB_HOST: ${DB_HOST}
5. Отсутствие логирования и мониторинга
# Всегда настраивайте ротацию логов
logging:
driver: &amp;amp;amp;amp;quot;json-file&amp;amp;amp;amp;quot;
options:
max-size: &amp;amp;amp;amp;quot;10m&amp;amp;amp;amp;quot;
max-file: &amp;amp;amp;amp;quot;3&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;#039;2&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;lt;a class=&amp;amp;quot;wpil_keyword_link&amp;amp;quot; href=&amp;amp;quot;https://it-apteka.com/category/networks/&amp;amp;quot; title=&amp;amp;quot;Сети&amp;amp;quot; data-wpil-keyword-link=&amp;amp;quot;linked&amp;amp;quot; data-wpil-monitor-id=&amp;amp;quot;60&amp;amp;quot;&amp;amp;gt;сети&amp;amp;lt;/a&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/


