Docker Composeプロダクションデプロイ実践:ヘルスチェックからゼロダウンタイム更新まで7つの重要戦略

DevOps

「私の環境では動くのに」——プロダクション環境のコンテナの墓場

すべての開発者の口癖:「私の環境では動くのに」。しかし、コンテナがプロダクション環境にデプロイされると、本当の悪夢が始まります:

  • コンテナが静かにOOM Killedされ、ログには Out of memory の1行だけ
  • データベースがまだ起動していないのに、アプリコンテナが Connection refused を連発
  • 深夜3時にコンテナがクラッシュ、再起動ポリシーなしで朝までサービス停止
  • ログファイルがディスクを圧迫、docker logs が数十GBの非構造化テキストを出力
  • プロダクション設定が docker-compose.yml に平文で書かれ、データベースパスワードが露出

プロダクション環境でまだ docker compose up -d を一発で実行しているなら、この記事はあなたのためです。

コア概念クイックリファレンス

概念 役割 プロダクション環境の主要設定
Health Check コンテナが本当に利用可能か検出 healthcheck + depends_on.condition
Resource Limits CPU/メモリを制限、リソース独占を防止 deploy.resources.limits
Restart Policy 異常終了後の自動再起動 deploy.restart_policy
Secrets 機密情報の暗号化保存 secrets + Docker Secret
Logging Driver 構造化ログ + ログローテーション logging.driver + logging.options
Profiles 環境ごとの選択的サービス起動 profiles
Watch ファイル変更をコンテナに自動同期 watch (Compose Watch)

プロダクション環境の5つの課題

課題1:コンテナ起動順序の制御不能

データベースがまだ初期化中に、アプリコンテナが接続を試みて起動に失敗。depends_on は起動順序のみを保証し、サービスの準備完了は保証しません。

課題2:リソースの無限膨張

リソース制限のないコンテナはブレーキのない車のようなもの。1つのメモリリークコンテナがホスト全体のメモリを消費し、すべてのサービスをダウンさせます。

課題3:ログのブラックホール

デフォルトの json-file ログドライバはローテーションしません。3ヶ月運用後、/var/lib/docker がディスクを埋め尽くし、全サービスがクラッシュします。

課題4:機密情報の漏洩

environment にデータベースパスワードを平文で記述、.env ファイルをGitリポジトリにコミット、イメージにAPI Keyをハードコード——これらはすべてプロダクション事故の時限爆弾です。

課題5:更新即ダウンタイム

docker compose up -d はデフォルトで古いコンテナを停止してから新しいコンテナを起動するため、更新中はサービスが利用不可になります。24時間365日稼働のサービスには許容できません。

7つの実践パターン

パターン1:ヘルスチェックと依存関係の順序付け

問題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:連続失敗回数、超過でunhealthyにマーク
  • start_period:コンテナ起動後の猶予期間、この間の失敗はretriesに含まない

パターン2:リソース制限とOOM保護

問題:制限のないコンテナはホストリソースを奪い合い、1つの暴走コンテナがマシン全体をダウンさせます。

解決策deploy.resources でCPUとメモリのlimitsとreservationsを設定。

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

limits vs reservations

  • limits:ハード上限、超過するとOOM KillまたはCPU制限
  • reservations:ソフト保証、スケジューラは満たそうとするが強制しない

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ポリシー設定:重要プロセスをkillしない
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=Asia/Tokyo
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

# secretファイルの書き込み
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 vs .env 比較

特徴 Docker Secrets .envファイル
暗号化保存 あり(Swarmモード) なし
ファイル権限 制限あり(/run/secrets/) ファイルシステム権限に依存
監査トレース あり なし
クロスノード同期 Swarmが自動同期 手動配布が必要
適用ケース Swarm/単一ホストプロダクション 開発環境

パターン5:ゼロダウンタイムローリングアップデート

問題docker compose up -d はデフォルトで古いコンテナを停止してから新しいコンテナを起動する。

解決策docker compose up --no-down + ヘルスチェック + リバースプロキシ。

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 "🚀 Starting zero-downtime update to ${NEW_IMAGE}"

# 1. 新しいイメージをプル
docker compose pull app

# 2. 新しいコンテナを起動(古いコンテナは停止しない)
docker compose up -d --no-deps --scale app=2 app

# 3. 新しいコンテナがヘルシーになるまで待機
echo "⏳ Waiting for new containers to be healthy..."
sleep 15

# 4. 新しいコンテナのヘルスチェック検証
for i in $(seq 1 30); do
  if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
    echo "✅ New container is healthy"
    break
  fi
  if [ $i -eq 30 ]; then
    echo "❌ Health check failed, rolling back..."
    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 "🎉 Update completed successfully"

パターン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)

問題:開発、ステージング、プロダクションの設定が混在し、ある環境の設定変更が他の環境に影響するリスクがある。

解決策docker-compose.override.yml + マルチファイルオーバーレイ戦略。

ディレクトリ構造

project/
├── docker-compose.yml              # ベース設定
├── docker-compose.override.yml     # 開発環境オーバーレイ(自動ロード)
├── docker-compose.staging.yml      # ステージング環境オーバーレイ
├── docker-compose.prod.yml         # プロダクション環境オーバーレイ
├── .env                            # デフォルト環境変数
├── .env.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

開発環境オーバーレイ 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

# ステージング環境
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コンテナは起動したが、DBの初期化が完了していない可能性

正しい書き方

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
    # リソース制限なし — 1つのメモリリークでホスト全体を消費

正しい書き方

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
    # プルのたびに異なるイメージの可能性 — 再現不可能

正しい書き方

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 ディレクティブとvolume権限を確認
port is already allocated ポート競合 ポートマッピングを変更するか、競合プロセスを停止
unhealthy ステータス ヘルスチェック失敗 healthcheck コマンドが正しいか確認
secret not found Secretファイルが存在しない secrets/ ディレクトリに対応ファイルが存在するか確認
Cannot connect to the Docker daemon Dockerが未実行 systemctl start docker
image pulling failed イメージプル失敗 ネットワーク/レジストリ認証/イメージ名スペルを確認

高度な最適化

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 ⚠️ 手動
ローリングアップデート ⚠️ スクリプト必要 ✅ ネイティブ ✅ ネイティブ ✅ ネイティブ
サービスディスカバリ ⚠️ DNS ✅ CoreDNS ✅ Consul ✅ DNS
ストレージオーケストレーション ✅ CSI ✅ CSI ⚠️ 基本
学習曲線
適用スケール 1-10サービス 100+サービス 50+サービス 10-50サービス
プロダクション対応 ✅ 単一ホスト ✅ 大規模 ✅ 中〜大規模 ⚠️ コミュニティ低下

選定の推奨:単一ホスト/小規模プロダクション環境にはDocker Compose、中〜大規模にはKubernetes、HashiCorpエコシステムならNomad。Docker Swarmは徐々にマイナー化しているため、新規プロジェクトには推奨しません。

まとめ

Docker Composeのプロダクションデプロイは、単なる docker compose up -d ではありません。ヘルスチェックでサービスの準備完了を保証し、リソース制限でOOM雪崩を防ぎ、ログローテーションでディスク枯渇を回避し、Secretsで機密情報を保護し、ゼロダウンタイム更新で24時間稼働を保障し、モニタリングスタックで盲目飛行を脱し、マルチ環境設定でdev/staging/prodを適切に分離します。この7つの戦略をマスターすれば、Docker Composeは中小規模のプロダクションデプロイに十分対応できます。

おすすめツール

ブラウザローカルツールを無料で試す →

#Docker#Docker Compose#生产部署#容器编排#2026#DevOps