5分でできる:すぐに動くフルスタックの構築
理論の説明より、まず一番実践的なことから始めよう——すぐ動くアプリ+データベースのスタックを作る。docker-compose.ymlファイルを作成する:
version: '3.8'
services:
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: myapp
MYSQL_USER: app_user
MYSQL_PASSWORD: app_pass
volumes:
- db_data:/var/lib/mysql
app:
image: wordpress:latest
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: myapp
WORDPRESS_DB_USER: app_user
WORDPRESS_DB_PASSWORD: app_pass
depends_on:
- db
volumes:
db_data:
スタックを起動:
docker compose up -d
状態を確認:
docker compose ps
docker compose logs -f app
http://localhost:8080を開くと、MySQLと連携したWordPressが動いている。一連の作業は30秒もかからない。手動でやる場合と比べてみよう:MySQLに5〜6個のフラグを付けたdocker runコマンド、WordPressに8〜10個のフラグを付けた別のコマンド、そして2つのコンテナが互いを認識できるようにネットワークを手動で作成……Composeはこれらすべてを1つのファイル、1つのコマンドにまとめてくれる。
詳細解説:罠にはまらないために理解しておくこと
depends_onは「サービスが準備完了するまで待つ」ではない
初めて本番環境をセットアップしたとき、自分もこの罠にはまった。depends_onが保証するのは、コンテナが順番に起動することだけだ——内部のサービスが接続を受け付ける準備ができているかどうかは保証しない。MySQLが初期化を完了して接続を受け付けるまで通常15〜30秒かかるが、appコンテナはその前にすでに起動を終えてしまっている。
healthcheckで正しく修正する:
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
app:
depends_on:
db:
condition: service_healthy
Networks:コンテナはどのようにお互いを認識するのか
Docker Composeはプロジェクトごとに専用のbridgeネットワークを自動作成する。コンテナ同士はIPアドレスではなくサービス名で通信する。WORDPRESS_DB_HOST: dbにIPアドレスをハードコードせずサービス名を使う理由はこれだ:IPアドレスは再起動のたびに変わる可能性があるが、サービス名は変わらない。
スタックが大きくなると、トラフィックを分離したくなる。例えば、nginxはフロントエンドとバックエンドの両方と通信する必要があるが、データベースはインターネットに出てはいけない:
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # インターネットアクセスなし
services:
nginx:
networks:
- frontend
- backend
api:
networks:
- backend
db:
networks:
- backend
internal: trueはデータベースを隔離するための便利なテクニックだ——dbコンテナは自らインターネットへの外部通信ができなくなり、攻撃対象領域を大幅に削減できる。
Volumes:Named VolumesとBind Mountの違い
services:
app:
volumes:
# Named volume — Dockerが管理、コンテナを削除しても保持される
- app_data:/var/www/html/uploads
# Bind mount — ホストからディレクトリをマウント(開発時に便利)
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
# tmpfs — RAMのみ、コンテナ停止で消去(キャッシュに使用)
- type: tmpfs
target: /tmp/cache
volumes:
app_data:
シンプルなルール:開発時の設定ファイルにはbind mountを使う——ホスト上でnginx.confを編集すれば即座に反映され、イメージの再ビルドは不要。データベースやアップロードファイルなど永続化が必要なデータにはNamed volumeを使う——Dockerが管理し、コンテナを削除して再作成しても消えない。
応用編:本番環境で毎日使うパターン
.envファイルで環境を分離する
docker-compose.ymlに認証情報をハードコードしてはいけない。.envを使い、すぐに.gitignoreに追加しよう:
# .env
MYSQL_ROOT_PASSWORD=super_secret_pass
MYSQL_DATABASE=production_db
APP_PORT=8080
APP_ENV=production
# docker-compose.yml
services:
db:
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
app:
ports:
- "${APP_PORT}:80"
environment:
APP_ENV: ${APP_ENV}
開発環境と本番環境のoverride files
同じコードベースから開発と本番デプロイを行うチームにとって非常に便利なパターンだ。共通の設定はdocker-compose.ymlに保持し、環境ごとに個別のoverride fileを作成する:
# docker-compose.override.yml ('docker compose up'実行時に自動的に読み込まれる)
services:
app:
volumes:
- .:/var/www/html # 開発時にソースコードをマウント
environment:
APP_DEBUG: "true"
# docker-compose.prod.yml
services:
app:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
restart: always
# 本番環境へのデプロイ
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Resource limits — クラスター全体を守る
30本以上のコンテナが動くクラスターでよく遭遇するのがこの状況だ:あるサービスがメモリリークを起こしてRAMを静かに食いつぶし、ノード全体を道連れにする。誰も気づかないのは、何も防ぐ仕組みがないからだ。resource limitsを適用してからは二度と起きなくなり——各サービスが自分の割り当て分だけ使うようになったことで、総resource使用量が約40%削減された。
services:
api:
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Profiles:必要なときだけサービスを起動する
services:
app: # profileなし = 常に起動
db: # profileなし = 常に起動
adminer:
image: adminer
profiles: ["tools"] # --profile toolsで呼ばれたときだけ起動
ports:
- "8081:8080"
# app + db を通常起動
docker compose up -d
# データベースのデバッグが必要なときにadminerも起動
docker compose --profile tools up -d
実践的なTips:意外と知られていない便利なコマンド
日常業務で役立つコマンド
# 複数サービスのログを同時に表示、キーワードでフィルタリング
docker compose logs -f --tail=100 app db | grep ERROR
# 実行中のコンテナに接続
docker compose exec app bash
# 他のサービスをダウンさせずにスケール
docker compose up -d --scale worker=3
# 1つのサービスだけを再ビルドして再デプロイ
docker compose up -d --no-deps --build app
# リアルタイムのリソース使用量を確認
docker compose stats
# 最新のイメージをPullして再起動
docker compose pull && docker compose up -d
プロジェクト名の設定——3ヶ月後のコンフリクトを防ぐ
Docker Composeはコンテナ名を{project}_{service}_{replica}というパターンで命名する。デフォルトのプロジェクト名はディレクトリ名だ。問題は:異なる2つのプロジェクトが同じappというディレクトリ名を使っていると、ネットワークを共有してコンテナ名が衝突する——午前2時にこれが起きると非常にデバッグが難しいエラーになる。ファイルの先頭にnameを宣言すればそれで解決だ:
name: myapp-production
services:
...
定期的なクリーンアップでディスク容量を確保
# プロジェクトのコンテナとネットワークを削除(volumeは保持)
docker compose down
# volumeも一緒に削除——注意、データが消える!
docker compose down -v
# Docker全体をクリーンアップ:停止済みコンテナ、未使用イメージ、volume
docker system prune -a --volumes
サーバーでdocker system prune -fを毎週cronで実行している——手動で介入することなく、毎月数十GBのディスクスペースを節約できている。

