docker-compose.yml の書き方10選:インフラエンジニアが現場で使う構成をまとめた
「docker-compose.ymlって、どう書けばいいの?」——最初にDocker Composeを触ったとき、ほぼ全員がここで止まります。YAMLのインデントにハマり、ポート番号を逆に書いて繋がらず、depends_onを信じすぎてDBより先にアプリが起動して接続エラー。思い当たる人は多いはずです。
この記事では、docker-compose.ymlの書き方を10パターンに分けて解説します。基本的な構文から本番環境を意識した構成まで、実際に動く形で紹介します。「とりあえず動く設定ファイルが欲しい」という方は、そのまま使ってください。
前提:YAMLの基本ルールを30秒で押さえる
docker-compose.ymlを書く前に、YAMLのルールを2つだけ覚えてください。これを知らないとインデントエラーで詰まります。
- インデントはスペースのみ:タブはNG。スペース2つか4つで統一する
- キーと値はコロンで区切る:
image: nginxのように書く
別のエディタからコピペするとタブが混入することがあります。VSCodeなら「タブをスペースに変換」を設定しておくと安心です。
1. 最小構成:とにかく動かす
まずは最小構成から。Nginxを1コンテナ起動するだけのシンプルなパターンです。
services:
web:
image: nginx:latest
ports:
- "8080:80"
portsの書き方は「ホスト側:コンテナ側」の順です。ここを逆に書くと繋がりません。最初に必ずハマるポイントなので覚えておいてください。
2. バージョン指定:書くべきか書かざるべきか
古いドキュメントを見るとversion: '3.8'という記述が出てきます。これはComposeファイルのスキーマバージョンを指定するものですが、Docker Compose v2以降では不要になりました。
# 古い書き方(非推奨)
version: '3.8'
services:
web:
image: nginx:latest
# 現在の書き方(versionなしでOK)
services:
web:
image: nginx:latest
古いドキュメントを参考にしていると混乱します。公式ドキュメントは常に最新を確認してください。
3. imageとbuild:既存イメージか自前ビルドか
コンテナのベースを指定する方法は2つです。
# 既存イメージを使う場合
services:
web:
image: ubuntu:latest
# Dockerfileからビルドする場合
services:
app:
build:
context: .
dockerfile: Dockerfile
contextはdocker-compose.ymlからの相対パスで指定します。「ビルドに必要なファイルがどこにあるか」を指定するイメージです。ここのパス指定を間違えると、ローカルのファイルがコンテナにコピーされずビルドが失敗します。
4. ポートマッピングと環境変数
実際のアプリケーションでよく使う設定です。
services:
app:
image: node:18
ports:
- "3000:3000"
environment:
- DB_HOST=db
- DB_USER=myuser
- DB_PASSWORD=mypassword
パスワードなどの機密情報を直書きするのはNGです。.envファイルで管理しましょう。
# .envファイル
DB_PASSWORD=secret_value
# docker-compose.yml
environment:
- DB_PASSWORD=${DB_PASSWORD}
.envファイルはGitにコミットしないこと。.gitignoreに追加してください。
5. ボリューム:データを消さないための設定
コンテナを削除するとデータも消えます。データベースのデータを残したい場合はボリュームが必須です。
services:
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
ボリュームを設定し忘れてコンテナを削除し、DBのデータを全消しした経験があります。特にDB系のサービスには必ず設定してください。
6. ネットワーク:コンテナ間の通信を制御する
Docker Composeはデフォルトで同一ファイル内のコンテナが通信できるネットワークを作成します。明示的に定義したい場合はこのように書きます。
services:
web:
image: nginx:latest
networks:
- frontend
app:
image: node:18
networks:
- frontend
- backend
db:
image: mysql:8.0
networks:
- backend
networks:
frontend:
backend:
本番環境では「DBはバックエンドネットワークにだけ繋ぐ」といった設計が必要です。全部同じネットワークに繋いだままだと、セキュリティ上の問題になります。
7. depends_on:起動順序を制御する(ただし過信するな)
DBが起動する前にアプリが起動して接続エラー——よくあるパターンです。depends_onで起動順序を制御できます。
services:
app:
build: .
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
重要:depends_onだけでは「コンテナが起動した」ことしか確認しません。「DBが接続を受け付ける状態になった」かどうかは別の話です。condition: service_healthyとhealthcheckを組み合わせて使うのが正解です。
8. 再起動ポリシー:落ちても自動で復旧させる
サーバー再起動時やコンテナのクラッシュ時に自動復旧させる設定です。
services:
app:
image: node:18
restart: unless-stopped
主なポリシーはこちらです。
always:常に再起動(Dockerデーモン再起動時も含む)on-failure:異常終了時のみ再起動unless-stopped:手動停止以外は再起動
本番環境ではunless-stoppedかalwaysを設定しておくのが基本です。
9. WebアプリとDBの連携:よく使う構成セット
Node.js + MySQLの構成例です。実際のプロジェクトでそのまま使える形で書きました。
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- DB_HOST=db
- DB_USER=myuser
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=mydb
depends_on:
db:
condition: service_healthy
networks:
- app_network
restart: unless-stopped
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=mydb
- MYSQL_USER=myuser
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app_network
networks:
app_network:
volumes:
db_data:
アプリ側でDB接続のホスト名をlocalhostにすると繋がりません。サービス名(この例ではdb)をホスト名として指定してください。コンテナ間通信ではサービス名がホスト名になります。
10. 本番環境を意識した構成:セキュリティとリソース制限
本番環境では開発環境と異なる設定が必要です。最低限意識すべき項目を追加した例です。
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_PASSWORD=${DB_PASSWORD}
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- frontend
- backend
restart: unless-stopped
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=mydb
- MYSQL_USER=myuser
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- backend
restart: unless-stopped
networks:
frontend:
backend:
volumes:
db_data:
追加しているポイントは3つです。
- リソース制限(
deploy.resources):暴走したコンテナがサーバーのリソースを食い尽くすのを防ぎます - ログローテーション(
logging):ログが溜まり続けてディスクがパンクするのを防ぎます - ネットワーク分離:DBをフロントエンドネットワークから切り離します
まとめ
今回紹介した10パターンをまとめます。
- 最小構成でNginxを起動
- versionキーは現在不要
- imageとbuildの使い分け
- ポートマッピングと環境変数(.envで管理)
- ボリュームでデータを永続化
- ネットワークでコンテナ間通信を制御
- depends_on + healthcheckで起動順序を保証
- 再起動ポリシーで自動復旧
- WebアプリとDBの連携構成
- 本番環境を意識したリソース制限とログ設定
最初から全部完璧に設定しようとしなくて大丈夫です。まず最小構成で動かして、必要な設定を一つずつ足していくのが一番の近道です。docker-compose.ymlは「動いてから育てる」ものです。