Contents
1️⃣ はじめに ― なぜ Compose の設計が重要なのか
Docker Compose は 複数コンテナをコードで管理 できる強力なツールです。
しかし、以下のような課題が見落とされがちです。
| 課題 | 影響 |
|---|---|
| ファイルが肥大化 → メンテナンスコスト増大 | 変更箇所を探しにくい |
| 機密情報を平文で管理 → セキュリティリスク | 情報漏洩や不正アクセスの原因 |
| ネットワーク・ボリュームがデフォルト設定のまま → 不要な通信やデータ破損 | 本番環境で障害につながる |
本ガイドでは、「公式にサポートされている」手法 に絞って解決策を提示します。
2️⃣ Compose ファイルバージョンの選び方
Docker Engine 20.10 以降 が対象となる最新の Compose ファイル形式は version: "3.9" です。
3.9は Swarm 機能だけでなく、profilesやhealthcheckといったモダンな構文をすべてサポートします。- 古いバージョン(例:
2.x)は一部オプションが廃止されているため、できるだけ新しい形式で統一しましょう。
|
1 2 3 |
# docker-compose.yml の冒頭 version: "3.9" |
ポイント
公式ドキュメントは常に最新版を参照してください。
https://docs.docker.com/compose/compose-file/
3️⃣ ファイル分割と再利用 ― include は存在しない
Compose に include キーワードはありません。公式が推奨する方法は「複数ファイルを組み合わせて実行」 です。
|
1 2 |
docker compose -f docker-compose.yml -f overrides.dev.yml up -d |
3‑1️⃣ 推奨ディレクトリ構成例
|
1 2 3 4 5 6 7 8 9 |
my-project/ ├─ docker-compose.yml # 基本設定(共通サービス・ネットワーク) ├─ docker-compose.prod.yml # 本番向けオーバーライド ├─ docker-compose.dev.yml # 開発向けオーバーライド └─ compose.d/ ├─ services.yml # 各サービス定義だけを抜粋 ├─ networks.yml └─ volumes.yml |
3‑2️⃣ 基本ファイル(docker-compose.yml)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
version: "3.9" services: # services.yml の内容がマージされます # 例として空のプレースホルダーだけ記載 # 実体は compose.d/services.yml にあります networks: # networks.yml が上書き・追加される想定です volumes: # volumes.yml が統合されます |
3‑3️⃣ オーバーライド例(docker-compose.dev.yml)
|
1 2 3 4 5 6 7 8 |
services: web: build: . environment: - NODE_ENV=development ports: - "3000:3000" |
メリット
- ファイル単位で変更範囲が限定できる
- CI/CD パイプラインでは必要なオーバーレイだけを指定すれば済む
4️⃣ 環境変数とシークレットのベストプラクティス
4‑1️⃣ .env は 非機密情報 のみ保存
| 項目 | 推奨場所 |
|---|---|
| アプリケーション名、ポート番号、デバッグフラグ | .env(リポジトリ外・.gitignore) |
| パスワードや API キー | Docker Secrets / env_file で暗号化管理 |
安全な .env の例
|
1 2 3 4 5 |
# .env (バージョン管理から除外) APP_ENV=production WEB_PORT=8080 POSTGRES_USER=app_user |
4‑2️⃣ Docker Secrets(Swarm)または docker compose run --env-file を活用
Swarm でのシークレット定義例
|
1 2 3 4 5 6 7 8 9 10 11 |
secrets: pg_password: external: true # 事前に `docker secret create pg_password -` で作成済み services: db: image: postgres:16-alpine secrets: - source: pg_password target: POSTGRES_PASSWORD # コンテナ内部で環境変数として利用可能 |
ローカル開発向け env_file の使用例
|
1 2 3 4 5 |
services: web: env_file: - ./config/.env.dev # .gitignore 推奨 |
ポイント
本番環境では必ず Secrets または外部の安全な鍵管理サービス(AWS Secrets Manager 等)に委ね、平文ファイルがコンテナ内に残らないようにします。
5️⃣ 典型的なマルチサービス構成例
以下は Web アプリ + PostgreSQL + Redis + RabbitMQ の構成です。
depends_on と healthcheck を組み合わせて、起動順序と可用性を保証します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
version: "3.9" services: web: build: ./web ports: - "${WEB_PORT}:80" environment: DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/app_db REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0 depends_on: db: condition: service_healthy redis: condition: service_healthy networks: - frontend db: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD_FILE: /run/secrets/pg_password # Secrets 利用例 secrets: - pg_password volumes: - db-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5 networks: - backend redis: image: redis:7-alpine command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"] volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 5s timeout: 3s retries: 4 networks: - backend rabbitmq: image: rabbitmq:3-management-alpine environment: RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER} RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS} ports: - "5672:5672" - "15672:15672" networks: - backend networks: frontend: driver: bridge backend: driver: bridge internal: true # 外部から直接アクセス不可 volumes: db-data: name: myproject_prod_db_data driver: local redis-data: name: myproject_prod_redis_data driver: local secrets: pg_password: external: true |
5‑1️⃣ depends_on と healthcheck の効果
| コンテナ | 待機条件 |
|---|---|
web → db |
service_healthy(PostgreSQL が起動し、pg_isready が OK) |
web → redis |
同上(Redis が PING で応答) |
web → rabbitmq |
service_started(起動したらすぐに接続可) |
この組み合わせだけで、「依存サービスがまだ準備できていない」エラー を大幅に減らせます。
6️⃣ ネットワーク・ボリューム設計指針
6‑1️⃣ カスタムネットワークで通信範囲を限定
| ネットワーク | 用途 |
|---|---|
frontend |
外部からアクセスが必要なサービス(Web)だけを接続 |
backend |
DB・キャッシュ・キューなど内部専用。internal: true で外部からの直接到達を遮断 |
6‑2️⃣ 永続ボリュームは命名規則とバックアップ戦略を持つ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
volumes: db-data: name: ${PROJECT_NAME}_prod_db_data # 例: myproject_prod_db_data driver: local driver_opts: o: bind type: none device: /var/lib/docker/volumes/${PROJECT_NAME}/db redis-data: name: ${PROJECT_NAME}_prod_redis_data driver: local |
- 命名は「プロジェクト‑環境‑用途」の形で一目で分かるように。
- バックアップはホスト側のマウント先ディレクトリを
rsync・スナップショットで取得すると安全です。
7️⃣ モノレポ(単一リポジトリ)での運用例
7‑1️⃣ サービスごとに独立した Compose ファイルを持つ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
my-monorepo/ ├─ docker-compose.yml # ルート(全体マージ用) └─ services/ ├─ web/ │ ├─ Dockerfile │ └─ compose.yml ├─ api/ │ ├─ Dockerfile │ └─ compose.yml └─ worker/ ├─ Dockerfile └─ compose.yml |
ルート docker-compose.yml
|
1 2 3 4 5 6 7 8 9 10 11 |
version: "3.9" services: # 各サービスファイルの内容はここにマージされます # 例: web, api, worker のみをロードしたい場合は -f オプションで選択 # 例えば開発時は次のように実行 # docker compose -f docker-compose.yml \ # -f services/web/compose.yml \ # -f services/api/compose.yml up -d |
- メリット
- CI/CD が変更されたサービスだけをビルド・テストできる。
- 共通設定(ネットワーク、ボリューム)は
docker-compose.ymlに集約し、重複を排除。
8️⃣ セキュリティ強化 ― 非 root ユーザーとシークレット管理
8‑1️⃣ Dockerfile は必ず非 root ユーザーで実行
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # アプリ実行ユーザー作成 RUN addgroup -S appgrp && adduser -S appusr -G appgrp FROM node:20-alpine WORKDIR /app COPY --from=builder /app . USER appusr # ここからは非 root EXPOSE 3000 CMD ["node", "dist/index.js"] |
8‑2️⃣ シークレットは ファイルシステムに残さない 設計
- Swarm で
docker secret create→ コンテナ内は一時的なメモリ領域にマウント。 - ローカル開発では
env_fileの代わりに.env.local(.gitignore)を使用し、Git に含めない。
9️⃣ トラブルシューティング ― Docker API バージョン不整合
Docker クライアントとデーモンの API バージョンがずれると次のようなエラーが出ます。
|
1 2 |
Error response from daemon: client version too old |
解決策
-
クライアントを最新に更新(Homebrew、APT など)
bash
brew upgrade docker # macOS の例
sudo apt-get install -y docker-ce-cli # Ubuntu の例 -
互換モードで実行(一時的な回避策)
bash
export DOCKER_API_VERSION=$(docker version --format '{{ .Server.APIVersion }}')
docker compose up
ただし根本的にはクライアントとサーバーのバージョンを合わせることが推奨です。
🔚 まとめ ― 今日から使える 5 つのチェックリスト
| # | チェック項目 |
|---|---|
| 1 | Compose ファイルは version: "3.9" を使用し、公式ドキュメントと同期させる |
| 2 | 複数ファイルは docker compose -f で組み合わせ、include は使わない |
| 3 | .env に入れる情報は 非機密 のみ。パスワード等は Secrets または外部管理ツールへ委譲 |
| 4 | カスタムネットワークと命名規則付きボリュームで「境界」と「永続性」を明確化 |
| 5 | Dockerfile は非 root ユーザー実行、CI/CD パイプラインでは変更のあったサービスだけをビルド |
it-bokenki.com では、上記ガイドに沿った実装例や CI/CD テンプレートも無料で公開しています。
今すぐリポジトリをクローンし、プロジェクトに組み込んで安全・高速なコンテナ運用を体感してください。
Happy Containerizing! 🚀