← ブログ一覧

Docker Compose で開発環境を統一する実践ガイド

「手元では動くのに…」を解消。Docker Compose で DB・Redis・メールサーバーを含む開発環境を構築する手順を解説。環境変数管理・ボリューム設計・トラブルシューティングまで網羅した実務ガイド。

#Docker#DevOps#インフラ#技術解説
Docker Compose で開発環境を統一する実践ガイド

Docker Compose で開発環境を統一する実践ガイド

「手元では動くのに本番で動かない」「新メンバーの環境構築に半日かかる」——こうした問題は、開発環境の統一で大幅に軽減できます。Docker Compose を使えば、PostgreSQL・Redis・メールサーバーといったミドルウェアを含む開発環境を、チーム全体で同じ状態に保てます。

この記事では、受託開発・自社開発の現場で即使える Docker Compose 環境の構築手順を、実務で遭遇する課題と解決策を交えて解説します。


1. Docker Compose による環境統一のメリット

解決できる課題

| 課題 | Docker Compose による解決 | |------|---------------------------| | 環境構築に時間がかかる | docker compose up 一発で完了 | | OS・バージョン違いで動作が変わる | コンテナで環境を固定 | | 本番と開発で DB バージョンが違う | image: postgres:16 で明示的に指定 | | ミドルウェアのインストールが煩雑 | 設定ファイルだけで管理 | | チームメンバー間で設定が異なる | Git でバージョン管理 |

実務上の注意点

  • 本番環境では Docker Compose を使わない: 本番は Kubernetes・ECS・Cloud Run など専用のオーケストレーションツールを使う
  • 開発専用の設定を含める: デバッグ用ポート公開・ボリュームマウントなど、開発に特化した設定を含める
  • パフォーマンスは妥協する: 開発環境では再現性を優先し、多少の遅延は許容する

2. 基本構成:Web アプリ + DB + Redis

ディレクトリ構造

project-root/
├── docker-compose.yml       # メイン設定
├── .env.example             # 環境変数テンプレート
├── .env                     # ローカル環境変数(gitignore)
├── docker/
│   ├── app/
│   │   └── Dockerfile       # アプリケーション用
│   └── nginx/
│       └── default.conf     # リバースプロキシ設定
└── volumes/                 # データ永続化(gitignore)
    ├── postgres/
    └── redis/

docker-compose.yml の基本形

version: '3.9'

services:
  # アプリケーション本体
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://appuser:apppass@db:5432/appdb
      - REDIS_URL=redis://redis:6379
    volumes:
      # ソースコードをマウント(ホットリロード用)
      - ./src:/app/src
      - ./package.json:/app/package.json
      # node_modules は除外(パフォーマンス向上)
      - /app/node_modules
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    command: npm run dev

  # PostgreSQL
  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppass
      POSTGRES_DB: appdb
    volumes:
      - ./volumes/postgres:/var/lib/postgresql/data
      # 初期化スクリプト
      - ./docker/db/init.sql:/docker-entrypoint-initdb.d/01-init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 5s
      timeout: 3s
      retries: 5

  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - ./volumes/redis:/data
    command: redis-server --appendonly yes

  # 開発用メールサーバー(Mailhog)
  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI

3. 環境変数管理の実務パターン

.env.example の作成

# Database
DATABASE_URL=postgresql://appuser:apppass@db:5432/appdb
POSTGRES_USER=appuser
POSTGRES_PASSWORD=apppass
POSTGRES_DB=appdb

# Redis
REDIS_URL=redis://redis:6379

# Application
NODE_ENV=development
API_PORT=3000
JWT_SECRET=dev-secret-change-in-production

# Email (Mailhog)
SMTP_HOST=mailhog
SMTP_PORT=1025
SMTP_USER=
SMTP_PASSWORD=

# External API (開発用のテストキー)
STRIPE_SECRET_KEY=sk_test_xxxxx
OPENAI_API_KEY=sk-xxxxx

docker-compose.yml での参照

services:
  app:
    env_file:
      - .env
    environment:
      # .env の値を上書きする場合はここで指定
      - NODE_ENV=development

セキュリティチェックリスト

| 項目 | 推奨設定 | |------|----------| | .env.gitignore に追加 | 必須 | | .env.example をコミット | 必須(値は空欄) | | 本番用シークレットは含めない | 必須 | | 開発用ダミー値を用意 | 推奨 | | チーム内で初期値を統一 | 推奨 |


4. ボリューム設計:データ永続化と開発効率

ボリュームの種類と使い分け

volumes:
  # パターン 1: ホストディレクトリをマウント(データ永続化)
  - ./volumes/postgres:/var/lib/postgresql/data
  
  # パターン 2: ソースコードマウント(ホットリロード)
  - ./src:/app/src
  
  # パターン 3: 名前付きボリューム(Docker 管理)
  - node_modules:/app/node_modules
  
  # パターン 4: tmpfs(一時ファイル、高速)
  tmpfs:
    - /tmp

node_modules 問題の解決

ホストとコンテナで OS が異なる場合、node_modules をマウントすると問題が起きます。

services:
  app:
    volumes:
      - ./src:/app/src
      - ./package.json:/app/package.json
      # node_modules は除外(匿名ボリューム)
      - /app/node_modules

データベースの初期化スクリプト

-- docker/db/init.sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_users_email ON users(email);

-- 開発用テストデータ
INSERT INTO users (email) VALUES 
  ('test1@example.com'),
  ('test2@example.com');

5. 依存関係とヘルスチェック

depends_on の限界

depends_on は起動順序を制御しますが、サービスの準備完了を待ちません

# ❌ これだけでは不十分
services:
  app:
    depends_on:
      - db

ヘルスチェックで確実に待機

services:
  app:
    depends_on:
      db:
        condition: service_healthy  # ヘルスチェック通過を待つ
  
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

アプリケーション側での再接続ロジック

// src/db/connection.ts
import { Client } from 'pg';

const MAX_RETRIES = 5;
const RETRY_DELAY = 2000;

export async function connectWithRetry(): Promise<Client> {
  const client = new Client({
    connectionString: process.env.DATABASE_URL,
  });

  for (let i = 0; i < MAX_RETRIES; i++) {
    try {
      await client.connect();
      console.log('Database connected');
      return client;
    } catch (error) {
      console.log(`Connection attempt ${i + 1}/${MAX_RETRIES} failed`);
      if (i === MAX_RETRIES - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
    }
  }
  throw new Error('Failed to connect to database');
}

6. よくある問題と解決策

問題 1: ポートが既に使用されている

ERROR: for db  Cannot start service db: 
  Bind for 0.0.0.0:5432 failed: port is already allocated

解決策: ホスト側のポートを変更

services:
  db:
    ports:
      - "5433:5432"  # ホスト:5433 → コンテナ:5432

問題 2: ボリュームのパーミッションエラー

ERROR: permission denied while trying to connect to 
  the Docker daemon socket

解決策: ユーザー権限の調整

services:
  app:
    user: "${UID}:${GID}"  # ホストユーザーと同じ権限
# .env に追加
UID=1000
GID=1000

問題 3: コンテナ間で通信できない

よくある間違い: localhost を使用

// ❌ コンテナ内から localhost は使えない
const dbUrl = 'postgresql://localhost:5432/appdb';

// ✅ サービス名を使う
const dbUrl = 'postgresql://db:5432/appdb';

問題 4: ホットリロードが効かない

Next.js など、一部のフレームワークは追加設定が必要です。

// next.config.js
module.exports = {
  // Docker 環境でのホットリロード有効化
  webpackDevMiddleware: config => {
    config.watchOptions = {
      poll: 1000,
      aggregateTimeout: 300,
    };
    return config;
  },
};

7. 実務で役立つ Tips

Makefile で操作を簡略化

.PHONY: up down restart logs clean

# 環境起動
up:
	docker compose up -d
	@echo "App: http://localhost:3000"
	@echo "Mailhog: http://localhost:8025"

# 環境停止
down:
	docker compose down

# 再起動
restart:
	docker compose restart

# ログ確認
logs:
	docker compose logs -f app

# DB マイグレーション
migrate:
	docker compose exec app npm run migrate

# DB シェル
db-shell:
	docker compose exec db psql -U appuser -d appdb

# Redis CLI
redis-cli:
	docker compose exec redis redis-cli

# 完全クリーンアップ(ボリュームも削除)
clean:
	docker compose down -v
	rm -rf volumes/

開発環境と CI 環境の共通化

# docker-compose.ci.yml
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    environment:
      - NODE_ENV=test
      - CI=true
    depends_on:
      db:
        condition: service_healthy
    command: npm run test:ci

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpass
      POSTGRES_DB: testdb
    tmpfs:
      - /var/lib/postgresql/data  # テストは永続化不要
# CI での実行
docker compose -f docker-compose.ci.yml up --abort-on-container-exit

プロファイルで環境を切り替え

services:
  # 常時起動
  app:
    # ...
  
  db:
    # ...
  
  # 必要時だけ起動
  mailhog:
    profiles: ["mail"]
    # ...
  
  elasticsearch:
    profiles: ["search"]
    # ...
# 基本環境のみ起動
docker compose up

# メールサーバーも起動
docker compose --profile mail up

# すべて起動
docker compose --profile mail --profile search up

8. チーム導入時のチェックリスト

必須項目

  • [ ] docker-compose.yml をリポジトリに追加
  • [ ] .env.example を作成し、必要な変数をすべて列挙
  • [ ] .envvolumes/.gitignore に追加
  • [ ] README に環境構築手順を記載
  • [ ] DB 初期化スクリプトを用意
  • [ ] ヘルスチェックを設定

推奨項目

  • [ ] Makefile で操作を簡略化
  • [ ] CI 用の設定ファイルを分離
  • [ ] プロファイルで開発・テスト環境を切り替え可能に
  • [ ] ログ出力先を統一(JSON 形式推奨)
  • [ ] コンテナのリソース制限を設定

ドキュメント項目

## 開発環境セットアップ

### 初回起動

1. `.env` ファイルを作成
   ```bash
   cp .env.example .env
  1. 環境を起動

    make up
    # または docker compose up -d
    
  2. DB マイグレーション実行

    make migrate
    
  3. ブラウザで確認

    • App: http://localhost:3000
    • Mailhog: http://localhost:8025

トラブルシューティング

  • ポート競合時: .envAPI_PORT を変更
  • データをリセット: make clean && make up

---

## まとめ

Docker Compose による開発環境統一は、チーム全体の生産性を大きく向上させます。この記事で紹介した構成をベースに、プロジェクトの特性に合わせてカスタマイズしてください。

**重要なポイント**:

1. **環境変数は `.env.example` で管理**し、`.env` は Git にコミットしない
2. **ヘルスチェックで確実に依存関係を制御**する
3. **Makefile で操作を簡略化**し、チームメンバーの学習コストを下げる
4. **本番環境では Docker Compose を使わない**ことを明確にする

---

**開発環境の構築・改善でお困りですか?**  
Yureate では、Docker・Kubernetes を含むインフラ設計から、開発フロー全体の最適化まで支援しています。[お問い合わせ](https://yureate.com/contact)からお気軽にご相談ください。
この内容について相談する他の記事を見る