Docker Composeで3層アーキテクチャを構築しよう
この章では、Docker Composeを使ってノートアプリケーションの3層構造を一括管理する方法をハンズオン形式で学習します。これにより、compose.yamlによる複数コンテナの一括管理・起動順序制御・データ永続化ができるようになります。
1. 本章の概要
1.1 本章の目的
前章では3層それぞれを個別のdocker runで起動しました。実務ではcompose.yaml1つで全体を定義・管理するのが標準で、起動順序制御やヘルスチェック、初期化SQLの自動実行など、複数コンテナ運用の実務ノウハウもComposeに集約されています。本章では、前章と同じアプリケーションを Compose で組み直します。
1.2 ハンズオンの流れ
前章と同じ3層構成をcompose.yamlで定義し、docker compose upで一括起動します。depends_onによる起動順序制御、ボリュームによるデータ永続化、初期化SQLの自動実行までを扱います。
flowchart LR
User[ユーザ<br>ブラウザ] --> Front
subgraph DockerCompose[Docker Compose]
Front[front<br>Nginx<br>:3000] --> API[api<br>FastAPI<br>:8000]
API --> DB[(db<br>MySQL<br>:3306)]
DB --- Volume[(db-data<br>ボリューム)]
end
1.3 事前準備
前提となる講座
この章では、以下の知識を前提としています。自信がない場合は先に関連講座を実施してみましょう。
| 講座名 | 必要な知識 |
|---|---|
| Git入門 | リポジトリ作成、コミット、Pushなど、Gitの基本操作 |
必要なツール
この章では、以下のツールを使用します。まだインストールしていない場合は、リンク先の手順に沿って準備をお願いします。
| ツール名 | 関連箇所 | 理由 |
|---|---|---|
| Docker Desktop | Docker Desktopのインストール | Docker Composeで複数コンテナを起動する実行環境として使用する |
| Git | Git/GitHubのセットアップ | サンプルコードの取得やバージョン管理に使用する |
必要なアカウント
この章では、以下のアカウントを使用します。まだ用意していない場合は、リンク先の手順に沿って準備をお願いします。
| アカウント名 | 関連箇所 | 理由 |
|---|---|---|
| GitHubアカウント | Git/GitHubのセットアップ | サンプルコードのリポジトリ利用に使用する |
2. 前章との違い
前の章 Dockerで3層アーキテクチャを構築しよう では、3つのコンテナを個別のコマンドで起動しました。MySQLコンテナを起動し、テーブルを手動で作成し、APIコンテナをビルドして起動し、ネットワークに接続し、フロントコンテナをビルドして起動する...という多くの手順が必要でした。
Docker Composeを使うと、これらの手順をcompose.yamlファイルに記述し、docker compose upコマンド1つで全てのコンテナを起動できます。
| 💡 ポイント |
|---|
| この章では、PythonやNginxをローカルにインストールする必要はありません。すべての処理はDockerコンテナ内で行われます。Docker Composeは、Docker Desktopに含まれているため、別途インストールする必要もありません。 |
3. プロジェクトの準備
3.1 サンプルリポジトリのクローン
この章で使用するノートアプリケーションのソースコードは、GitHubのサンプルリポジトリに用意しています。コマンドプロンプト(Windows)またはターミナル(Mac)を開き、任意の場所で以下のコマンドを実行します。
git clone https://github.com/gevanni-academy/sample-note-api.git
クローンが完了したら、Visual Studio Codeを起動し、「ファイル」→「フォルダーを開く」から、クローンしたsample-note-apiフォルダを開いてください。以降の操作は、Visual Studio Codeのターミナルから行います。
3.2 フォルダ構成の確認
クローンしたリポジトリのフォルダ構成は以下のようになっています。
sample-note-api
├── compose.yaml
├── api/
│ ├── Dockerfile
│ ├── main.py
│ ├── models.py
│ ├── database.py
│ ├── requirements.txt
│ └── routers/
│ └── notes.py
├── front/
│ ├── Dockerfile
│ ├── index.html
│ ├── style.css
│ └── script.js
└── db/
└── init.sql
各フォルダは3層アーキテクチャの各層に対応しています。
| フォルダ | 層 | 内容 |
|---|---|---|
| db/ | データベース層 | init.sql(テーブル作成・テストデータ投入用の初期化SQL) |
| api/ | アプリケーション層 | FastAPIのソースコード、Dockerfile、requirements.txt |
| front/ | プレゼンテーション層 | HTML/CSS/JavaScriptのフロントエンド、Nginx用のDockerfile |
そして、プロジェクトのルートにcompose.yamlがあります。このファイルがDocker Composeの設定ファイルで、3つのサービスの構成をまとめて定義しています。
| 💡 ポイント |
|---|
| PythonやFastAPIの詳しい説明は省略します。コードの詳細については PythonでREST APIを作ろう に記載があります。この章では、Docker Composeの設定と使い方に集中します。 |
4. compose.yamlの解説
compose.yamlを開いてみましょう。以下の内容が記述されています。
services:
# データベース層
db:
image: mysql:8.0
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: notedb
MYSQL_USER: noteuser
MYSQL_PASSWORD: notepass
volumes:
- db-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpass"]
interval: 10s
timeout: 5s
retries: 5
# アプリケーション層
api:
build: ./api
ports:
- "8000:8000"
environment:
DB_HOST: db
DB_USER: noteuser
DB_PASSWORD: notepass
DB_NAME: notedb
depends_on:
db:
condition: service_healthy
# プレゼンテーション層
front:
build: ./front
ports:
- "3000:80"
depends_on:
- api
volumes:
db-data:
各設定について解説します。
4.1 servicesセクション
servicesセクションには、起動する3つのコンテナを定義しています。
| サービス名 | 役割 | 説明 |
|---|---|---|
| db | データベース層 | MySQLコンテナ。ノートデータを永続化する |
| api | アプリケーション層 | FastAPIコンテナ。REST APIを提供する |
| front | プレゼンテーション層 | Nginxコンテナ。ノート管理画面を表示する |
4.2 dbサービスの設定
db:
image: mysql:8.0
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: notedb
MYSQL_USER: noteuser
MYSQL_PASSWORD: notepass
volumes:
- db-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpass"]
interval: 10s
timeout: 5s
retries: 5
各設定を解説します。
image: mysql:8.0
imageで使用するDockerイメージを指定しています。ここではMySQLの公式イメージのバージョン8.0を使用します。
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: notedb
MYSQL_USER: noteuser
MYSQL_PASSWORD: notepass
environmentで、MySQLコンテナの初期設定に必要な環境変数を指定しています。MYSQL_DATABASEを指定すると、コンテナ初回起動時にそのデータベースが自動的に作成されます。MYSQL_USERとMYSQL_PASSWORDで、アプリケーションがデータベースに接続するためのユーザを作成しています。
volumes:
- db-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumesで、コンテナ内のデータをホスト側に保存する設定をしています。1行目のdb-data:/var/lib/mysqlは、MySQLのデータを名前付きボリュームに保存することで、コンテナを削除してもデータが失われないようにしています。2行目は、db/init.sqlをMySQLの初期化ディレクトリにマウントしています。これにより、コンテナ初回起動時にテーブル作成とテストデータ投入が自動で実行されます。
| 📝 MySQLの初期化スクリプトについて |
|---|
MySQLの公式イメージは、コンテナ初回起動時に/docker-entrypoint-initdb.d/ディレクトリ内のSQLファイルを自動的に実行します。この仕組みを利用することで、テーブル作成やデータ投入を自動化できます。 |
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpass"]
interval: 10s
timeout: 5s
retries: 5
healthcheckは、コンテナの状態を定期的に確認するための設定です。mysqladmin pingコマンドを使って、MySQLが正常に応答するかをチェックしています。MySQLは起動に時間がかかるため、この設定がないと後述するAPIサーバがデータベース接続に失敗することがあります。
4.3 apiサービスの設定
api:
build: ./api
ports:
- "8000:8000"
environment:
DB_HOST: db
DB_USER: noteuser
DB_PASSWORD: notepass
DB_NAME: notedb
depends_on:
db:
condition: service_healthy
各設定を解説します。
build: ./api
buildで、Dockerイメージをビルドするディレクトリを指定しています。./apiディレクトリ内のDockerfileをもとにイメージが作成されます。
ports:
- "8000:8000"
portsで、ホストの8000番ポートをコンテナの8000番ポートに転送しています。これにより、ブラウザからhttp://localhost:8000でAPIにアクセスできるようになります。
environment:
DB_HOST: db
DB_USER: noteuser
DB_PASSWORD: notepass
DB_NAME: notedb
environmentで、APIがデータベースに接続するための情報を環境変数として渡しています。DB_HOSTにはdbというサービス名を指定しています。Docker Composeでは、同じネットワーク内のサービス同士はサービス名で通信できるため、IPアドレスではなくサービス名を使います。
depends_on:
db:
condition: service_healthy
depends_onで、サービスの起動順序を制御しています。condition: service_healthyを指定することで、dbサービスのhealthcheckが成功するまでAPIの起動を待機させています。これにより、MySQLが完全に起動してからAPIが接続を試みるようになります。
4.4 frontサービスの設定
front:
build: ./front
ports:
- "3000:80"
depends_on:
- api
各設定を解説します。
build: ./front
buildで、./frontディレクトリのDockerfileからフロントエンド用のイメージをビルドします。
ports:
- "3000:80"
portsで、ホストの3000番ポートをコンテナの80番ポートに転送しています。Nginxはコンテナ内で80番ポートで動作するため、ブラウザからはhttp://localhost:3000でアクセスできるようになります。
depends_on:
- api
depends_onで、apiサービスが起動してからフロントエンドを起動するように順序を指定しています。
4.5 volumesセクション
volumes:
db-data:
db-dataという名前付きボリュームを定義しています。このボリュームにMySQLのデータが保存されるため、コンテナを削除してもデータは保持されます。
5. アプリケーションの起動
5.1 サービスの一括起動
Visual Studio Codeのターミナルを開き、Docker Composeでサービスを起動します。
docker compose up -d --build
| オプション | 説明 |
|---|---|
| -d | バックグラウンドでコンテナを起動するため |
| --build | イメージを(再)ビルドするため。初回起動時やソースコードを変更した場合に使用 |
初回起動時は、イメージのビルドに時間がかかります。
| 💡 ポイント |
|---|
docker compose up -d --buildの1コマンドで、3つのコンテナすべてが起動します。前の章では、各コンテナを個別に起動し、ネットワークを作成して接続する必要がありましたが、Docker Composeではこれらの操作が自動的に行われます。 |
5.2 起動状況の確認
サービスの状態を確認します。
docker compose ps
すべてのサービスがrunningまたはhealthy状態になっていれば成功です。
NAME IMAGE STATUS
sample-note-api-api-1 sample-note-api-api Up
sample-note-api-db-1 mysql:8.0 Up (healthy)
sample-note-api-front-1 sample-note-api-front Up
| ⚠️ サービスが起動しない場合 |
|---|
docker compose logs サービス名でエラーログを確認してください。よくある問題として、以下のものがあります。・ dbサービスがhealthyにならない → MySQLの起動に時間がかかっています。1分程度待ってから再度docker compose psを実行してください。・ apiサービスがすぐに終了する → docker compose logs apiでエラーを確認してください。・ frontサービスがビルドに失敗する → docker compose logs frontでエラーを確認してください。 |
5.3 ログの確認
全サービスのログをリアルタイムで確認できます。
docker compose logs -f
以下のようなログが表示されていれば、各サービスが正常に起動しています。
db:ready for connectionsと表示され、MySQLが接続を受け付ける状態になっているapi:Uvicorn running on http://0.0.0.0:8000と表示され、APIサーバが起動しているfront:start worker processと表示され、Nginxが起動している
Ctrl+Cでログの表示を終了します。
特定のサービスのログだけを確認したい場合は、サービス名を指定します。例えば、APIサーバのログだけを確認するには以下を実行します。
docker compose logs api
以下のようにUvicorn running on http://0.0.0.0:8000と表示されていれば、APIサーバが正常に起動しています。
sample-note-api-api-1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
6. 動作確認
6.1 ブラウザでアクセス
Webブラウザを開き、以下のURLにアクセスします。
http://localhost:3000
「ノートアプリケーション」というページが表示され、init.sqlで登録した初期データのノートが一覧表示されれば成功です。

6.2 機能の確認
ノートアプリケーションの各機能が正しく動作するか確認していきます。
まず、「新規ノート」欄にタイトルと内容を入力し、「追加」ボタンをクリックしてください。

一覧に新しいノートが追加されれば、フロントエンドからAPIを経由してデータベースへの書き込みが正常に行われています。

次に、ノートの「詳細」ボタンをクリックしてください。

ノートの全文と作成日・更新日が表示されれば、データベースからの読み取りが正常に動作しています。

続いて、「編集」ボタンをクリックします。

タイトルや内容を変更して保存してください。

変更内容が反映されれば、更新処理が正常に動作しています。

最後に、「削除」ボタンをクリックしてください。

ノートが一覧から消えれば、削除処理も正常です。

これらの操作がすべて動作すれば、3層アーキテクチャ(フロントエンド → API → データベース)の連携が正しく構成されています。
6.3 APIの直接確認
Visual Studio Codeのターミナルから、APIサーバに直接リクエストを送信して確認することもできます。
curl http://localhost:8000/notes
以下のように、ノートの一覧がJSON形式で返ってくれば、APIサーバが正常に動作しています。
[{"title":"Meeting Notes","content":"Next meeting is on Monday. Need to prepare documents.","id":3,"created_at":"2025-01-01T00:00:00","updated_at":"2025-01-01T00:00:00"},{"title":"Shopping List","content":"Milk, Eggs, Bread, Coffee Beans","id":2,"created_at":"2025-01-01T00:00:00","updated_at":"2025-01-01T00:00:00"},{"title":"Docker Compose Notes","content":"Docker Compose manages multiple containers. Define services in compose.yaml.","id":1,"created_at":"2025-01-01T00:00:00","updated_at":"2025-01-01T00:00:00"}]
7. 前章との比較
前の章(手動操作)と本章(Docker Compose)の手順を比較してみましょう。
| 操作 | 手動操作 | Docker Compose |
|---|---|---|
| ネットワーク作成 | docker network create |
自動作成 |
| イメージビルド | コンテナごとにdocker image build |
自動ビルド |
| コンテナ起動 | コンテナごとにdocker container run |
docker compose up -d --build |
| ネットワーク接続 | コンテナごとにdocker network connect |
自動接続 |
| テーブル作成 | 手動でSQLを実行 | init.sqlで自動実行 |
| 停止・削除 | docker container stop + rm + network rm |
docker compose down |
このように、Docker Composeを使うことで、複雑な手順をcompose.yamlに定義し、1つのコマンドで実行できるようになります。
8. 不要リソースの削除
ハンズオンが終わったら、作成したリソースを削除します。
8.1 サービスの停止と削除
Visual Studio Codeのターミナルから、Docker Composeで起動したすべてのサービスを停止し、コンテナ、ネットワーク、ボリューム、イメージをまとめて削除します。
docker compose down -v --rmi all
| オプション | 説明 |
|---|---|
| -v | ボリュームも削除する |
| --rmi all | ビルドしたイメージも削除する |
以下のように、各リソースが削除されたことが表示されれば成功です。
[+] Running 4/4
✔ Container sample-note-api-front-1 Removed
✔ Container sample-note-api-api-1 Removed
✔ Container sample-note-api-db-1 Removed
✔ Network sample-note-api_default Removed
✔ Volume sample-note-api_db-data Removed
| 💡 ポイント |
|---|
-vオプションを付けないと、db-dataボリュームは残ります。これにより、次回docker compose upしたときに前回のデータが復元されます。完全にクリーンな状態に戻したい場合は、-vオプションを付けてください。 |
8.2 削除の確認
コンテナが削除されたことを確認します。
docker compose ps
以下のようにヘッダ行のみが表示され、コンテナが1つも表示されなければ削除完了です。
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
次に、ネットワークが削除されたことを確認します。
docker network ls
表示されたネットワーク一覧にsample-note-api_defaultが含まれていなければ、ネットワークも正常に削除されています。
最後に、イメージが削除されたことを確認します。
docker image ls
表示された一覧にsample-note-api-api、sample-note-api-front、mysqlが含まれていなければ、イメージも正常に削除されています。
8.3 プロジェクトフォルダの削除(任意)
クローンしたプロジェクトフォルダが不要な場合は、Visual Studio Codeでフォルダを閉じた後、エクスプローラー(Windows)やFinder(Mac)からsample-note-apiフォルダを削除してください。
9. まとめ
この章では、Docker Composeを使った3層アーキテクチャの構築を体験しました。
- Docker Composeを使い、複数のコンテナを
compose.yamlファイルで一括管理できる - compose.yaml に services・volumes・networks を記述し、環境をコードで表現できる
- depends_on と healthcheck を組み合わせて、サービスの起動順序を適切に制御できる
- 初期化SQLを
/docker-entrypoint-initdb.d/にマウントすることで、DBのセットアップを自動化できる docker compose up -d --buildで一括起動、docker compose downで一括停止ができる- 個別コマンドの手順と比べて、開発中の起動・停止・再ビルドを大幅に簡略化できる
次の章では、AWSが提供するコンテナレジストリである Amazon ECR について学びます。