はじめに
個人開発でDocker / Docker Composeを使い始めると、次第に以下のような悩みにぶつかることが多いのではないでしょうか。
-
docker compose up -dを毎回打つのが面倒 -
execのサービス名を間違えたり、DB接続のユーザー名を忘れたりする - 「久々に触るプロジェクトで、どうやってマイグレーションするんだっけ?」とREADMEを探す
本記事では、Docker Compose操作をMakefileに集約し、「便利ツール」ではなく「プロジェクトの実行仕様書」として扱うアプローチを紹介します。
なぜMakefileなのか
課題:コマンドの肥大化と暗黙知の増大
Docker Composeを直接操作していると、コマンドが長く複雑になりがちです。
# よくある長いコマンドの例
docker compose exec db psql -U admin_user -d postgres_db
docker compose exec backend alembic revision --autogenerate -m "add_user_table"
また、プロジェクト固有のマイグレーション手順やヘルスチェックの方法が「READMEにしか書いていない(しかも実行が保証されていない)」という状態になり、手順が形骸化してしまいます。
解決策:Makefileを「正」とする
- Docker操作はすべてMakefile経由で行う
- Makefileを単なるショートカットではなく実行仕様書と定義する
- READMEには「詳細はMakefileを見てください」と言える状態にする
実践的なMakefileの全体像
以下は、FastAPI + PostgreSQLの構成を想定した、実戦的なMakefileです。
.PHONY: help \
up up-log down restart logs ps build \
backend db psql migrate revision \
health1 health2 health3 health4 health-all \
lint
# =========================
# 基本操作
# =========================
help:
@echo "===================== Usage =====================:"
@echo "docker:"
@echo " make up - コンテナ起動"
@echo " make up-log - コンテナ起動(ログあり)"
@echo " make down - コンテナ停止"
@echo " make restart - 再起動"
@echo " make ps - 起動状態確認"
@echo " make logs - ログ表示"
@echo ""
@echo "DB / Migration:"
@echo " make psql - DB(psql)接続"
@echo " make migrate - alembic upgrade head"
@echo " make revision msg=\"\" - alembic revision (手動)"
@echo ""
@echo "health check:"
@echo " make health-all - API一括チェック"
@echo ""
@echo "静的解析 (Linter):"
@echo " make lint - ruff checkとmypyを実施"
@echo ""
# =========================
# Docker Compose 操作
# =========================
up:
docker compose up -d
up-log:
docker compose up
down:
docker compose down
restart:
docker compose down
docker compose up -d
build:
docker compose up -d --build
ps:
docker compose ps
logs:
docker compose logs -f
# =========================
# 開発・デバッグ
# =========================
backend:
docker compose exec backend bash
db:
docker compose exec db bash
psql:
docker compose exec db psql -U admin_user -d knowledgehub_db
# =========================
# Alembicマイグレーション
# =========================
migrate:
docker compose exec backend alembic upgrade head
revision:
ifndef msg
$(error 引数 msg が指定されていません。例: make revision msg="add_user_table")
endif
docker compose exec backend alembic revision --autogenerate -m "$(msg)"
# =========================
# ヘルスチェック
# =========================
health-all: health1 health2 health3 health4
health1:
@echo "--- [1/4] Basic Health Check ---"
curl http://localhost:8000/api/health
@echo "\n"
health2:
@echo "--- [2/4] Client Error Test (400系) ---"
curl http://localhost:8000/api/error-test
@echo "\n"
health3:
@echo "--- [3/4] Server Error Test (500系) ---"
curl http://localhost:8000/api/error-test-500
@echo "\n"
health4:
@echo "--- [4/4] Database Connection Check ---"
curl http://localhost:8000/api/db-check
@echo "\n"
# =========================
# 静的解析 (Linter)
# =========================
lint:
@echo "--- [1/2] Ruff Check ---"
ruff check backend/app
@echo "--- [2/2] mypy Check ---"
mypy backend/app --config-file backend/pyproject.toml
このMakefileのポイント
1. 安全性の担保(ifndefによる引数チェック)
make revisionを実行する際、コメント(msg)を必須にしています。
revision:
ifndef msg
$(error 引数 msg が指定されていません。例: make revision msg="add_user_table")
endif
docker compose exec backend alembic revision --autogenerate -m "$(msg)"
これにより、「あとで見返したときに何のためのマイグレーションかわからない」という事態を防げます。
# エラーになる
$ make revision
Makefile:xx: *** 引数 msg が指定されていません。例: make revision msg="add_user_table". Stop.
# 正しい実行
$ make revision msg="add_user_table"
2. ヘルスチェックの多段化
単なる疎通確認だけでなく、DB接続確認やエラーハンドリングのテスト(400系/500系)をhealth-allとしてまとめることで、デプロイ直前や環境構築直後の動作確認が一瞬で終わります。
$ make health-all
--- [1/4] Basic Health Check ---
{"status":"healthy"}
--- [2/4] Client Error Test (400系) ---
{"detail":"This is a test error"}
--- [3/4] Server Error Test (500系) ---
{"detail":"Internal server error"}
--- [4/4] Database Connection Check ---
{"status":"connected","database":"knowledgehub_db"}
3. 静的解析(Linter/Type Check)の統合
Docker外で実行しがちなruffやmypyもMakefileに含めています。これにより、「このプロジェクトで通すべき品質基準」がMakefileを見るだけで明確になります。
$ make lint
--- [1/2] Ruff Check ---
All checks passed!
--- [2/2] mypy Check ---
Success: no issues found in 15 source files
導入後の開発フロー
Makefileを導入すると、開発者の行動は次のように変わります。
-
開発開始:
make upで起動 -
DBの状態確認:
make psqlで即座にクエリを叩く -
モデル変更:
make revision msg="add_column"でファイル生成、make migrateで反映 -
品質確認:
make lintを通してからコミット -
不調時:
make health-allで、アプリが死んでいるのかDBが死んでいるのかを切り分け
実際の作業例
# プロジェクト開始
$ make up
[+] Running 2/2
✔ Container project-db Started
✔ Container project-backend Started
# DBにカラム追加
$ make revision msg="add_email_to_users"
INFO [alembic.runtime.migration] Running upgrade -> abc123, add_email_to_users
$ make migrate
INFO [alembic.runtime.migration] Running upgrade abc123 -> def456
# 動作確認
$ make health-all
--- [1/4] Basic Health Check ---
{"status":"healthy"}
...
# コミット前の品質チェック
$ make lint
--- [1/2] Ruff Check ---
All checks passed!
--- [2/2] mypy Check ---
Success: no issues found
まとめ:Makefileは最強の「実行できる仕様書」
Docker / Docker Composeを使った個人開発では、以下の役割分担が理想的です。
- Docker Compose: 実行基盤。インフラの構成を定義
- Makefile: 開発手順の正。「このプロジェクトはどう動かすべきか」の定義
「Makefileは古い」という印象を持っている方にこそ、この「標準化ツール」としてのパワーを体感してほしいと思います。手順をMakefileに固定することで、半年後の自分や、プロジェクトに参加する他人の認知負荷を劇的に下げることができます。