T-CREATOR

Docker Compose と Makefile を組み合わせて開発効率を最大化する方法

Docker Compose と Makefile を組み合わせて開発効率を最大化する方法

Docker Compose と Makefile を組み合わせて開発効率を最大化する方法

現代のソフトウェア開発において、Docker Compose は複数のコンテナを管理する強力なツールとして広く採用されています。しかし、複雑なコマンドの管理や繰り返し作業は開発者の負担となりがちです。

そこで注目されているのが、Makefile との組み合わせによる開発フローの効率化です。この記事では、Docker Compose と Makefile を連携させることで、どのように開発効率を最大化できるのかを具体的に解説いたします。

背景

Docker Compose の役割と特徴

Docker Compose は、複数のコンテナアプリケーションを定義・実行するためのツールです。YAML ファイル形式で設定を記述し、一つのコマンドで複数のサービスを起動できます。

主な特徴として、以下が挙げられます:

  • 複数コンテナの一括管理
  • サービス間のネットワーク設定
  • ボリュームマウントの簡単な定義
  • 環境変数の管理
yamlversion: '3.8'
services:
  web:
    build: .
    ports:
      - "3000:3000"
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp

この設定により、Web アプリケーションとデータベースを同時に起動できます。

Makefile の役割と特徴

Makefile は、ビルド自動化ツールとして長年使われてきました。現在では、開発タスクの自動化においても重要な役割を果たしています。

makefile.PHONY: build
build:
	docker-compose build

.PHONY: up
up:
	docker-compose up -d

タブ文字でインデントされたコマンドが実行され、複雑なコマンドをシンプルなターゲット名で呼び出せます。

なぜ組み合わせるのか

Docker Compose と Makefile の組み合わせが注目される理由は明確です。Docker Compose の柔軟性と Makefile の自動化機能が相互補完することで、以下のメリットが生まれます。

まず、コマンドの簡略化が挙げられるでしょう。長い Docker Compose コマンドを短いターゲット名で実行できるようになります。また、チーム全体で統一されたワークフローを確立できます。

mermaidflowchart LR
  dev[開発者] -->|make start| makefile[Makefile]
  makefile -->|docker-compose up| compose[Docker Compose]
  compose -->|コンテナ起動| containers[アプリケーション群]
  containers -->|稼働確認| dev

この図は、開発者がシンプルなコマンドで複雑なコンテナ群を起動する流れを示しています。

課題

Docker Compose 単体での限界

Docker Compose を単独で使用する場合、いくつかの制約が開発効率を阻害します。最も顕著なのは、コマンドの冗長性です。

開発中に頻繁に実行する基本的なコマンドでも、以下のような長いコマンドが必要になります:

bash# 開発環境の起動
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# ログの確認
docker-compose -f docker-compose.yml -f docker-compose.dev.yml logs -f web

# テストの実行
docker-compose -f docker-compose.yml -f docker-compose.test.yml run --rm test npm test

これらのコマンドを毎回手動で入力するのは非効率的です。

複雑なコマンドの管理問題

実際のプロジェクトでは、さらに複雑なコマンドが必要になることがあります。環境の初期化、データベースのマイグレーション、テストデータの投入など、多段階の処理が求められます。

bash# 環境の完全初期化(例)
docker-compose down -v
docker-compose build --no-cache
docker-compose up -d db
sleep 10
docker-compose exec db psql -U postgres -d myapp -f /scripts/init.sql
docker-compose up -d

このような複雑な手順をドキュメント化しても、実行ミスが発生しやすくなります。

チーム開発での統一性の欠如

チーム開発では、メンバー間での環境構築手順の統一が重要です。しかし、Docker Compose 単体では以下の問題が発生します:

  • 個人の環境に依存したコマンド実行
  • 手順書の形骸化
  • 新規メンバーのオンボーディング時間の長期化
mermaidsequenceDiagram
  participant 新人 as 新規メンバー
  participant 先輩 as 既存メンバー
  participant docs as ドキュメント
  
  新人->>docs: 環境構築手順を確認
  docs->>新人: 複雑なコマンド一覧
  新人->>先輩: 手順が分からない
  先輩->>新人: 口頭での説明
  新人->>新人: 何度もトライアンドエラー

このフローは非効率で、チーム全体の生産性を低下させます。

解決策

Makefile による Docker Compose コマンドの抽象化

Makefile を導入することで、複雑な Docker Compose コマンドをシンプルなターゲット名に抽象化できます。これにより、開発者はコマンドの詳細を覚える必要がなくなります。

makefile# 基本的なターゲット定義
.PHONY: start stop restart build

start:
	docker-compose up -d

stop:
	docker-compose down

restart: stop start

build:
	docker-compose build --no-cache

これらのターゲットにより、make start という直感的なコマンドでアプリケーションを起動できます。

開発フローの標準化

Makefile では、より複雑なワークフローも標準化できます。環境に応じた処理の分岐や、前提条件のチェックも組み込めるでしょう。

makefile# 環境変数の設定
ENV ?= development
COMPOSE_FILE = docker-compose.yml

ifeq ($(ENV),production)
	COMPOSE_FILE += -f docker-compose.prod.yml
else ifeq ($(ENV),test)
	COMPOSE_FILE += -f docker-compose.test.yml
else
	COMPOSE_FILE += -f docker-compose.dev.yml
endif

# 環境に応じた起動
up:
	docker-compose $(COMPOSE_FILE) up -d

このように、環境変数によって異なる設定ファイルを自動選択できます。

環境変数とタスクの管理

Makefile では、プロジェクト固有の設定や環境変数を一元管理できます。これにより、チーム全体で一貫した開発環境を維持できるでしょう。

makefile# プロジェクト設定
PROJECT_NAME = myapp
DOCKER_REGISTRY = myregistry.com
VERSION ?= latest

# 環境変数の設定
export COMPOSE_PROJECT_NAME=$(PROJECT_NAME)
export DOCKER_REGISTRY=$(DOCKER_REGISTRY)

# ヘルプの表示
help:
	@echo "使用可能なコマンド:"
	@echo "  make start   - アプリケーションを起動"
	@echo "  make stop    - アプリケーションを停止"
	@echo "  make test    - テストを実行"
	@echo "  make clean   - 環境をクリーンアップ"

具体例

基本的な Makefile の設定

実際のプロジェクトで使用できる Makefile の基本構成を見てみましょう。まず、プロジェクトルートに配置する Makefile です。

makefile# プロジェクト設定
COMPOSE_FILE = docker-compose.yml
PROJECT_NAME = webapp

# デフォルトターゲット
.DEFAULT_GOAL := help

# ヘルプ表示
.PHONY: help
help:
	@echo "=== $(PROJECT_NAME) 開発コマンド ==="
	@echo "make start    - 開発環境を起動"
	@echo "make stop     - 環境を停止"
	@echo "make logs     - ログを表示"
	@echo "make shell    - コンテナにログイン"

この基本設定により、プロジェクトの操作が統一されます。

次に、実際の Docker Compose 操作を定義します:

makefile# Docker Compose 基本操作
.PHONY: start stop restart

start:
	@echo "開発環境を起動しています..."
	docker-compose -f $(COMPOSE_FILE) up -d
	@echo "起動完了: http://localhost:3000"

stop:
	@echo "環境を停止しています..."
	docker-compose -f $(COMPOSE_FILE) down

restart: stop start
	@echo "環境を再起動しました"

コマンド実行時にメッセージを表示することで、何が起こっているかが明確になります。

Docker Compose ファイルとの連携

効率的な連携のため、Docker Compose ファイルも Makefile と協調するよう設計します。

yamlversion: '3.8'
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=${NODE_ENV:-development}
    depends_on:
      - db

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: ${DB_NAME:-webapp}
      POSTGRES_USER: ${DB_USER:-postgres}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

環境変数を活用することで、Makefile から柔軟に設定を制御できます。

対応する Makefile のターゲットは以下のようになります:

makefile# 環境変数の設定
export NODE_ENV ?= development
export DB_NAME ?= webapp_dev
export DB_USER ?= postgres
export DB_PASSWORD ?= password

# データベース操作
.PHONY: db-init db-migrate db-seed

db-init:
	@echo "データベースを初期化しています..."
	docker-compose exec db createdb -U $(DB_USER) $(DB_NAME) || true

db-migrate:
	@echo "マイグレーションを実行しています..."
	docker-compose exec web npm run migrate

db-seed:
	@echo "テストデータを投入しています..."
	docker-compose exec web npm run seed

開発・テスト・本番環境の切り替え

環境ごとに異なる設定を使い分けるため、Makefile で環境の切り替えを自動化できます。

makefile# 環境設定
ENV ?= development

# 環境別の Compose ファイル
ifeq ($(ENV),production)
	COMPOSE_FILES = -f docker-compose.yml -f docker-compose.prod.yml
else ifeq ($(ENV),test)
	COMPOSE_FILES = -f docker-compose.yml -f docker-compose.test.yml
else
	COMPOSE_FILES = -f docker-compose.yml -f docker-compose.dev.yml
endif

# 環境別起動
.PHONY: start-dev start-test start-prod

start-dev:
	$(MAKE) start ENV=development

start-test:
	$(MAKE) start ENV=test

start-prod:
	$(MAKE) start ENV=production

環境切り替えの流れを図で表すと以下のようになります:

mermaidstateDiagram-v2
  [*] --> Development
  [*] --> Test
  [*] --> Production

  state "docker-compose.dev.yml" as DC_DEV
  state "docker-compose.test.yml" as DC_TEST
  state "docker-compose.prod.yml" as DC_PROD
  state "Running" as RUNNING

  Development --> DC_DEV
  Test --> DC_TEST
  Production --> DC_PROD

  DC_DEV --> RUNNING
  DC_TEST --> RUNNING
  DC_PROD --> RUNNING

各環境が適切な設定ファイルを使用して起動される様子を示しています。

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

Makefile は CI/CD パイプラインとの連携でも威力を発揮します。GitHub Actions や GitLab CI などから、統一されたコマンドでテストやデプロイを実行できるでしょう。

makefile# CI/CD 用ターゲット
.PHONY: ci-test ci-build ci-deploy

ci-test:
	@echo "CI環境でテストを実行..."
	docker-compose -f docker-compose.test.yml up -d
	docker-compose -f docker-compose.test.yml exec -T web npm test
	docker-compose -f docker-compose.test.yml down

ci-build:
	@echo "本番用イメージをビルド..."
	docker-compose -f docker-compose.prod.yml build
	docker tag $(PROJECT_NAME)_web:latest $(DOCKER_REGISTRY)/$(PROJECT_NAME):$(VERSION)

ci-deploy:
	@echo "本番環境にデプロイ..."
	docker push $(DOCKER_REGISTRY)/$(PROJECT_NAME):$(VERSION)
	$(MAKE) start ENV=production

GitHub Actions での使用例:

yamlname: CI/CD Pipeline

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: make ci-test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: make ci-deploy

この構成により、CI/CD パイプラインからも統一されたコマンドでタスクを実行できます。

完全な開発ワークフロー例

実際のプロジェクトで使用できる包括的な Makefile の例をご紹介します:

makefile# =============================================================================
# プロジェクト設定
# =============================================================================
PROJECT_NAME = webapp
ENV ?= development
VERSION ?= latest

# =============================================================================
# Docker Compose ファイル設定
# =============================================================================
COMPOSE_FILE = docker-compose.yml

ifeq ($(ENV),production)
	COMPOSE_FILE += -f docker-compose.prod.yml
else ifeq ($(ENV),test)
	COMPOSE_FILE += -f docker-compose.test.yml
else
	COMPOSE_FILE += -f docker-compose.dev.yml
endif

# =============================================================================
# メインターゲット
# =============================================================================
.DEFAULT_GOAL := help

.PHONY: help
help:
	@echo "=== $(PROJECT_NAME) 開発コマンド ==="
	@echo ""
	@echo "🚀 基本操作:"
	@echo "  make start     - 開発環境を起動"
	@echo "  make stop      - 環境を停止"
	@echo "  make restart   - 環境を再起動"
	@echo "  make status    - サービス状況を確認"
	@echo ""
	@echo "🔧 開発作業:"
	@echo "  make build     - イメージをビルド"
	@echo "  make shell     - Webコンテナにログイン"
	@echo "  make logs      - ログを表示"
	@echo "  make test      - テストを実行"
	@echo ""
	@echo "🗄️ データベース:"
	@echo "  make db-init   - データベースを初期化"
	@echo "  make db-migrate- マイグレーションを実行"
	@echo "  make db-seed   - テストデータを投入"
	@echo ""
	@echo "🧹 クリーンアップ:"
	@echo "  make clean     - 全て停止・削除"
	@echo "  make reset     - 環境を完全リセット"

このヘルプ機能により、新しいメンバーでも迷わずに開発を始められます。

続いて、各種操作の実装です:

makefile# =============================================================================
# 基本操作
# =============================================================================
.PHONY: start stop restart status

start:
	@echo "📚 $(ENV)環境を起動しています..."
	docker-compose $(COMPOSE_FILE) up -d
	@echo "✅ 起動完了: http://localhost:3000"

stop:
	@echo "🛑 環境を停止しています..."
	docker-compose $(COMPOSE_FILE) down

restart: stop start

status:
	@echo "📊 サービス状況:"
	docker-compose $(COMPOSE_FILE) ps

デバッグ・保守操作の自動化

開発中のデバッグや保守作業も Makefile で効率化できます:

makefile# =============================================================================
# 開発・デバッグ操作
# =============================================================================
.PHONY: build shell logs test

build:
	@echo "🔨 イメージをビルドしています..."
	docker-compose $(COMPOSE_FILE) build

shell:
	@echo "🐚 Webコンテナにログインします..."
	docker-compose $(COMPOSE_FILE) exec web /bin/bash

logs:
	@echo "📄 ログを表示しています..."
	docker-compose $(COMPOSE_FILE) logs -f

test:
	@echo "🧪 テストを実行しています..."
	docker-compose -f docker-compose.test.yml run --rm test npm test
	@echo "✅ テスト完了"

ログの確認やコンテナへのログインが簡単になり、デバッグ効率が向上します。

環境管理とクリーンアップ

プロジェクトの保守において重要なクリーンアップ操作も自動化できます:

makefile# =============================================================================
# データベース操作
# =============================================================================
.PHONY: db-init db-migrate db-seed db-reset

db-init:
	@echo "🗄️ データベースを初期化しています..."
	docker-compose $(COMPOSE_FILE) exec db createdb -U postgres $(PROJECT_NAME) || true

db-migrate:
	@echo "📈 マイグレーションを実行しています..."
	docker-compose $(COMPOSE_FILE) exec web npm run migrate

db-seed:
	@echo "🌱 テストデータを投入しています..."
	docker-compose $(COMPOSE_FILE) exec web npm run seed

db-reset: db-init db-migrate db-seed
	@echo "✅ データベースのリセットが完了しました"

# =============================================================================
# クリーンアップ操作
# =============================================================================
.PHONY: clean reset

clean:
	@echo "🧹 環境をクリーンアップしています..."
	docker-compose $(COMPOSE_FILE) down -v --remove-orphans
	docker system prune -f

reset: clean build start db-reset
	@echo "🔄 環境の完全リセットが完了しました"

これらの操作により、環境の不具合時にも迅速に復旧できます。

完全なワークフローを図で示すと以下のようになります:

mermaidflowchart TD
  A[make start] --> B{環境変数チェック}
  B -->|development| C[dev設定で起動]
  B -->|test| D[test設定で起動]
  B -->|production| E[prod設定で起動]
  
  C --> F[アプリケーション稼働]
  D --> G[テスト環境稼働]
  E --> H[本番環境稼働]
  
  F --> I[make logs でログ確認]
  F --> J[make shell でデバッグ]
  F --> K[make test でテスト実行]
  
  I --> L[問題発見時]
  J --> L
  K --> L
  
  L --> M[make restart で再起動]
  L --> N[make reset で完全リセット]

この図は、開発者が Makefile を使って効率的に作業を進める流れを表しています。問題が発生した際の対処法も含まれており、スムーズな開発が可能です。

まとめ

Docker Compose と Makefile の組み合わせは、現代の開発現場における効率化の重要な手法です。複雑なコマンドの抽象化、チーム開発での統一性確保、CI/CD との連携など、多くのメリットをもたらします。

この記事でご紹介した実装パターンを参考に、ぜひ皆さまのプロジェクトでも導入をご検討ください。最初は基本的なターゲットから始めて、徐々に高度な自動化を追加していくのがおすすめです。

適切に設計された Makefile は、開発チーム全体の生産性を大幅に向上させる強力なツールとなるでしょう。

関連リンク