1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker Composeってなんだ?〜複数コンテナを「1コマンド」で操る技術を完全理解〜

1
Last updated at Posted at 2026-03-01

この記事の対象読者

  • Dockerの基本(イメージ、コンテナ、ボリューム)は理解している方
  • docker runを何度も叩いて「これ毎回やるの面倒...」と思い始めた方
  • AI開発でGPU付きの複数サービス(API + DB + 推論エンジン)をまとめて管理したい方

この記事で得られること

  • Composeの本質: 「なぜdocker runの連打ではダメなのか」が腹落ちする
  • compose.yamlの読み書き: サービス、ネットワーク、ボリューム、依存関係の定義を完全理解
  • 実践力: GPU対応AIスタック、Web+DB+キャッシュ構成、開発ワークフロー自動化まで身につく

この記事で扱わないこと

  • Kubernetes(次回記事で扱います)
  • Docker Swarmによるマルチノードオーケストレーション
  • Compose v5 Go SDKのプログラミング詳細

1. Docker Composeとの出会い

docker runを5個連続で叩いた日、私は悟った。「これは人間がやる仕事じゃない」と。

事の発端は、Ollama(ローカルLLM推論サーバー)+ Open WebUI + PostgreSQL + Redis + Nginx をRTX 5090環境で動かそうとした時のことです。

# 1つ目: ネットワーク作成
docker network create ai-stack

# 2つ目: PostgreSQL
docker run -d --name postgres --network ai-stack -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres:16

# 3つ目: Redis
docker run -d --name redis --network ai-stack redis:7-alpine

# 4つ目: Ollama(GPU付き)
docker run -d --name ollama --network ai-stack --gpus all -v ollama-models:/root/.ollama ollama/ollama

# 5つ目: Open WebUI
docker run -d --name webui --network ai-stack -p 8080:8080 -e OLLAMA_BASE_URL=http://ollama:11434 ghcr.io/open-webui/open-webui:main

5つのコンテナ、5つのdocker run。さらにこれを止めるにはdocker stopを5回、削除するにはdocker rmを5回。環境を別のマシンに再現するには、このコマンド群をどこかにメモしておく必要がある。そしてメモは必ず古くなる。

Docker Composeは、この「複数コンテナの定義と管理」を1つのYAMLファイルにまとめ、docker compose upの一撃で全てを起動する仕組みです。

料理に例えるなら、docker runの連打は「レシピを口頭で伝える」こと。Docker Composeは「レシピブック」を作ること。誰が見ても同じ料理が再現できる。そしてレシピブックはバージョン管理できる。

ここまでで、Docker Composeの存在意義がなんとなく掴めたでしょうか。次は、この記事で使う用語を整理しておきましょう。


2. 前提知識の確認

本題に入る前に、この記事で頻出する用語を確認します。

2.1 サービス(Service)とは

Composeにおけるサービスは、Dockerコンテナの「定義」です。1つのサービスが1つのコンテナに対応します(スケールすれば複数)。services:ブロック内に記述します。

2.2 プロジェクト(Project)とは

Composeのプロジェクトは、1つのcompose.yamlで定義されたサービス群の総称です。デフォルトではディレクトリ名がプロジェクト名になります。ネットワークやボリュームはプロジェクト単位で隔離されます。

2.3 Compose Specification とは

Docker Composeの設定ファイル形式の仕様です。かつては version: '3.8' のようにバージョンを明記していましたが、2026年現在のCompose v5ではversionフィールドは不要(非推奨)です。仕様はローリングリリースで更新されます。

2.4 プロファイル(Profile)とは

サービスをグループ化する仕組みです。たとえば「開発時だけ使うデバッグツール」や「テスト時だけ使うDBシード」をプロファイルで分けることで、本番環境と開発環境をひとつのcompose.yamlで管理できます。

これらの用語が押さえられたら、Docker Composeの歴史と背景を見ていきましょう。


3. Docker Composeが生まれた背景

3.1 「1コンテナ1プロセス」の原則と現実のギャップ

Dockerには「1コンテナに1つのプロセスを割り当てる」というベストプラクティスがあります。Webアプリ、データベース、キャッシュ、リバースプロキシ...それぞれが独立したコンテナ。

しかし現実のアプリケーションは複数のサービスが連携して動きます。問題は:

課題 docker runの連打では...
起動順序 DBが先に起動していないとアプリがクラッシュする
ネットワーク コンテナ間通信のためにネットワークを手動で作る必要がある
再現性 コマンド群をメモしないと別環境で再現できない
一括操作 停止・削除・再起動が個別に必要
設定の共有 環境変数の管理がバラバラ

3.2 Docker Composeの進化の歴史

2014年 ─ Fig(Figプロジェクト)としてスタート
    │     Orchard Laboratories がPython製ツールとして開発
    ▼
2014年 ─ Docker社がOrchardを買収、Docker Composeに改名(v1)
    │     コマンド: docker-compose(ハイフンあり)
    │     設定ファイル: docker-compose.yml
    │     ファイルフォーマット: version 1, 2.x, 3.x
    ▼
2020年 ─ Docker Compose V2 発表
    │     Go言語で完全リライト(v1はPython製)
    │     コマンド: docker compose(スペース、Dockerのサブコマンドに)
    │     Compose Specification を採用
    │     version フィールドが任意に
    ▼
2026年1月 ─ Docker Compose V5 リリース ← 今ここ
          Go SDK の公式提供
          Docker Desktop 4.56.0 に同梱
          ※ v3, v4 はレガシーファイルフォーマットとの混同回避で欠番

なぜv3とv4が欠番なのか?: docker-compose.yml の version: '3.8' というファイルフォーマット番号との混乱を避けるため。Compose CLIのバージョンをv5に飛ばして、明確に区別しました。

3.3 Compose V5 の注目ポイント(2026年現在)

機能 説明
Go SDK CLIを使わずにGoアプリからComposeを操作可能
Compose Watch ファイル変更を監視して自動sync/rebuild/restart
Docker Model Runner連携 compose.yamlでLLMモデルを直接定義可能
時間ベースpull_policy daily, weekly, every_Xh でイメージ更新を制御
プロバイダーサービス 外部システムやクラウドサービスを直接参照
Compose Bridge Compose定義をKubernetesマニフェストに変換(GA)

背景がわかったところで、基本的な仕組みを見ていきましょう。


4. 基本概念と仕組み

4.1 compose.yaml の構造

compose.yamlは大きく4つのトップレベル要素で構成されます。

# compose.yaml の全体構造

services:     # ← コンテナの定義(必須)
  web:
    image: nginx
  db:
    image: postgres

networks:     # ← ネットワークの定義(任意)
  frontend:
  backend:

volumes:      # ← 永続ボリュームの定義(任意)
  db-data:

configs:      # ← 設定ファイルの定義(任意)
secrets:      # ← 秘密情報の定義(任意)
┌─────────────────────────────────────────────┐
│             compose.yaml                     │
│                                              │
│  ┌─────────── services ───────────┐         │
│  │ web:          db:     cache:   │         │
│  │ ┌─────┐   ┌─────┐   ┌─────┐  │         │
│  │ │nginx│   │postgres│ │redis│  │         │
│  │ └──┬──┘   └──┬──┘   └──┬──┘  │         │
│  └────┼─────────┼─────────┼─────┘         │
│       │         │         │                 │
│  ┌────┴─────────┴─────────┴─────┐         │
│  │         networks              │         │
│  │  frontend ←→ backend          │         │
│  └──────────────┬────────────────┘         │
│                 │                            │
│  ┌──────────────┴────────────────┐         │
│  │         volumes               │         │
│  │  db-data   cache-data         │         │
│  └───────────────────────────────┘         │
│                                              │
│  docker compose up   → 全サービス起動        │
│  docker compose down → 全サービス停止+削除    │
└─────────────────────────────────────────────┘

4.2 サービス定義の主要フィールド

services:
  myapp:
    # --- イメージ指定(2つの方法) ---
    image: python:3.12-slim        # 既存イメージを使う場合
    build:                          # Dockerfileからビルドする場合
      context: .
      dockerfile: Dockerfile

    # --- ネットワーク ---
    ports:
      - "8080:8080"                # ホスト:コンテナ
    networks:
      - frontend

    # --- データ永続化 ---
    volumes:
      - ./src:/app/src             # バインドマウント(開発用)
      - app-data:/app/data         # 名前付きボリューム(永続化)

    # --- 環境変数 ---
    environment:
      - DB_HOST=db
      - DB_PORT=5432
    env_file:
      - .env                       # .envファイルから読み込み

    # --- 依存関係 ---
    depends_on:
      db:
        condition: service_healthy  # DBのヘルスチェック通過を待つ

    # --- リソース制限 ---
    deploy:
      resources:
        limits:
          memory: 2g
          cpus: "1.0"
        reservations:
          devices:                  # GPU予約
            - driver: nvidia
              count: all
              capabilities: [gpu]

    # --- ヘルスチェック ---
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

    # --- その他 ---
    restart: unless-stopped         # 異常終了時に自動再起動
    shm_size: "4g"                  # /dev/shm のサイズ

4.3 ネットワークの自動生成

Docker Composeの最も便利な特徴のひとつが、ネットワークの自動管理です。

# 明示的にnetworksを定義しなくても...
services:
  web:
    image: nginx
  db:
    image: postgres

この場合、Composeは自動的に<プロジェクト名>_defaultというbridgeネットワークを作成し、全サービスを接続します。そしてサービス名がそのままホスト名になります。

web コンテナ内から:
  $ ping db          → PostgreSQLに到達する
  $ curl web:80      → 自分自身にも名前で到達

DNS解決: サービス名 → コンテナのIPアドレス

明示的にネットワークを分離したい場合:

services:
  nginx:
    networks: [frontend, backend]   # 両方に接続
  api:
    networks: [backend]             # バックエンドのみ
  db:
    networks: [backend]             # バックエンドのみ

networks:
  frontend:                         # 外部向け
  backend:
    internal: true                  # 外部からアクセス不可

4.4 depends_on と起動順序制御

depends_onはサービスの起動順序を制御しますが、単純なdepends_onは「コンテナの起動」を待つだけで、「サービスの準備完了」は待ちません。

# ❌ BAD: DBコンテナが起動しただけでアプリが接続を試みる → 失敗
services:
  app:
    depends_on:
      - db

# ✅ GOOD: DBのヘルスチェックが通るまで待つ
services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10

基本概念が理解できたところで、実際にコードを書いて動かしてみましょう。


5. 実践:実際に使ってみよう

5.1 環境構築

この記事の実践パートは、以下の環境で動作確認しています。

項目 バージョン
OS Windows 11 Pro 24H2 + WSL2
ディストロ Ubuntu 24.04 LTS
Docker Engine 29.x
Docker Compose v5.1.0
GPU NVIDIA RTX 5090

5.2 環境別の設定ファイル

開発環境用(compose.yaml)

# compose.yaml - 開発環境用
# 起動: docker compose up -d
# 停止: docker compose down

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./src:/app/src           # ソースコードをバインドマウント(ホットリロード用)
    environment:
      - ENV=development
      - DEBUG=true
      - DB_HOST=db
      - DB_PORT=5432
      - DB_NAME=myapp_dev
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    restart: unless-stopped
    develop:                      # Compose Watch(V5機能)
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: requirements.txt

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=myapp_dev
      - POSTGRES_USER=dev
      - POSTGRES_PASSWORD=devpassword
    ports:
      - "5432:5432"              # 開発時はホストからDB直接接続可能に
    volumes:
      - db-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 初期化SQL
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev -d myapp_dev"]
      interval: 5s
      timeout: 3s
      retries: 10

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"              # 開発時はRedis CLIで直接接続可能に
    volumes:
      - cache-data:/data

volumes:
  db-data:
  cache-data:

本番環境用(compose.prod.yaml)

# compose.prod.yaml - 本番環境用
# 起動: docker compose -f compose.yaml -f compose.prod.yaml up -d

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
    ports:
      - "8000:8000"
    environment:
      - ENV=production
      - DEBUG=false
      - DB_HOST=db
      - DB_PORT=5432
      - DB_NAME=${DB_NAME}
      - REDIS_URL=redis://cache:6379
    env_file:
      - .env.prod               # 本番用シークレット
    volumes: []                  # バインドマウント無効化
    deploy:
      resources:
        limits:
          memory: 2g
          cpus: "2.0"
    restart: always
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "5"

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    ports: []                    # 本番ではDB外部公開しない
    volumes:
      - db-data:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 1g

  cache:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
    ports: []                    # 本番ではRedis外部公開しない

  nginx:                         # 本番のみリバースプロキシ追加
    image: nginx:1.27-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    restart: always

テスト/CI環境用(compose.test.yaml)

# compose.test.yaml - テスト/CI環境用
# 実行: docker compose -f compose.yaml -f compose.test.yaml run --rm test

services:
  app:
    environment:
      - ENV=test
      - DEBUG=false
      - DB_HOST=db
      - DB_NAME=myapp_test
    ports: []

  db:
    environment:
      - POSTGRES_DB=myapp_test
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=testpassword
    ports: []
    tmpfs:                       # テスト用DBはメモリ上で高速化
      - /var/lib/postgresql/data

  test:                          # テスト実行専用サービス
    build:
      context: .
      dockerfile: Dockerfile
    command: pytest tests/ -v --cov=src --cov-report=html
    environment:
      - ENV=test
      - DB_HOST=db
      - DB_NAME=myapp_test
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - ./tests:/app/tests
      - ./htmlcov:/app/htmlcov   # カバレッジレポート出力

5.3 Compose操作の実践スクリプト

#!/usr/bin/env bash
# compose_guide.sh - Docker Compose 操作ガイドスクリプト
# 実行方法: bash compose_guide.sh

set -euo pipefail

echo "=========================================="
echo " Docker Compose 実践ガイド"
echo "=========================================="

echo ""
echo "=== 1. 基本コマンド ==="
cat << 'EOF'
# 全サービスをバックグラウンドで起動
$ docker compose up -d

# 特定サービスだけ起動
$ docker compose up -d db cache

# ログをリアルタイム表示
$ docker compose logs -f

# 特定サービスのログだけ
$ docker compose logs -f app

# サービスの状態確認
$ docker compose ps

# 全サービス停止 + コンテナ削除
$ docker compose down

# ボリュームも含めて完全削除(データ消失注意)
$ docker compose down -v
EOF
echo ""

echo "=== 2. ビルド & 更新 ==="
cat << 'EOF'
# Dockerfileを変更後にリビルド
$ docker compose build

# キャッシュ無効でリビルド(依存関係が壊れた時)
$ docker compose build --no-cache

# リビルド + 起動を一発で
$ docker compose up -d --build

# 特定サービスだけリビルド
$ docker compose build app

# イメージの更新を確認してプル
$ docker compose pull
EOF
echo ""

echo "=== 3. 複数ファイルの合成 ==="
cat << 'EOF'
# 開発環境(デフォルト)
$ docker compose up -d

# 本番環境(compose.yamlを上書き)
$ docker compose -f compose.yaml -f compose.prod.yaml up -d

# テスト実行
$ docker compose -f compose.yaml -f compose.test.yaml run --rm test
EOF
echo ""

echo "=== 4. プロファイル ==="
cat << 'EOF'
# compose.yaml 内のプロファイル定義例:
# services:
#   debug-tools:
#     profiles: [debug]
#     image: busybox

# デフォルト起動(debug-toolsは起動しない)
$ docker compose up -d

# debugプロファイルも含めて起動
$ docker compose --profile debug up -d

# 有効なプロファイル一覧
$ docker compose config --profiles
EOF
echo ""

echo "=== 5. Compose Watch(開発ワークフロー) ==="
cat << 'EOF'
# ファイル変更を監視して自動反映
$ docker compose up --watch

# watchの動作:
#   sync    → ファイルをコンテナにコピー(ホットリロード向き)
#   rebuild → Dockerイメージを再ビルド(依存関係変更時)
#   sync+restart → ファイルコピー後にサービス再起動
EOF
echo ""

echo "=== 6. 便利なワンライナー ==="
cat << 'EOF'
# サービス内でコマンド実行
$ docker compose exec app bash
$ docker compose exec db psql -U dev -d myapp_dev

# 一時的なコンテナで実行(使い捨て)
$ docker compose run --rm app python manage.py migrate

# 全サービスのリソース使用量
$ docker compose top

# compose.yamlの設定を解決した結果を表示(デバッグ用)
$ docker compose config

# 特定サービスだけ再起動
$ docker compose restart app

# 特定サービスをスケール
$ docker compose up -d --scale worker=3
EOF

echo ""
echo "=========================================="
echo "✅ ガイド完了!"

5.4 実行結果

開発環境の一括起動:

$ docker compose up -d
[+] Running 4/4
 ✔ Network myproject_default  Created    0.1s
 ✔ Container myproject-db-1   Healthy    5.3s
 ✔ Container myproject-cache-1 Started   0.8s
 ✔ Container myproject-app-1   Started   5.5s

$ docker compose ps
NAME                  STATUS          PORTS
myproject-app-1       Up 10 seconds   0.0.0.0:8000->8000/tcp
myproject-cache-1     Up 15 seconds   0.0.0.0:6379->6379/tcp
myproject-db-1        Up 15 seconds   0.0.0.0:5432->5432/tcp

$ docker compose logs app --tail 5
myproject-app-1  | INFO:     Application startup complete.
myproject-app-1  | INFO:     Uvicorn running on http://0.0.0.0:8000
myproject-app-1  | INFO:     Connected to PostgreSQL at db:5432
myproject-app-1  | INFO:     Redis connection established at cache:6379
myproject-app-1  | INFO:     Environment: development (DEBUG=true)

5.5 よくあるエラーと対処法

エラー 原因 対処法
service "app" depends on "db" which is not healthy DBのヘルスチェックがタイムアウト healthcheckretriesintervalを増やす。start_periodを設定してDB初期化時間を確保
port is already allocated 同じポートが別のCompose/コンテナで使用中 docker psで確認。compose.yamlのポートマッピングを変更するか、競合コンテナを停止
no matching manifest for linux/amd64 イメージがプラットフォーム非対応 platform: linux/amd64を明示するか、対応イメージを選択
could not select device driver "nvidia" NVIDIA Container Toolkit未インストール NVIDIA Container Toolkitをインストール。nvidia-ctk runtime configure --runtime=dockerを実行後Dockerを再起動
bind mount source path does not exist バインドマウントのホスト側パスが存在しない マウント先のディレクトリを事前に作成。相対パスはcompose.yamlのあるディレクトリ基準
yaml: line X: did not find expected key compose.yamlのインデント崩れ YAMLはスペース2個のインデント。タブは不可。VS CodeのDocker拡張で自動検証

docker compose down -vは永続ボリュームを全削除します。DBデータ、キャッシュデータ、全て消えます。本番環境では-vフラグを絶対につけないでください。開発環境のリセット専用です。

5.6 環境診断スクリプト

#!/usr/bin/env python3
"""
Docker Compose環境診断スクリプト
実行方法: python3 check_compose_env.py
"""

import subprocess
import os
import sys
import json
from pathlib import Path


def run_cmd(cmd: str) -> str:
    """コマンドを実行して結果を返す"""
    try:
        result = subprocess.run(
            cmd, shell=True, capture_output=True, text=True, timeout=15
        )
        return result.stdout.strip()
    except (subprocess.TimeoutExpired, Exception):
        return ""


def check_compose_environment():
    """Docker Compose環境を包括的にチェック"""
    issues = []
    warnings = []

    print("=" * 55)
    print(" Docker Compose 環境診断レポート")
    print("=" * 55)

    # --- 1. Compose基本情報 ---
    print("\n🐳 Docker Compose基本情報:")
    compose_ver = run_cmd("docker compose version --short 2>/dev/null")
    if compose_ver:
        print(f"  Compose: v{compose_ver}")
        major = int(compose_ver.split('.')[0]) if compose_ver[0].isdigit() else 0
        if major < 2:
            issues.append("Compose v1(Python版)は非推奨です。v5へアップグレードしてください。")
    else:
        issues.append("Docker Composeがインストールされていません。")
        return

    # Docker Engine
    engine_ver = run_cmd("docker version --format '{{.Server.Version}}' 2>/dev/null")
    print(f"  Docker Engine: {engine_ver or '接続不可'}")

    # BuildKit
    buildkit = run_cmd("docker buildx version 2>/dev/null")
    print(f"  BuildKit: {'有効' if buildkit else '要確認'}")

    # --- 2. compose.yamlの検出 ---
    print("\n📄 Composeファイル検出:")
    compose_files = [
        "compose.yaml", "compose.yml",
        "docker-compose.yaml", "docker-compose.yml"
    ]
    found_files = [f for f in compose_files if Path(f).exists()]

    if found_files:
        for f in found_files:
            print(f"{f}")
        if len(found_files) > 1:
            warnings.append(
                f"複数のComposeファイルが存在します: {found_files}"
                "docker compose configで優先順位を確認してください。"
            )
    else:
        print("  ℹ️  カレントディレクトリにComposeファイルなし")

    # --- 3. compose.yamlの検証 ---
    if found_files:
        print("\n🔍 Composeファイル検証:")
        config_result = run_cmd("docker compose config --quiet 2>&1")
        if config_result:
            print(f"  ⚠️  検証エラー: {config_result[:200]}")
            issues.append("compose.yamlに構文エラーがあります。")
        else:
            print("  ✅ 構文チェックOK")

        # サービス数
        services = run_cmd("docker compose config --services 2>/dev/null")
        if services:
            service_list = services.strip().split('\n')
            print(f"  サービス数: {len(service_list)}")
            for s in service_list:
                print(f"    - {s}")

    # --- 4. 実行中のComposeプロジェクト ---
    print("\n📦 実行中のComposeプロジェクト:")
    ls_result = run_cmd("docker compose ls 2>/dev/null")
    if ls_result:
        for line in ls_result.strip().split('\n'):
            print(f"  {line}")
    else:
        print("  実行中のプロジェクトなし")

    # --- 5. GPU確認 ---
    print("\n🎮 GPU(Compose GPU対応):")
    nvidia_check = run_cmd("docker info --format '{{.Runtimes}}' 2>/dev/null")
    if "nvidia" in str(nvidia_check).lower():
        print("  NVIDIA Runtime: 有効 ✅")
        print("  → compose.yamlでdeploy.resources.reservations.devicesを使用可能")
    else:
        print("  NVIDIA Runtime: 未検出")
        print("  → GPU不要なら問題なし")

    # --- 6. ディスク使用量 ---
    print("\n💾 ディスク使用量:")
    df_result = run_cmd(
        "docker system df --format 'table {{.Type}}\t{{.Size}}\t{{.Reclaimable}}' 2>/dev/null"
    )
    if df_result:
        for line in df_result.strip().split('\n'):
            print(f"  {line}")

    # --- 7. .envファイル確認 ---
    print("\n🔐 環境変数ファイル:")
    env_files = [".env", ".env.dev", ".env.prod", ".env.test"]
    for ef in env_files:
        if Path(ef).exists():
            line_count = sum(
                1 for line in open(ef) if not line.startswith('#') and line.strip()
            )
            print(f"{ef} ({line_count}変数)")

    # --- 結果サマリー ---
    print("\n" + "=" * 55)
    if issues:
        print("❌ 問題:")
        for issue in issues:
            print(f"  🔴 {issue}")
    if warnings:
        print("⚠️  注意:")
        for w in warnings:
            print(f"  🟡 {w}")
    if not issues and not warnings:
        print("✅ Docker Compose環境は正常です!")
    print("=" * 55)


if __name__ == "__main__":
    check_compose_environment()

実装方法がわかったので、次は具体的なユースケースを見ていきます。


6. ユースケース別ガイド

6.1 ユースケース1: GPU対応AIローカル推論スタック

想定読者: OllamaやローカルLLMをDocker Composeで管理したい方

推奨構成: Ollama(GPU推論) + Open WebUI + PostgreSQL

サンプルコード:

# compose.ai-stack.yaml - GPU対応AIローカル推論スタック
# 起動: docker compose -f compose.ai-stack.yaml up -d
# UI:   http://localhost:8080

services:
  # --- LLM推論エンジン ---
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama-models:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all           # 全GPUを使用
              capabilities: [gpu]
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  # --- WebUI ---
  webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - "8080:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - DATABASE_URL=postgresql://aiuser:aipassword@db:5432/openwebui
      - WEBUI_AUTH=true
    volumes:
      - webui-data:/app/backend/data
    depends_on:
      ollama:
        condition: service_healthy
      db:
        condition: service_healthy
    restart: unless-stopped

  # --- データベース ---
  db:
    image: postgres:16-alpine
    container_name: ai-postgres
    environment:
      - POSTGRES_DB=openwebui
      - POSTGRES_USER=aiuser
      - POSTGRES_PASSWORD=aipassword
    volumes:
      - ai-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U aiuser -d openwebui"]
      interval: 5s
      timeout: 3s
      retries: 10
    restart: unless-stopped

volumes:
  ollama-models:     # LLMモデルファイル(数GB〜数十GB)
  webui-data:        # WebUI設定・チャット履歴
  ai-db-data:        # PostgreSQLデータ
#!/usr/bin/env bash
# setup_ai_stack.sh - AIスタックの初期セットアップ
# 実行方法: bash setup_ai_stack.sh

set -euo pipefail

COMPOSE_FILE="compose.ai-stack.yaml"

echo "=== AI推論スタック セットアップ ==="

# 1. 起動
echo "[1/3] スタック起動中..."
docker compose -f "$COMPOSE_FILE" up -d

# 2. Ollamaの準備完了を待つ
echo "[2/3] Ollamaの起動を待機中..."
until docker compose -f "$COMPOSE_FILE" exec -T ollama \
    curl -sf http://localhost:11434/api/tags > /dev/null 2>&1; do
    echo "  待機中..."
    sleep 3
done
echo "  Ollama起動完了 ✅"

# 3. モデルのダウンロード
echo "[3/3] LLMモデルをダウンロード中..."
docker compose -f "$COMPOSE_FILE" exec -T ollama ollama pull llama3.2:8b

echo ""
echo "=========================================="
echo "✅ セットアップ完了!"
echo "  WebUI:  http://localhost:8080"
echo "  Ollama: http://localhost:11434"
echo ""
echo "GPUの確認:"
docker compose -f "$COMPOSE_FILE" exec ollama \
    nvidia-smi --query-gpu=name,memory.total --format=csv,noheader 2>/dev/null \
    || echo "  (GPU情報取得不可)"
echo "=========================================="

VRAMの目安: RTX 5090(32GB)なら70BパラメータのモデルもGGUF Q4量子化で動作します。VRAMが足りない場合はollama pull llama3.2:3bで小さいモデルから試してください。

6.2 ユースケース2: Web + DB + キャッシュの本番構成

想定読者: Webアプリケーションの開発〜本番デプロイまでをComposeで管理したい方

推奨構成: FastAPI + PostgreSQL + Redis + Nginx

サンプルコード:

# compose.webapp.yaml - Webアプリ本番構成
# 起動: docker compose -f compose.webapp.yaml up -d

services:
  # --- リバースプロキシ ---
  nginx:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      api:
        condition: service_healthy
    restart: always

  # --- APIサーバー ---
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: runtime
    environment:
      - DB_URL=postgresql+asyncpg://appuser:${DB_PASSWORD}@db:5432/webapp
      - REDIS_URL=redis://cache:6379/0
      - SECRET_KEY=${SECRET_KEY}
    expose:
      - "8000"                    # nginxからのみアクセス可能
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 15s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 1g
          cpus: "2.0"
    restart: always

  # --- バックグラウンドワーカー ---
  worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: runtime
    command: python -m celery -A app.worker worker --loglevel=info
    environment:
      - DB_URL=postgresql+asyncpg://appuser:${DB_PASSWORD}@db:5432/webapp
      - REDIS_URL=redis://cache:6379/0
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    deploy:
      resources:
        limits:
          memory: 512m
    restart: always

  # --- データベース ---
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=webapp
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d webapp"]
      interval: 5s
      timeout: 3s
      retries: 10
    deploy:
      resources:
        limits:
          memory: 512m
    restart: always

  # --- キャッシュ + メッセージブローカー ---
  cache:
    image: redis:7-alpine
    command: >
      redis-server
      --appendonly yes
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    deploy:
      resources:
        limits:
          memory: 300m
    restart: always

volumes:
  postgres-data:
  redis-data:

6.3 ユースケース3: Compose Watchによる開発ワークフロー自動化

想定読者: docker compose up --buildを繰り返すのが面倒な方

推奨構成: Compose V5のWatch機能を活用した自動開発環境

サンプルコード:

# compose.watch-demo.yaml - Compose Watch のデモ
# 起動: docker compose -f compose.watch-demo.yaml up --watch

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    develop:
      watch:
        # ソースコード変更 → コンテナ内に即座にsync(ホットリロード)
        - action: sync
          path: ./frontend/src
          target: /app/src
          ignore:
            - node_modules/
            - "*.test.ts"

        # package.json変更 → イメージを再ビルド
        - action: rebuild
          path: ./frontend/package.json

  backend:
    build: ./backend
    ports:
      - "8000:8000"
    develop:
      watch:
        # Pythonコード変更 → sync + サービス再起動
        - action: sync+restart
          path: ./backend/src
          target: /app/src

        # requirements.txt変更 → イメージを再ビルド
        - action: rebuild
          path: ./backend/requirements.txt
アクション 速度 用途 向いているFW
sync 最速(秒以下) ファイルをコンテナにコピー React, Next.js, Vue(HMR対応)
sync+restart 速い(数秒) ファイルコピー + サービス再起動 Flask, FastAPI(--reload無し)
rebuild 遅い(分単位) イメージを再ビルド 依存関係変更時(package.json等)

ユースケースを把握できたところで、この先の学習パスを確認しましょう。


7. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめします。

初級者向け(まずはここから)

  1. 既存のdocker runコマンドをcompose.yamlに変換する: 自分のdocker runservices:ブロックに書き直す練習
  2. 公式チュートリアル: Docker Compose Getting Started を一通り進める
  3. .envファイルで秘密情報を管理する: パスワードをcompose.yamlにハードコードしない習慣をつける

中級者向け(実践に進む)

  1. マルチファイル構成: compose.yaml + compose.prod.yaml + compose.test.yaml の使い分け
  2. Compose Watch: docker compose up --watch で開発ワークフローを自動化
  3. ヘルスチェック + depends_on条件: service_healthy で確実な起動順序を実現

上級者向け(さらに深く)

  1. Compose Bridge: Compose定義からKubernetesマニフェストを生成
  2. Go SDK: Compose v5のSDKを使ってGoアプリからコンテナを操作
  3. セキュリティ: secrets:configs:、ネットワーク分離(internal: true)の活用

8. まとめ

この記事では、Docker Composeについて以下を解説しました:

  1. Composeの本質: 複数コンテナの定義をYAMLに宣言し、一括管理する仕組み
  2. compose.yamlの構造: services / networks / volumes の3つの柱と、サービス間の依存関係・ヘルスチェックによる起動順序制御
  3. 環境の分離: 開発/本番/テストをマルチファイル構成で安全に管理する方法
  4. 実践: GPU対応AIスタック、本番Webアプリ構成、Compose Watchによる開発自動化

私の所感

Docker Composeとの本当の出会いは、RTX 5090Ollama + Open WebUI + PostgreSQL をまとめて動かした時でした。docker runを5個叩いていた時は「まあこんなもんか」と思っていましたが、compose.yamlを書いてdocker compose up -d一発で全部立ち上がった瞬間、「なぜもっと早く使わなかったのか」と後悔しました。

特にGPUパススルーがdeploy.resources.reservations.devicesで宣言的に書けるのが革命的です。docker run --gpus allと違い、compose.yamlに書いてあれば誰がどの環境で起動しても同じGPU設定が適用される。CUDA Gapで散々苦しんだ身としては、「環境の再現性」がいかに重要か身に染みています。

2026年現在、Compose v5はGo SDKの搭載、Docker Model Runnerとの統合、Compose BridgeによるKubernetes連携と、単なる「docker runの便利ラッパー」を遥かに超えた存在に進化しています。

docker runの連打」から「compose.yamlの1ファイル管理」へ。この移行は、あなたの開発体験を確実に変えるはずです。


参考文献


この記事は「わかったつもりになってない?」シリーズの一部です。

No. タイトル 状態
1 Linuxってなんだ? ✅ 公開済み
2 Ubuntuってなんだ? ✅ 公開済み
3 WSLってなんだ? ✅ 公開済み
4 Dockerってなんだ? ✅ 公開済み
5 Docker Composeってなんだ?(この記事) ✅ 公開済み
6 Kubernetesってなんだ? ✅ 公開済み

📌 X(Twitter)でも技術情報を発信しています: @geneLab_999

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?