Продакшн-деплой Docker Compose: 7 ключевых стратегий от health checks до обновлений без простоя
«Работает на моей машине» — Кладбище продакшн-контейнеров
Мантра каждого разработчика: «Работает на моей машине.» Но когда контейнеры попадают в продакшн, начинается настоящий кошмар:
- Контейнеры тихо убиваются OOM Killer, оставляя в логах только
Out of memory - База данных ещё не готова, контейнеры приложения кричат
Connection refused - Контейнер падает в 3 часа ночи без политики перезапуска — сервис недоступен до утра
- Файлы логов заполняют диски,
docker logsвыводит десятки ГБ неструктурированного текста - Продакшн-учётные данные в открытом виде внутри
docker-compose.yml, пароли от БД раскрыты
Если вы всё ещё используете docker compose up -d как всю свою продакшн-стратегию — эта статья для вас.
Справочник ключевых концепций
| Концепция | Назначение | Ключевая продакшн-конфигурация |
|---|---|---|
| Health Check | Определить, действительно ли контейнер готов | healthcheck + depends_on.condition |
| Лимиты ресурсов | Ограничить CPU/память, предотвратить перерасход | deploy.resources.limits |
| Политика перезапуска | Автоперезапуск при аномальном завершении | deploy.restart_policy |
| Secrets | Шифрованное хранение конфиденциальных данных | secrets + Docker Secret |
| Драйвер логирования | Структурированное логирование + ротация логов | logging.driver + logging.options |
| Profiles | Выборочный запуск сервисов по окружению | profiles |
| Watch | Автосинхронизация изменений файлов в контейнеры | watch (Compose Watch) |
5 Продакшн-проблем
Проблема 1: Неконтролируемый порядок запуска контейнеров
База данных всё ещё инициализируется, пока контейнер приложения пытается подключиться, что приводит к ошибкам запуска. depends_on гарантирует только порядок запуска, а не готовность сервиса.
Проблема 2: Неограниченное потребление ресурсов
Контейнеры без лимитов ресурсов — как автомобили без тормозов. Один контейнер с утечкой памяти может потребить всю память хоста и положить все сервисы.
Проблема 3: Чёрная дыра логов
Драйвер логов json-file по умолчанию не выполняет ротацию. После 3 месяцев работы /var/lib/docker заполняет диск, и все сервисы падают.
Проблема 4: Утечка конфиденциальных данных
Пароли от БД в блоках environment в открытом виде, файлы .env закоммичены в Git, API-ключи захардкожены в образах — это бомбы замедленного действия для продакшн-инцидентов.
Проблема 5: Обновления означают простой
docker compose up -d по умолчанию останавливает старые контейнеры перед запуском новых, делая сервис недоступным во время обновлений. Для сервисов 24/7 это неприемлемо.
7 Продакшн-паттернов
Паттерн 1: Health checks и упорядочивание зависимостей
Проблема: depends_on управляет только порядком запуска, не гарантирует готовность сервиса.
Решение: Использовать healthcheck + depends_on.condition: service_healthy.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
app:
image: myapp:latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
Ключевые параметры:
interval: Интервал проверки, для продакшена рекомендуется 5-10 секундtimeout: Таймаут одной проверки, рекомендуется 3-5 секундretries: Подряд идущие неудачи перед пометкой как unhealthystart_period: Льготный период после запуска контейнера, неудачи не засчитываются
Паттерн 2: Лимиты ресурсов и защита от OOM
Проблема: Контейнеры без ограничений конкурируют за ресурсы хоста. Один неконтролируемый контейнер может положить всю машину.
Решение: Использовать deploy.resources для установки лимитов и резерваций CPU и памяти.
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
worker:
image: myworker:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
memory: 512M
Лимиты vs Резервации:
limits: Жёсткий потолок, превышен → OOM Kill или CPU throttlingreservations: Мягкая гарантия, планировщик пытается выполнить, но не принуждает
Стратегия защиты от OOM:
services:
critical-service:
image: critical-app:latest
deploy:
resources:
limits:
memory: 1G
cap_add:
- SYS_PTRACE
На уровне хоста настройте vm.overcommit_memory и скорректируйте политики OOM:
# Проверить OOM Score контейнера
docker inspect --format='{{.State.OOMKilled}}' <container_id>
# Установить политику OOM хоста: не убивать критические процессы
echo -1000 > /proc/<pid>/oom_score_adj
Паттерн 3: Структурированное логирование и ротация логов
Проблема: Драйвер логов json-file по умолчанию не выполняет ротацию; диски заполняются со временем.
Решение: Настроить драйвер логов + политику ротации. Для продакшена рекомендуется драйвер local.
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
tag: "{{.Name}}/{{.ID}}"
nginx:
image: nginx:1.27-alpine
logging:
driver: json-file
options:
max-size: "50m"
max-file: "10"
tag: "nginx/{{.Name}}"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
Сравнение драйверов логов:
| Драйвер | Случай использования | Преимущества | Недостатки |
|---|---|---|---|
| local | Продакшн по умолчанию | Авто-ротация, сжатое хранение | Только локально |
| json-file | Нужны структурированные JSON-логи | Нативная поддержка Docker | Требуется ручная настройка ротации |
| syslog | Централизованный сбор логов | Можно отправлять удалённо | Сложная конфигурация |
| fluentd | Интеграция со стеком EFK | Гибкая маршрутизация логов | Требуется развёртывание Fluentd |
Структурированное логирование на уровне приложения:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV TZ=UTC
ENV LOG_FORMAT=json
CMD ["node", "server.js"]
const logger = {
info: (msg, meta = {}) => {
console.log(JSON.stringify({ level: 'info', msg, ts: new Date().toISOString(), ...meta }));
},
error: (msg, meta = {}) => {
console.error(JSON.stringify({ level: 'error', msg, ts: new Date().toISOString(), ...meta }));
}
};
Паттерн 4: Управление Secrets
Проблема: Конфиденциальные данные хранятся в открытом виде в compose-файлах или переменных окружения.
Решение: Docker Secrets + переменные окружения с суффиксом _FILE.
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
image: myapp:latest
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
API_KEY_FILE: /run/secrets/api_key
JWT_SECRET_FILE: /run/secrets/jwt_secret
secrets:
- api_key
- jwt_secret
Управление файлами secrets:
# Создать директорию secrets
mkdir -p secrets
chmod 700 secrets
# Записать файлы secrets
echo "my-super-secret-password-2026" > secrets/db_password.txt
echo "ak-live-xxxx-yyyy-zzzz" > secrets/api_key.txt
echo "jwt-hs256-secret-key-here" > secrets/jwt_secret.txt
# Установить права: только для чтения root
chmod 600 secrets/*.txt
.gitignore должен включать:
secrets/
*.secret
.env.production
.env.staging
Сравнение Docker Secrets и .env:
| Возможность | Docker Secrets | Файлы .env |
|---|---|---|
| Шифрованное хранение | Да (в режиме Swarm) | Нет |
| Файловые разрешения | Ограничены (/run/secrets/) | Зависит от файловой системы |
| Аудиторский след | Да | Нет |
| Межузловая синхронизация | Swarm синхронизирует автоматически | Ручное распространение |
| Случай использования | Swarm/однохостовый продакшн | Разработка |
Паттерн 5: Rolling-обновления без простоя
Проблема: docker compose up -d по умолчанию останавливает старые контейнеры перед запуском новых.
Решение: Использовать docker compose up --no-down + health checks + обратный прокси.
services:
app:
image: myapp:${APP_VERSION:-latest}
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 0
order: stop-first
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 3
start_period: 15s
labels:
- "com.toolsku.app=true"
ports:
- "3000-3001:3000"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 3s
retries: 3
Конфигурация обратного прокси Nginx:
upstream app_backend {
server app:3000;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_502 http_503;
}
location /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
Скрипт обновления без простоя:
#!/bin/bash
set -euo pipefail
NEW_IMAGE="myapp:v2.0.0"
echo "🚀 Запуск обновления без простоя до ${NEW_IMAGE}"
# 1. Загрузить новый образ
docker compose pull app
# 2. Запустить новые контейнеры (не останавливая старые)
docker compose up -d --no-deps --scale app=2 app
# 3. Дождаться готовности новых контейнеров
echo "⏳ Ожидание готовности новых контейнеров..."
sleep 15
# 4. Проверить здоровье нового контейнера
for i in $(seq 1 30); do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "✅ Новый контейнер здоров"
break
fi
if [ $i -eq 30 ]; then
echo "❌ Health check не прошёл, откат..."
docker compose up -d --no-deps --scale app=1 app
exit 1
fi
sleep 2
done
# 5. Уменьшить до 1 реплики
docker compose up -d --no-deps --scale app=1 app
echo "🎉 Обновление успешно завершено"
Паттерн 6: Стек мониторинга Prometheus + Grafana
Проблема: Работа в продакшне без мониторинга — как полёт вслепую — о проблемах вы узнаёте только из жалоб пользователей.
Решение: Развёртывание полного стека мониторинга Prometheus + Grafana.
services:
prometheus:
image: prom/prometheus:v2.52.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=5GB'
deploy:
resources:
limits:
memory: 512M
networks:
- monitoring
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
secrets:
- grafana_password
volumes:
- grafana_data:/var/lib/grafana
deploy:
resources:
limits:
memory: 256M
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:v1.8.0
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks:
- monitoring
secrets:
grafana_password:
file: ./secrets/grafana_password.txt
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Конфигурация Prometheus:
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
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: 'app'
static_configs:
- targets: ['app:3000']
metrics_path: /metrics
Правила алертов:
# monitoring/alert_rules.yml
groups:
- name: container_alerts
rules:
- alert: ContainerDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.instance }} is down"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes / (1024 * 1024) > 400
for: 5m
labels:
severity: warning
annotations:
summary: "Memory usage exceeds 400MB on {{ $labels.instance }}"
- alert: DiskSpaceLow
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space below 10% on {{ $labels.instance }}"
Паттерн 7: Мультиоконружение (dev/staging/prod)
Проблема: Конфигурации dev, staging и продакшна смешаны — изменение конфигурации одного окружения рискует затронуть другие.
Решение: Использовать docker-compose.override.yml + стратегию многофайлового наложения.
Структура директорий:
project/
├── docker-compose.yml # Базовая конфигурация
├── docker-compose.override.yml # Переопределение для dev (загружается автоматически)
├── docker-compose.staging.yml # Переопределение для staging
├── docker-compose.prod.yml # Переопределение для продакшна
├── .env # Переменные окружения по умолчанию
├── .env.staging # Переменные окружения staging
├── .env.prod # Переменные окружения продакшна
├── monitoring/
│ ├── prometheus.yml
│ └── alertmanager.yml
└── secrets/
├── db_password.txt
├── api_key.txt
└── grafana_password.txt
Базовая конфигурация docker-compose.yml:
services:
app:
image: myapp:${APP_VERSION:-latest}
environment:
NODE_ENV: ${NODE_ENV:-development}
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
postgres_data:
networks:
app-network:
driver: bridge
Переопределение для dev docker-compose.override.yml (загружается автоматически):
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229"
environment:
NODE_ENV: development
LOG_LEVEL: debug
deploy:
resources:
limits:
memory: 512M
command: node --inspect=0.0.0.0:9229 server.js
adminer:
image: adminer:latest
ports:
- "8080:8080"
networks:
- app-network
Переопределение для продакшна docker-compose.prod.yml:
services:
app:
image: myapp:${APP_VERSION}
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: info
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
postgres:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
memory: 1G
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
redis:
deploy:
resources:
limits:
memory: 512M
command: redis-server --appendonly yes --maxmemory 400mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
deploy:
resources:
limits:
memory: 128M
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
volumes:
redis_data:
Команды запуска:
# Разработка (автозагрузка override)
docker compose up -d
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml --env-file .env.staging up -d
# Продакшн
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d
5 Частых ошибок
Ошибка 1: depends_on не означает готовность сервиса
❌ Неправильно:
services:
app:
depends_on:
- postgres
# Контейнер postgres запущен, но БД может быть ещё не инициализирована
✅ Правильно:
services:
app:
depends_on:
postgres:
condition: service_healthy
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
Ошибка 2: Нет лимитов ресурсов
❌ Неправильно:
services:
app:
image: myapp:latest
# Без лимитов ресурсов — одна утечка памяти может потребить весь хост
✅ Правильно:
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
Ошибка 3: Нет ротации логов
❌ Неправильно:
services:
app:
image: myapp:latest
# Драйвер json-file по умолчанию, логи растут бесконечно
✅ Правильно:
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
Ошибка 4: Конфиденциальные данные в открытом виде
❌ Неправильно:
services:
postgres:
environment:
POSTGRES_PASSWORD: "my-secret-password-123"
# Пароль в открытом виде в compose-файле — катастрофа при коммите в Git
✅ Правильно:
services:
postgres:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Ошибка 5: Использование тега latest
❌ Неправильно:
services:
app:
image: myapp:latest
# Каждый pull может получить другой образ — невоспроизводимо
✅ Правильно:
services:
app:
image: myapp:2.1.0
# Или использовать переменную для управления версиями
image: myapp:${APP_VERSION:-2.1.0}
Справочник устранения ошибок
| Сообщение об ошибке | Причина | Решение |
|---|---|---|
OOMKilled |
Контейнер превысил лимит памяти | Увеличить лимит memory или оптимизировать использование памяти приложением |
Connection refused |
Зависимый сервис не готов | Добавить healthcheck + depends_on.condition |
no space left on device |
Логи/образы заполняют диск | Настроить ротацию logging + docker system prune |
Цикл restarting |
Приложение падает при запуске | Проверить docker logs <id> и проверить конфигурацию |
permission denied |
Проблема с правами доступа к файлу/директории | Проверить директиву user и права томов |
port is already allocated |
Конфликт портов | Изменить маппинг портов или остановить конфликтующий процесс |
Статус unhealthy |
Health check не проходит | Убедиться, что команда healthcheck корректна |
secret not found |
Файл secret отсутствует | Убедиться, что соответствующий файл существует в secrets/ |
Cannot connect to the Docker daemon |
Docker не запущен | systemctl start docker |
image pulling failed |
Ошибка загрузки образа | Проверить сеть/авторизацию в registry/написание имени образа |
Расширенная оптимизация
Docker Compose Watch для разработки
Compose Watch автоматически синхронизирует изменения файлов в контейнеры без пересборки образов:
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
- action: sync+restart
path: ./config
target: /app/config
# Запустить режим watch
docker compose watch
Сетевая изоляция и безопасность
services:
app:
networks:
- frontend
- backend
postgres:
networks:
- backend
# postgres не в сети frontend — внешний доступ заблокирован
nginx:
networks:
- frontend
ports:
- "80:80"
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
# internal:true отключает внешний доступ
Оптимизация образов с многоэтапной сборкой
# ---- Этап сборки ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Этап выполнения ----
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Сравнение инструментов оркестрации
| Возможность | Docker Compose | Kubernetes | Nomad | Docker Swarm |
|---|---|---|---|---|
| Сложность | Низкая | Высокая | Средняя | Низкая |
| Деплой на одном хосте | ✅ Отлично | ❌ Избыточно | ⚠️ Приемлемо | ✅ Отлично |
| Мультихостовая оркестрация | ❌ Не поддерживается | ✅ Основная возможность | ✅ Основная возможность | ⚠️ Базово |
| Автомасштабирование | ❌ | ✅ HPA/VPA | ✅ | ⚠️ Вручную |
| Rolling updates | ⚠️ Требуются скрипты | ✅ Нативно | ✅ Нативно | ✅ Нативно |
| Service discovery | ⚠️ DNS | ✅ CoreDNS | ✅ Consul | ✅ DNS |
| Оркестрация хранилища | ❌ | ✅ CSI | ✅ CSI | ⚠️ Базово |
| Кривая обучения | Низкая | Высокая | Средняя | Низкая |
| Масштаб | 1-10 сервисов | 100+ сервисов | 50+ сервисов | 10-50 сервисов |
| Готовность к продакшну | ✅ Один хост | ✅ Крупный масштаб | ✅ Средний-крупный масштаб | ⚠️ Сокращающееся сообщество |
Рекомендация: Используйте Docker Compose для однохостового/маломасштабного продакшна, Kubernetes — для крупного масштаба, Nomad — если вы в экосистеме HashiCorp. Docker Swarm всё больше маргинализируется — не рекомендуется для новых проектов.
Итоги
Продакшн-деплой Docker Compose — это не просто
docker compose up -d. Health checks гарантируют реальную готовность сервисов, лимиты ресурсов предотвращают OOM-лавины, ротация логов избегает исчерпания диска, Secrets защищают конфиденциальные данные, обновления без простоя гарантируют доступность 24/7, стеки мониторинга устраняют слепые зоны, а мультиоконружение разделяет dev/staging/prod. Освойте эти 7 стратегий, и Docker Compose вполне способен на продакшн-деплои малого и среднего масштаба.
Рекомендуемые инструменты
- Форматирование JSON - Форматировать JSON-конфигурации, связанные с Docker Compose YAML
- Кодирование Base64 - Кодировать Secrets и конфиденциальные данные конфигурации
- Калькулятор хешей - Генерировать контрольные суммы файлов конфигурации для обеспечения согласованности деплоев
Попробуйте эти локальные браузерные инструменты — регистрация не требуется →