15 проектов на одном сервере: Docker Compose в production для digital-агентства

Задача клиента

В январе 2026 года к нам в АйТи Фреш обратилось digital-агентство из Казани, специализирующееся на разработке веб-приложений для малого и среднего бизнеса. У агентства — 15 активных клиентских проектов, которые размещались на одном мощном выделенном сервере (64 ГБ RAM, 16 CPU, NVMe SSD).

Проблемы, с которыми столкнулся клиент:

  • Конфликты зависимостей — разные проекты требовали разных версий Node.js, PHP, Python и библиотек, что приводило к сложным конфигурациям с nvm, pyenv и т.д.
  • Отсутствие изоляции — падение одного проекта (утечка памяти, бесконечный цикл) влияло на все остальные
  • Сложный деплой — обновление каждого проекта требовало ручной работы, не было единого процесса
  • Нет мониторинга — о проблемах узнавали от клиентов, а не из алертов
  • Безопасность — все проекты работали в общей сети, компрометация одного могла привести к утечке данных другого

Наши инженеры предложили контейнеризировать все 15 проектов с помощью Docker Compose, обеспечив полную изоляцию, автоматический мониторинг, resource limits и CI/CD пайплайн для каждого проекта.

Почему Docker Compose, а не Kubernetes

Docker Compose — инструмент для определения и запуска многоконтейнерных приложений. Хотя для крупных кластеров существует Kubernetes, Docker Compose остаётся отличным выбором для малых и средних проектов — до 10–15 сервисов на одном или нескольких серверах. Для digital-агентства с 15 проектами на одном сервере Compose — идеальный баланс между функциональностью и простотой.

Критерии выбора, которые мы обсудили с клиентом

  • Docker Compose — 1–3 сервера, до 15 сервисов, небольшая команда, быстрый деплой, простота
  • Docker Swarm — 3–10 серверов, встроенная оркестрация, совместим с Compose-файлами
  • Kubernetes — 10+ серверов, сотни сервисов, автоскейлинг, продвинутый networking, большая команда

Мы рекомендовали начать с Docker Compose. Мигрировать на Kubernetes имеет смысл, когда появляется потребность в автоскейлинге, multi-node deployment или когда количество сервисов превышает возможности одного сервера.

Версия Compose и совместимость

Начиная с Docker Compose V2, используйте команду docker compose (без дефиса). V2 встроен в Docker CLI и не требует отдельной установки:

# Проверяем версию
docker compose version
# Docker Compose version v2.27.0

# Если V2 не установлен
sudo apt install -y docker-compose-plugin

# Или standalone
sudo curl -SL 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

Стандартная структура проекта, которую мы разработали

Мы создали единый шаблон структуры для всех 15 проектов, чтобы любой разработчик агентства мог легко разобраться в любом проекте.

Структура директорий

project/
├── docker-compose.yml          # Основной файл
├── docker-compose.override.yml # Переопределения для dev (автоматически мёржится)
├── docker-compose.prod.yml     # Production-специфичные настройки
├── .env                        # Переменные окружения (НЕ коммитить!)
├── .env.example                # Шаблон переменных
├── .dockerignore               # Исключения для build context
├── Makefile                    # Удобные команды
├── services/
│   ├── app/
│   │   ├── Dockerfile
│   │   ├── Dockerfile.prod     # Multi-stage production build
│   │   └── ...
│   ├── nginx/
│   │   ├── nginx.conf
│   │   ├── conf.d/
│   │   └── ssl/
│   └── postgres/
│       └── init/
│           └── 01-init.sql
├── monitoring/
│   ├── prometheus/
│   │   └── prometheus.yml
│   └── grafana/
│       └── dashboards/
└── scripts/
    ├── backup.sh
    └── deploy.sh

Базовый docker-compose.yml для каждого проекта

# docker-compose.yml

services:
  app:
    build:
      context: ./services/app
      dockerfile: Dockerfile.prod
      args:
        - APP_VERSION=${APP_VERSION:-latest}
    image: registry.company.local/app:${APP_VERSION:-latest}
    container_name: app
    restart: unless-stopped
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 256M
    networks:
      - frontend
      - backend
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"
        tag: "{{.Name}}"

  nginx:
    image: nginx:1.27-alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./services/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./services/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./services/nginx/ssl:/etc/nginx/ssl:ro
      - static-files:/var/www/static:ro
    depends_on:
      app:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 256M
    networks:
      - frontend
    logging:
      driver: json-file
      options:
        max-size: "100m"
        max-file: "10"

  postgres:
    image: postgres:16-alpine
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=ru_RU.UTF-8"
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./services/postgres/init:/docker-entrypoint-initdb.d:ro
      - ./backups/postgres:/backups
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
    shm_size: '256m'
    networks:
      - backend
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 768M
    networks:
      - backend

volumes:
  postgres-data:
    driver: local
  redis-data:
    driver: local
  static-files:
    driver: local

networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
  backend:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 172.20.1.0/24

Сетевая изоляция: ключ к безопасности 15 проектов

Главное требование клиента — чтобы компрометация одного проекта не затронула остальные 14. Мы реализовали многоуровневую сетевую изоляцию.

Принципы сетевой изоляции, которые мы реализовали

Для каждого проекта мы создали минимум две изолированные сети:

  • frontend — для сервисов, которым нужен доступ извне (nginx, load balancer)
  • backend — для внутренних сервисов (БД, кеш, очереди). Обязательно с параметром internal: true, который запрещает исходящий трафик в интернет
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # Контейнеры в этой сети не имеют доступа в интернет
  monitoring:
    driver: bridge
    internal: true

Nginx находится в сети frontend и backend (proxy_pass к приложению). Приложение — в frontend и backend. PostgreSQL и Redis — только в backend. Это предотвращает прямой доступ к БД из интернета даже в случае компрометации nginx. Каждый проект работает в своей изолированной сетевой среде.

DNS и service discovery между контейнерами

Docker Compose автоматически создаёт DNS-записи для каждого сервиса. Внутри проекта обращайтесь к сервисам по имени:

# В приложении
DATABASE_URL=postgresql://user:pass@postgres:5432/db
REDIS_URL=redis://:pass@redis:6379/0

# В nginx.conf
upstream app {
    server app:3000;
}

# При масштабировании (несколько реплик)
# Docker автоматически балансирует по round-robin
# docker compose up -d --scale app=3

Volumes, secrets и безопасное хранение данных

Управление данными и секретами для 15 проектов требует особого внимания. Мы разработали единый подход.

Работа с volumes

# Named volumes — для persistent данных
volumes:
  postgres-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /data/postgres   # Конкретный путь на хосте

  # Backup volume
  backups:
    driver: local
    driver_opts:
      type: nfs
      o: addr=10.0.1.100,rw
      device: ":/exports/backups"

# В сервисах:
services:
  postgres:
    volumes:
      # Named volume для данных
      - postgres-data:/var/lib/postgresql/data
      # Bind mount для конфигов (read-only)
      - ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
      # tmpfs для временных файлов
    tmpfs:
      - /tmp:size=100M
      - /run

Правила, которые мы установили для агентства:

  • Используйте named volumes для данных, bind mounts — только для конфигов (с флагом :ro)
  • Никогда не храните данные в anonymous volumes
  • Для БД всегда указывайте конкретный путь через driver_opts

Управление секретами

# .env файл (НЕ коммитить в git!)
DB_USER=app_user
DB_PASSWORD=SuperSecretP@ss2026
DB_NAME=app_production
REDIS_PASSWORD=RedisS3cret!
APP_SECRET_KEY=a1b2c3d4e5f6...

# .env.example (коммитим как шаблон)
DB_USER=
DB_PASSWORD=
DB_NAME=
REDIS_PASSWORD=
APP_SECRET_KEY=

# .gitignore
.env
*.key
*.pem

Для повышенной безопасности мы также подготовили вариант с Docker secrets (для перехода на Swarm mode):

# docker-compose.yml с secrets
services:
  app:
    secrets:
      - db_password
      - app_secret_key
    environment:
      - DB_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
  app_secret_key:
    file: ./secrets/app_secret_key.txt

Multi-stage Dockerfile для production

# services/app/Dockerfile.prod

# === Stage 1: Build ===
FROM node:20-alpine AS builder

WORKDIR /app

# Кешируем зависимости
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

COPY . .
RUN npm run build

# === Stage 2: Production ===
FROM node:20-alpine AS production

# Безопасность: не запускаем от root
RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -D appuser

WORKDIR /app

# Копируем только production-артефакты
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./

USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/main.js"]

Healthchecks и resource limits для стабильности

В production каждый контейнер обязан иметь healthcheck и resource limits. Без них Docker не сможет корректно перезапустить зависший сервис или предотвратить OOM-kill, который повалит соседние проекты.

Healthchecks для разных типов сервисов

# Веб-приложение
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 60s  # Время на запуск приложения

# PostgreSQL
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

# Redis
healthcheck:
  test: ["CMD", "redis-cli", "-a", "$${REDIS_PASSWORD}", "ping"]
  interval: 10s
  timeout: 5s
  retries: 3

# Nginx
healthcheck:
  test: ["CMD-SHELL", "curl -f http://localhost/nginx-health || exit 1"]
  interval: 30s
  timeout: 5s
  retries: 3

# RabbitMQ
healthcheck:
  test: ["CMD", "rabbitmq-diagnostics", "check_running"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 60s

# Elasticsearch
healthcheck:
  test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -vq '\"status\":\"red\"'"]
  interval: 30s
  timeout: 10s
  retries: 5
  start_period: 120s

Resource limits — как мы распределили ресурсы между 15 проектами

deploy:
  resources:
    limits:
      cpus: '2.0'      # Максимум CPU (жёсткий лимит)
      memory: 1G       # Максимум RAM (OOM-kill при превышении)
    reservations:
      cpus: '0.25'     # Гарантированный минимум CPU
      memory: 256M     # Гарантированный минимум RAM

Вот таблица лимитов, которую мы составили для типичного стека каждого проекта:

СервисCPU limitMemory limitMemory reservation
Nginx1.0256M64M
Node.js App2.01G256M
PostgreSQL2.02G512M
Redis1.0512M128M
Elasticsearch2.02G1G

Важно: не устанавливайте memory limit равным reservation. Оставляйте запас для пиковых нагрузок. Для PostgreSQL параметр shm_size должен быть выставлен отдельно.

Единый мониторинг: Prometheus + Grafana + Loki

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

Docker Compose для стека мониторинга

# monitoring/docker-compose.monitoring.yml

services:
  prometheus:
    image: prom/prometheus:v2.52.0
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus/alerts:/etc/prometheus/alerts:ro
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-lifecycle'
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:11.0.0
    container_name: grafana
    restart: unless-stopped
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
      - GF_SERVER_ROOT_URL=https://grafana.company.local
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
      - ./grafana/datasources:/etc/grafana/provisioning/datasources:ro
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
    networks:
      - monitoring
      - frontend

  loki:
    image: grafana/loki:3.0.0
    container_name: loki
    restart: unless-stopped
    volumes:
      - loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
    networks:
      - monitoring

  promtail:
    image: grafana/promtail:3.0.0
    container_name: promtail
    restart: unless-stopped
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail/config.yml:/etc/promtail/config.yml:ro
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:v1.8.0
    container_name: node-exporter
    restart: unless-stopped
    command:
      - '--path.rootfs=/host'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    volumes:
      - /:/host:ro,rslave
    pid: host
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 128M
    networks:
      - monitoring

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    container_name: cadvisor
    restart: unless-stopped
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    networks:
      - monitoring

volumes:
  prometheus-data:
  grafana-data:
  loki-data:

networks:
  monitoring:
    driver: bridge
    internal: true
  frontend:
    external: true

Конфигурация Prometheus

# monitoring/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

rule_files:
  - '/etc/prometheus/alerts/*.yml'

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

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

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

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

CI/CD интеграция и автоматический деплой

Для каждого из 15 проектов мы настроили автоматический деплой через GitLab CI/CD, чтобы разработчики могли обновлять свои проекты одним кликом.

Makefile для единообразного управления

# Makefile

.PHONY: help up down restart logs build deploy backup

PROJECT_NAME := myapp
COMPOSE := docker compose -p $(PROJECT_NAME)

help: ## Показать справку
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

up: ## Запустить все сервисы
	$(COMPOSE) up -d

down: ## Остановить все сервисы
	$(COMPOSE) down

restart: ## Перезапустить все сервисы
	$(COMPOSE) restart

logs: ## Показать логи (make logs s=app)
	$(COMPOSE) logs -f $(s)

build: ## Собрать образы
	$(COMPOSE) build --no-cache

deploy: ## Production деплой
	git pull
	$(COMPOSE) pull
	$(COMPOSE) build
	$(COMPOSE) up -d --remove-orphans
	$(COMPOSE) exec app npm run migrate
	echo "Deploy completed at $$(date)"

backup: ## Создать бэкап БД
	$(COMPOSE) exec postgres pg_dump -U $${DB_USER} -Fc $${DB_NAME} > backups/db_$$(date +%Y%m%d_%H%M%S).dump

ps: ## Статус сервисов
	$(COMPOSE) ps

shell: ## Зайти в контейнер (make shell s=app)
	$(COMPOSE) exec $(s) sh

GitLab CI/CD pipeline

# .gitlab-ci.yml

stages:
  - test
  - build
  - deploy

variables:
  DOCKER_TLS_CERTDIR: "/certs"
  REGISTRY: registry.company.local
  IMAGE_NAME: $REGISTRY/myapp

test:
  stage: test
  image: node:20-alpine
  script:
    - npm ci
    - npm run lint
    - npm run test
  only:
    - merge_requests
    - main

build:
  stage: build
  image: docker:27
  services:
    - docker:27-dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $REGISTRY
    - docker build -f services/app/Dockerfile.prod -t $IMAGE_NAME:$CI_COMMIT_SHA -t $IMAGE_NAME:latest .
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA
    - docker push $IMAGE_NAME:latest
  only:
    - main

deploy:
  stage: deploy
  script:
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - ssh -o StrictHostKeyChecking=no deploy@production-server "cd /opt/myapp && APP_VERSION=$CI_COMMIT_SHA make deploy"
  environment:
    name: production
    url: https://app.company.local
  only:
    - main
  when: manual

Zero-downtime deploy

#!/bin/bash
# scripts/deploy.sh — Zero-downtime деплой

set -euo pipefail

PROJECT_DIR="/opt/myapp"
cd "$PROJECT_DIR"

echo "[$(date)] Начало деплоя"

# Обновляем код
git pull origin main

# Подтягиваем новые образы
docker compose pull

# Пересобираем приложение
docker compose build app

# Zero-downtime: поднимаем новый контейнер, затем убиваем старый
# 1. Масштабируем до 2 реплик
docker compose up -d --no-deps --scale app=2 app

# 2. Ждём, пока новый контейнер пройдёт healthcheck
sleep 30

# 3. Убиваем старый контейнер
docker compose up -d --no-deps --scale app=1 app

# Применяем миграции
docker compose exec app npm run migrate

# Удаляем неиспользуемые образы
docker image prune -f

echo "[$(date)] Деплой завершён успешно"

Результаты внедрения

Миграция всех 15 проектов в Docker Compose заняла 2 недели: одна неделя на создание шаблонов и контейнеризацию первых 5 проектов, вторая — на оставшиеся 10 и настройку мониторинга. Вот измеримые результаты:

  • 15 проектов полностью изолированы — каждый работает в своих контейнерах с отдельными сетями, volumes и resource limits
  • Ноль инцидентов «положил соседа» — утечка памяти в одном проекте не влияет на остальные благодаря memory limits
  • Время деплоя сократилось с 30 минут до 2 минут — благодаря CI/CD и Makefile
  • Zero-downtime deploy — обновление проекта проходит без прерывания обслуживания клиентов
  • Единый мониторинг всех проектов — Grafana-дашборд показывает CPU, RAM, сеть и диск каждого контейнера
  • Централизованные логи через Loki — поиск ошибок по всем 15 проектам из одного интерфейса
  • Стандартизация — любой разработчик агентства может работать с любым проектом, зная единую структуру
  • Экономия 40% дискового пространства — multi-stage builds и общие base images значительно уменьшили размер образов

Руководство агентства отмечает, что контейнеризация не только решила технические проблемы, но и ускорила онбординг новых разработчиков: вместо настройки локального окружения достаточно выполнить docker compose up.

Часто задаваемые вопросы

Да, Docker Compose отлично подходит для production на одном или нескольких серверах с небольшим количеством сервисов (до 10-15). Ключевые требования: используйте healthchecks, resource limits, именованные volumes для данных, сетевую изоляцию, правильное логирование и мониторинг. Для горизонтального масштабирования на несколько серверов рассмотрите Docker Swarm или Kubernetes.

Используйте файл .env (не коммитьте его в git), Docker secrets (в Swarm mode), или внешние менеджеры секретов (HashiCorp Vault, AWS Secrets Manager). Никогда не прописывайте пароли и ключи напрямую в docker-compose.yml. Для CI/CD используйте переменные окружения pipeline (GitLab CI Variables, GitHub Secrets).

Настройте logging driver в docker-compose.yml: json-file с ограничением размера (max-size: 50m, max-file: 5) для предотвращения переполнения диска. Для централизованного сбора используйте стек Loki + Promtail + Grafana или ELK (Elasticsearch + Logstash + Kibana). Promtail автоматически собирает логи из /var/lib/docker/containers и отправляет в Loki с метками контейнера.

Для баз данных используйте нативные инструменты (pg_dump, mysqldump) через docker compose exec. Для файловых volumes — остановите контейнер и скопируйте данные: docker run --rm -v myvolume:/data -v /backup:/backup alpine tar czf /backup/volume.tar.gz /data. Автоматизируйте через cron и тестируйте восстановление регулярно.

Используйте стратегию blue-green: масштабируйте сервис до 2 реплик (--scale app=2), дождитесь прохождения healthcheck нового контейнера, затем уменьшите до 1 реплики. Nginx автоматически направит трафик на здоровый контейнер. Для более сложных сценариев используйте Docker Swarm с настройкой update_config: { order: start-first }.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#Docker Compose production#docker-compose best practices#Docker Compose примеры#Docker healthcheck#Docker resource limits#Docker secrets#Docker networking#Docker Compose мониторинг