一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • Docker Composeで作る開発環境:マルチコンテナ構成のベストプラクティス

Docker Composeによるマルチコンテナ開発環境の構築

開発チームが拡大するにつれ、「自分のマシンでは動くのに」という問題が頻繁に発生するようになります。Docker Composeを活用すれば、複数のサービスを一括管理し、チーム全員が同一の開発環境を即座に立ち上げることができます。本記事では、実務で培ったマルチコンテナ構成のベストプラクティスを具体的なコード例とともに解説します。

基本的なdocker-compose.ymlの設計

まず、典型的なWebアプリケーション構成として、アプリケーションサーバー、データベース、キャッシュの3コンテナ構成を考えます。

version: '3.8'

services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app
- node_modules:/app/node_modules
environment:
- DATABASE_URL=postgres://user:pass@db:5432/myapp_dev
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started

db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=myapp_dev
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5

cache:
image: redis:7-alpine
volumes:
- redis_data:/data

volumes:
node_modules:
postgres_data:
redis_data:

ここで重要なのは、depends_onにヘルスチェック条件を付けている点です。単にコンテナが起動しただけでは、PostgreSQLが接続を受け付ける準備ができているとは限りません。service_healthy条件を使うことで、データベースが確実に利用可能になってからアプリケーションが起動します。

開発用と本番用の設定を分離する

Docker Composeには設定ファイルのオーバーライド機能があり、これを活用して環境ごとの差異を管理します。

# docker-compose.override.yml(開発環境用、自動読み込み)
services:
app:
build:
target: development
volumes:
- .:/app
command: npm run dev
environment:
- DEBUG=true
- LOG_LEVEL=debug

# docker-compose.prod.yml(本番環境用)
services:
app:
build:
target: production
restart: always
environment:
- NODE_ENV=production
- LOG_LEVEL=info
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G

開発時は単にdocker compose upとすればdocker-compose.ymldocker-compose.override.ymlが自動的にマージされます。本番環境ではdocker compose -f docker-compose.yml -f docker-compose.prod.yml upと明示的に指定します。

マルチステージビルドとの組み合わせ

Dockerfileでマルチステージビルドを使うと、開発用と本番用のイメージを一つのファイルで管理できます。

FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

FROM base AS production
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["node", "dist/main.js"]

ボリューム管理の実践的なポイント

開発環境でバインドマウントを使う際、よく遭遇するのがNode.jsのnode_modules問題です。ホストとコンテナでnode_modulesを共有するとネイティブバイナリの互換性問題が発生します。名前付きボリュームでnode_modulesを分離するパターンは必須テクニックです。

また、データベースのボリュームに関しては、開発中にスキーマを大幅に変更した場合などにデータを初期化したいことがあります。その際は以下のコマンドが便利です。

# ボリュームごと破棄して再構築
docker compose down -v
docker compose up --build

ネットワーク設計とサービス間通信

Docker Composeはデフォルトで一つのブリッジネットワークを作成し、サービス名でDNS解決が行われます。しかし、マイクロサービス構成ではネットワークを分離したいケースがあります。

services:
frontend:
networks:
- frontend_net
api:
networks:
- frontend_net
- backend_net
db:
networks:
- backend_net

networks:
frontend_net:
backend_net:

この構成では、frontendからdbに直接アクセスすることはできず、必ずapiを経由する設計を強制できます。セキュリティの観点からも有効なパターンです。

デバッグとトラブルシューティング

実務では、コンテナの動作確認やログ調査が頻繁に必要になります。覚えておくべきコマンドをまとめます。

# 特定サービスのログをリアルタイムで確認
docker compose logs -f app

# コンテナ内に入って調査
docker compose exec app sh

# サービスの状態一覧
docker compose ps

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

よくあるトラブルと対処法

  • ポートの競合:ホスト側で既に使われているポートがある場合、portsのホスト側ポート番号を変更する
  • ファイル変更が反映されない:ボリュームのキャッシュが原因の場合、docker compose up --build --force-recreateで解決
  • コンテナ間通信の失敗:サービス名で接続しているか確認し、docker compose exec app ping dbで疎通確認する
  • ディスク容量の圧迫:不要なイメージやボリュームをdocker system prune -a --volumesで一括削除する

CI/CDパイプラインとの統合

Docker Composeは開発環境だけでなく、CIパイプラインでのテスト実行にも活用できます。テスト用のcomposeファイルを用意し、テスト完了後にコンテナを自動破棄するのがポイントです。

# CI環境でのテスト実行例
docker compose -f docker-compose.yml -f docker-compose.test.yml run --rm app npm test
docker compose down -v

GitHub ActionsやGitLab CIと組み合わせれば、PR作成時にマルチコンテナ環境でのインテグレーションテストを自動実行できます。

まとめ

Docker Composeによるマルチコンテナ構成は、チーム開発の生産性を大きく向上させます。重要なポイントを振り返ります。

  • ヘルスチェックによるサービス間の起動順序制御を必ず設定する
  • 開発用と本番用の設定はオーバーライドファイルで分離する
  • ボリューム管理を適切に行い、ネイティブバイナリの互換性問題を回避する
  • ネットワーク分離でサービス間のアクセスを制御する
  • CI/CDパイプラインへの統合でテストの信頼性を高める

最初の設計に少し時間をかけるだけで、その後の開発体験が大きく変わります。ぜひチームの開発環境に導入してみてください。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア