
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を作成し、必要な変数をすべて列挙 - [ ]
.envとvolumes/を.gitignoreに追加 - [ ] README に環境構築手順を記載
- [ ] DB 初期化スクリプトを用意
- [ ] ヘルスチェックを設定
推奨項目
- [ ] Makefile で操作を簡略化
- [ ] CI 用の設定ファイルを分離
- [ ] プロファイルで開発・テスト環境を切り替え可能に
- [ ] ログ出力先を統一(JSON 形式推奨)
- [ ] コンテナのリソース制限を設定
ドキュメント項目
## 開発環境セットアップ
### 初回起動
1. `.env` ファイルを作成
```bash
cp .env.example .env
-
環境を起動
make up # または docker compose up -d -
DB マイグレーション実行
make migrate -
ブラウザで確認
- App: http://localhost:3000
- Mailhog: http://localhost:8025
トラブルシューティング
- ポート競合時:
.envのAPI_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)からお気軽にご相談ください。
