もう迷わない!Docker Compose ymlファイルの書き方入門ガイド
はじめに:なぜ今、Docker Composeなのか?
現代のWebアプリケーション開発において、Dockerはもはやなくてはならないツールの一つとなりました。アプリケーションを「コンテナ」という隔離された環境にパッケージングすることで、「自分のPCでは動いたのに、サーバー上では動かない」といった環境差異の問題から私たちを解放してくれます。
しかし、開発が進むにつれて、アプリケーションはより複雑になっていきます。例えば、Webサーバー、アプリケーションサーバー、データベース、キャッシュサーバーなど、複数のコンポーネントが連携して一つのサービスを構成することは珍しくありません。
これらのコンポーネントをそれぞれ個別のDockerコンテナとして管理しようとすると、どうなるでしょうか?
“`bash
Webサーバー(Nginx)を起動
$ docker run –name my-nginx -p 8080:80 -v ./nginx.conf:/etc/nginx/nginx.conf:ro -d nginx
アプリケーションサーバー(Node.js)を起動
$ docker run –name my-app -p 3000:3000 –link my-postgres:db -e DATABASE_URL=… -d my-app-image
データベース(PostgreSQL)を起動
$ docker run –name my-postgres -e POSTGRES_PASSWORD=… -v pgdata:/var/lib/postgresql/data -d postgres
“`
このように、非常に長く、複雑で、覚えにくい docker run コマンドをコンテナの数だけ実行し、それぞれを正しく連携(ネットワーク設定など)させる必要があります。手動での管理は、タイプミスを誘発し、プロジェクトへの新規参画者への説明コストも増大させ、何よりも開発の生産性を著しく低下させます。
この「複数コンテナの管理地獄」を解決してくれるのが、Docker Compose です。
Docker Composeは、複数のコンテナで構成されるアプリケーションを、たった一つの設定ファイルで定義し、たった一つのコマンドで管理するためのツールです。この設定ファイルこそが、本記事の主役である docker-compose.yml ファイルです。
このdocker-compose.ymlを使いこなせるようになれば、あなたの開発体験は劇的に向上します。
- 再現性の向上: 誰が実行しても同じ構成のアプリケーション環境を数秒で構築できます。
- 生産性の向上:
docker-compose up一つで全てのサービスを起動し、docker-compose down一つで全てを停止・削除できます。 - 可読性の向上: プロジェクト全体の構成がYAMLファイルとして一目でわかります。
- 管理の簡素化: 複雑なネットワーク設定やボリューム管理を自動化できます。
この記事では、docker-compose.ymlの書き方に戸惑っている初心者の方を対象に、その基本的な概念から、実践的な書き方、そしてプロが使うベストプラクティスまで、順を追って徹底的に解説します。この記事を読み終える頃には、あなたはもうdocker-compose.ymlの書き方で迷うことはなくなるでしょう。
第1章:Docker Composeの基本を理解する
docker-compose.ymlの書き方を学ぶ前に、まずはDocker Composeがどのような概念に基づいているのかを理解しましょう。急がば回れです。
1.1. Docker Composeとは?
Docker Composeをオーケストラに例えると分かりやすいかもしれません。
- 各コンテナ(Nginx, Node.js, PostgreSQLなど):オーケストラの各楽器(ヴァイオリン、チェロ、トランペットなど)
docker-compose.ymlファイル:オーケストラの「楽譜」。どの楽器が、どのタイミングで、どのように演奏するかが書かれています。- Docker Compose コマンド (
docker-compose up):オーケストラの「指揮者」。楽譜に従って、各楽器に演奏開始の合図を出し、全体として一つのハーモニーを奏でさせます。
つまり、Docker Composeは「複数のコンテナ(楽器)を、docker-compose.yml(楽譜)に基づいて、協調動作させる(指揮する)ためのツール」なのです。
1.2. 押さえておくべき3つのコアコンセプト
docker-compose.ymlを記述する上で、中心となる3つの重要な概念があります。
1. サービス (Services)
サービスは、アプリケーションを構成する各コンテナの「設計図」です。例えば、「webという名前のサービスは、nginxの公式イメージを使って、ポート8080番を公開するコンテナを起動する」といった定義をします。dbサービス、apiサービスなど、役割ごとに名前をつけて定義するのが一般的です。docker-compose.ymlの心臓部と言える部分です。
2. ネットワーク (Networks)
Docker Composeは、デフォルトでdocker-compose.ymlが置かれているプロジェクト専用のプライベートな仮想ネットワークを作成します。そして、そのファイルで定義された全てのサービス(コンテナ)を、自動的にそのネットワークに接続します。
これが非常に強力な機能で、これにより、コンテナ同士はサービス名をホスト名として相互に通信できるようになります。例えば、apiサービスのコンテナからdbサービスのコンテナに接続したい場合、IPアドレスを気にする必要はなく、単にdbという名前でアクセスできます。このおかげで、複雑なネットワーク設定から解放されるのです。
3. ボリューム (Volumes)
コンテナは本来、一時的なものです。コンテナを停止・削除すると、その中で作成・変更されたデータは全て消えてしまいます。これではデータベースのデータなどを永続的に保存できません。
そこで使われるのがボリュームです。ボリュームは、ホストマシン(あなたのPC)上の特定のディレクトリを、コンテナ内のディレクトリに「マウント(接続)」する仕組みです。これにより、コンテナが削除されてもデータはホストマシン上に残り続け、データの永続化が実現できます。
Docker Composeでは、このボリュームの定義と管理も簡単に行うことができます。
1.3. インストール
現在、Docker Desktop(Windows, Mac)をインストールすると、Docker Composeは自動的に同梱されています。そのため、ほとんどの場合は追加のインストール作業は不要です。
Linux環境でDocker Engineを直接インストールした場合は、別途Docker Composeのインストールが必要になることがあります。その際は、公式サイトのドキュメントに従ってインストールしてください。
ターミナルで以下のコマンドを実行し、バージョン情報が表示されれば準備OKです。
bash
$ docker-compose --version
Docker Compose version v2.5.0
(バージョン番号は環境によって異なります)
第2章:docker-compose.ymlの基本構造と書き方
それでは、いよいよdocker-compose.ymlファイルの書き方を見ていきましょう。
2.1. YAMLという言語
docker-compose.ymlは、YAML (YAML Ain’t Markup Language) という形式で記述されます。YAMLは人間にとって非常に読み書きしやすいデータ記述言語です。その最大の特徴は「インデント(字下げ)」で階層構造を表現する点です。
YAMLの最重要ルール:
– インデントにはスペース2つを使うのが一般的です。(タブは使わないでください!)
– 親子関係はインデントの深さで表現します。
– キー: 値 の形式でデータを記述します。コロン:の後には必ず半角スペースが必要です。
– - (ハイフン) はリスト(配列)を表します。
このルールさえ守れば、YAMLは決して難しくありません。
2.2. トップレベルのキー
docker-compose.ymlは、いくつかのトップレベル(インデントが最も浅い階層)のキーから構成されます。
“`yaml
version: “3.8” # (任意) ファイルフォーマットのバージョン
services:
# ここに各サービスの定義を記述する (必須)
networks:
# (任意) カスタムネットワークを定義する場合に記述する
volumes:
# (任意) 名前付きボリュームを定義する場合に記述する
“`
version: かつては必須でしたが、現在は任意です。どのバージョンのComposeファイルフォーマット仕様に準拠しているかを示すもので、"3.8"や"3.9"などを指定するのが一般的です。古い記事では必須と書かれていることが多いですが、最新のDocker Composeでは省略可能です。services: このファイルで最も重要な部分です。 ここに、アプリケーションを構成する各サービス(コンテナ)の定義を記述していきます。networks: デフォルトで作成されるネットワーク以外に、自分で細かくネットワークを制御したい場合に使います。入門段階ではあまり使いません。volumes: データベースのデータなど、永続化したいデータを保存するための「名前付きボリューム」を定義する場合に使います。
初心者のうちは、まず services の中身を完璧に理解することに集中しましょう。
第3章:実践① シンプルなWebサーバーを構築する
百聞は一見に如かず。実際にdocker-compose.ymlを書きながら、その使い方をマスターしていきましょう。
目標: Nginxコンテナを一つ起動し、ローカルのHTMLファイルを表示させる。
ステップ1:プロジェクトの準備
まず、作業用のディレクトリを作成しましょう。
“`bash
プロジェクトディレクトリを作成
$ mkdir nginx-sample
$ cd nginx-sample
HTMLファイルを置くためのディレクトリを作成
$ mkdir html
表示させる簡単なHTMLファイルを作成
$ echo “
Hello, Docker Compose!
” > html/index.html
“`
現在のディレクトリ構成は以下のようになっているはずです。
nginx-sample/
├── html/
│ └── index.html
└── (ここにdocker-compose.ymlを作成する)
ステップ2:最初のdocker-compose.ymlを作成する
nginx-sampleディレクトリの直下に、docker-compose.ymlという名前のファイルを新規作成し、以下の内容を記述してください。
“`yaml
nginx-sample/docker-compose.yml
version: “3.8”
services:
web:
image: nginx:latest
ports:
– “8080:80”
volumes:
– ./html:/usr/share/nginx/html
“`
たったこれだけです。一行ずつ、丁寧に解説していきましょう。
-
version: "3.8"- Composeファイルのバージョンを指定しています。
-
services:- ここからがサービスの定義の始まりです。
-
web:- インデントが一段深くなっていることに注目してください。これは
servicesの子要素であり、サービス名を定義しています。名前は任意ですが、役割がわかる名前(web,app,dbなど)をつけるのが一般的です。
- インデントが一段深くなっていることに注目してください。これは
-
image: nginx:latest- さらにインデントが深くなっています。これは
webサービスの詳細設定です。 imageキーは、このサービス(コンテナ)を起動するために使用するDockerイメージを指定します。ここでは、Docker Hubにある公式のnginxイメージの最新版(:latestタグ)を使うことを意味します。Docker Hubにイメージが存在する場合、Dockerは自動的にそれをダウンロードしてくれます。
- さらにインデントが深くなっています。これは
-
ports:- ホストマシン(あなたのPC)とコンテナのポートを繋ぐ設定です(ポートフォワーディング)。
- "8080:80": リスト形式(ハイフン-)で指定します。:の左側 (8080)がホスト側のポート番号です。:の右側 (80)がコンテナ側のポート番号です。- これは、「ホストマシンの
8080番ポートへのアクセスを、コンテナの80番ポートへ転送する」という意味です。Nginxはデフォルトで80番ポートでリクエストを待ち受けるため、このように設定します。
-
volumes:- ホストマシンとコンテナのディレクトリを同期(マウント)する設定です。
- ./html:/usr/share/nginx/html::の左側 (./html)がホスト側のディレクトリパスです。./はカレントディレクトリ(docker-compose.ymlがある場所)を意味します。:の右側 (/usr/share/nginx/html)がコンテナ側のディレクトリパスです。これはNginxがデフォルトでHTMLファイルを置く場所です。- これにより、ホストの
htmlディレクトリの中身が、コンテナの/usr/share/nginx/htmlディレクトリにマウントされます。ホスト側でindex.htmlを編集すると、即座にコンテナ内に反映されるため、開発効率が飛躍的に向上します。
ステップ3:Docker Composeを実行する
docker-compose.ymlを保存したら、ターミナルでそのファイルがあるディレクトリ (nginx-sample) に移動し、以下のコマンドを実行します。
bash
$ docker-compose up -d
docker-compose up:docker-compose.ymlの内容に基づいて、定義された全てのサービス(今回はwebサービス)をビルド&起動するコマンドです。-d: 「Detachedモード」の略で、バックグラウンドでコンテナを起動します。このオプションをつけないと、ターミナルにログが出力され続け、そのターミナルが使えなくなってしまいます。
初回実行時は、nginx:latestイメージのダウンロードが始まります。ダウンロードが終わると、コンテナが作成され、起動します。
[+] Running 2/2
⠿ Network nginx-sample_default Created 0.1s
⠿ Container nginx-sample-web-1 Started
ステップ4:動作確認
コンテナが起動したら、Webブラウザを開き、アドレスバーに http://localhost:8080 と入力してください。
先ほど作成したhtml/index.htmlの内容である「Hello, Docker Compose!」という見出しが表示されれば成功です!
ステップ5:後片付け
環境を停止・削除するには、以下のコマンドを実行します。
bash
$ docker-compose down
このコマンドは、upで作成されたコンテナとネットワークを停止・削除してくれます。非常にクリーンな後片付けが可能です。
おめでとうございます!あなたはdocker-compose.ymlを使って、最初のアプリケーション環境を構築できました。
第4章:実践② 複数コンテナ連携(Webアプリ + DB)
Docker Composeの真価は、複数のコンテナを連携させる時に発揮されます。ここでは、より実践的な例として、Node.js製のWebアプリケーションとPostgreSQLデータベースを連携させる構成を構築してみましょう。
目標:
– appサービス: Node.jsアプリケーション。dbサービスに接続し、DBサーバーのバージョンを取得して表示する。
– dbサービス: PostgreSQLデータベース。データを永続化する。
ステップ1:プロジェクトの準備
先ほどとは別に、新しいプロジェクトディレクトリを作成します。
bash
$ mkdir fullstack-app
$ cd fullstack-app
このディレクトリ内に、以下のファイルとディレクトリを作成していきます。
1. Node.jsアプリケーションのコード (app.js)
“`javascript
// fullstack-app/app.js
const express = require(‘express’);
const { Pool } = require(‘pg’);
const app = express();
const port = 3000;
// PostgreSQLへの接続設定
// 環境変数から接続情報を取得する
const pool = new Pool({
user: process.env.POSTGRES_USER,
host: process.env.POSTGRES_HOST, // ここが重要!サービス名をホスト名として指定
database: process.env.POSTGRES_DB,
password: process.env.POSTGRES_PASSWORD,
port: 5432,
});
app.get(‘/’, async (req, res) => {
try {
const client = await pool.connect();
const result = await client.query(‘SELECT version()’);
const dbVersion = result.rows[0].version;
client.release();
res.send(<h1>Hello from Node.js!</h1><p>Connected to PostgreSQL. DB Version: ${dbVersion}</p>);
} catch (err) {
console.error(err);
res.status(500).send(‘Error connecting to the database’);
}
});
app.listen(port, () => {
console.log(App listening at http://localhost:${port});
});
``host: process.env.POSTGRES_HOST
**ポイント:**の部分です。後ほどdocker-compose.ymlで定義するdb`というサービス名を、そのままホスト名として利用できるのがDocker Composeのネットワーク機能の強力な点です。
2. 依存パッケージの定義 (package.json)
“`json
// fullstack-app/package.json
{
“name”: “fullstack-app”,
“version”: “1.0.0”,
“description”: “”,
“main”: “app.js”,
“scripts”: {
“start”: “node app.js”
},
“dependencies”: {
“express”: “^4.17.1”,
“pg”: “^8.7.1”
}
}
“`
3. Node.jsアプリ用のDockerfile (Dockerfile)
今回はnginxのように既存のイメージを使うのではなく、自作のアプリケーションを動かすため、Dockerfileでカスタムイメージをビルドします。
“`dockerfile
fullstack-app/Dockerfile
ベースイメージとしてNode.jsの公式イメージを使用
FROM node:16-alpine
アプリケーションの作業ディレクトリを作成
WORKDIR /usr/src/app
package.jsonとpackage-lock.jsonをコピー
COPY package*.json ./
依存パッケージをインストール
RUN npm install
アプリケーションのソースコードをコピー
COPY . .
アプリケーションがリッスンするポートを公開
EXPOSE 3000
コンテナ起動時に実行するコマンド
CMD [ “npm”, “start” ]
“`
4. 環境変数を管理するファイル (.env)
パスワードなどの機密情報をdocker-compose.ymlに直接書き込むのはセキュリティ上好ましくありません。そこで、.envファイルに切り出して管理するのがベストプラクティスです。
“`ini
fullstack-app/.env
PostgreSQL設定
POSTGRES_DB=mydatabase
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
POSTGRES_HOST=db # データベースサービスのサービス名
``.env
**注意:**ファイルは.gitignore`に追加し、Gitリポジトリに含めないようにしてください。
ステップ2:docker-compose.ymlを作成する
全ての準備が整いました。fullstack-appディレクトリの直下に、docker-compose.ymlを以下の内容で作成します。
“`yaml
fullstack-app/docker-compose.yml
version: “3.8”
services:
# Node.js アプリケーションサービス
app:
build: .
ports:
– “3000:3000”
volumes:
– .:/usr/src/app
env_file:
– .env
depends_on:
– db
# PostgreSQL データベースサービス
db:
image: postgres:14-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
– postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
“`
これも一行ずつ、特に新しい部分を重点的に解説します。
-
services:- 今回は
appとdbという2つのサービスを定義しています。
- 今回は
-
appサービスbuild: .imageの代わりにbuildを使っています。これは、DockerイメージをDocker Hubから取得するのではなく、指定されたパスにあるDockerfileを使ってイメージをビルドすることを意味します。.はカレントディレクトリを指すので、同じ階層にあるDockerfileが使われます。
volumes: - .:/usr/src/app- カレントディレクトリ全体をコンテナの作業ディレクトリにマウントしています。これにより、ローカルで
app.jsを編集すると、コンテナを再ビルドすることなく即座にアプリケーションに反映されます(ただし、npm installが必要な変更の場合は再ビルドが必要です)。
- カレントディレクトリ全体をコンテナの作業ディレクトリにマウントしています。これにより、ローカルで
env_file: - .env- 指定したファイル(ここでは
.env)に書かれたキー=値のペアを、環境変数としてコンテナに読み込ませます。これにより、app.js内のprocess.env.POSTGRES_USERなどで値を取得できます。
- 指定したファイル(ここでは
depends_on: - db- 非常に重要な設定です。 これは、「
appサービスはdbサービスに依存している」ということをDocker Composeに伝えます。これにより、コンテナの起動順序が制御され、必ずdbコンテナが起動した後にappコンテナが起動するようになります。データベースが起動していないのにアプリケーションが接続しようとしてエラーになる、といった事態を防げます。
- 非常に重要な設定です。 これは、「
-
dbサービスimage: postgres:14-alpine- PostgreSQLの公式イメージを使用します。
:latestではなく、バージョン(14)と軽量版(-alpine)を明示的に指定するのが、再現性の観点から推奨されるプラクティスです。
- PostgreSQLの公式イメージを使用します。
environment:- コンテナに環境変数を直接渡す方法です。
POSTGRES_DB: ${POSTGRES_DB}:${...}という記法は、Docker Composeが.envファイル(またはホストの環境変数)から値を読み込んで展開してくれるものです。これにより、.envファイルで定義した値をpostgresイメージが初期設定として利用できます。
volumes: - postgres_data:/var/lib/postgresql/data:の左側がpostgres_dataという名前になっています。これは「名前付きボリューム」と呼ばれるものです。:の右側は、PostgreSQLが実際のデータベースファイルを保存するコンテナ内のパスです。
-
トップレベルの
volumes:postgres_data:: ここで「名前付きボリューム」postgres_dataを正式に定義しています。名前付きボリュームはDockerが管理する領域に作成され、データの永続化に推奨される方法です。dbコンテナをdocker-compose downで削除しても、このボリュームは(-vオプションをつけない限り)削除されず、次回upした時に同じデータが引き継がれます。
ステップ3:複数コンテナアプリケーションを実行する
ターミナルでfullstack-appディレクトリに移動し、以下のコマンドを実行します。
bash
$ docker-compose up --build -d
--build:docker-compose.yml内にbuildディレクティブがある場合、upの前に強制的にイメージの再ビルドを行います。コードやDockerfileを変更した際は、このオプションをつけるのが確実です。
dbコンテナが先に起動し、次にappコンテナがビルドされ、起動する様子が確認できます。
ステップ4:動作確認
Webブラウザで http://localhost:3000 にアクセスしてください。
「Hello from Node.js!」というメッセージと共に、PostgreSQLのバージョン情報が表示されれば、複数コンテナの連携は見事成功です!
ステップ5:ログの確認と後片付け
何か問題が起きた場合は、ログを確認するのが基本です。
“`bash
全サービスのログを表示
$ docker-compose logs
特定のサービス(例: app)のログだけを表示
$ docker-compose logs app
ログをリアルタイムで追跡する
$ docker-compose logs -f app
“`
後片付けは同じくdownコマンドです。今回は名前付きボリュームも作成したので、それも一緒に削除してみましょう。
“`bash
コンテナ、ネットワーク、そして名前付きボリュームを全て削除
$ docker-compose down -v
``-vオプションをつけると、docker-compose.yml`で定義された名前付きボリュームも削除されます。データベースを完全に初期化したい場合などに使用します。
第5章:よく使う主要なキーとオプションリファレンス
ここまでで、基本的なdocker-compose.ymlの書き方はマスターできたはずです。この章では、より高度な設定を行うために、よく使われるキーとオプションをリファレンスとしてまとめます。
サービス (services) の下で使うキー
-
image: <イメージ名>:<タグ>- コンテナの元となるDockerイメージを指定します。
- 例:
image: redis:6-alpine
-
build: <パス>orbuild: { context: <パス>, dockerfile: <Dockerfile名> }- Dockerfileからイメージをビルドします。
- 単純な場合:
build: . - Dockerfileが別の場所にある場合:
build: { context: ./backend, dockerfile: Dockerfile.dev }
-
ports: - "<ホストポート>:<コンテナポート>"- ポートフォワーディングを設定します。
- 例:
ports: - "8000:8000"
-
volumes: - "<ホストパス or 名前付きボリューム>:<コンテナパス>"- ボリュームをマウントします。
- バインドマウント:
volumes: - ./src:/app/src - 名前付きボリューム:
volumes: - db_data:/var/lib/mysql - 読み取り専用:
volumes: - ./config.json:/app/config.json:ro
-
environment: <キー: 値>or- <キー=値>- 環境変数を設定します。
- マップ形式:
environment: { NODE_ENV: development, PORT: 3000 } - リスト形式:
environment: [ "NODE_ENV=development", "PORT=3000" ]
-
env_file: <ファイルパス>or- <ファイルパス>- 環境変数ファイルを読み込みます。
- 例:
env_file: .env.development
-
depends_on: - <サービス名>- サービスの起動順序を定義します。
- 例:
depends_on: - redis - db
-
command: <コマンド>- Dockerfileで指定されたデフォルトのコンテナ起動コマンド(
CMDやENTRYPOINT)を上書きします。 - 例:
command: python manage.py runserver 0.0.0.0:8000 - シェル形式:
command: bundle exec rails s -p 3000 -b '0.0.0.0'
- Dockerfileで指定されたデフォルトのコンテナ起動コマンド(
-
restart: <ポリシー>- コンテナが停止した際の再起動ポリシーを定義します。
no: 再起動しない(デフォルト)。always: 常に再起動する。on-failure: 異常終了(終了コードが0以外)した場合のみ再起動する。unless-stopped: 明示的に停止されるまで常に再起動する。- 本番環境では
alwaysやunless-stoppedがよく使われます。 - 例:
restart: unless-stopped
-
networks: - <ネットワーク名>- サービスを接続するネットワークを指定します。トップレベルの
networksでカスタムネットワークを定義した場合に使います。 - 例:
networks: - frontend - backend
- サービスを接続するネットワークを指定します。トップレベルの
-
healthcheck:- コンテナが正常に動作しているかをチェックする方法を定義します。
depends_onがコンテナの起動しか待たないのに対し、healthcheckはアプリケーションが本当に準備完了になるまで待つ、より堅牢な依存関係を構築できます(設定はやや高度になります)。 - 例(PostgreSQL):
yaml
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydatabase"]
interval: 10s
timeout: 5s
retries: 5
- コンテナが正常に動作しているかをチェックする方法を定義します。
トップレベルのキー
-
networks: <ネットワーク名>:- カスタムネットワークを定義します。
- 例:
yaml
networks:
my_custom_network:
driver: bridge
-
volumes: <ボリューム名>:- 名前付きボリュームを定義します。
- 例:
volumes: { redis_data: {}, mysql_data: {} }
第6章:ベストプラクティスとヒント
最後に、よりプロフェッショナルなdocker-compose.ymlを書くためのベストプラクティスをいくつか紹介します。
-
機密情報は
.envファイルに分離する- APIキーやパスワードなどを
docker-compose.ymlに直接書き込まず、.envファイルを活用しましょう。そして、.envは必ず.gitignoreに追加してください。
- APIキーやパスワードなどを
-
データ永続化には「名前付きボリューム」を使う
- ソースコードの同期のような開発時の利便性を目的とする場合はバインドマウント(
./src:/appなど)が便利です。 - しかし、データベースのデータなど、アプリケーションが生成するデータを永続化する場合は、Dockerが管理してくれる「名前付きボリューム」を使いましょう。パフォーマンスや権限管理の面で優れています。
- ソースコードの同期のような開発時の利便性を目的とする場合はバインドマウント(
-
イメージタグは
latestを避けるimage: postgres:latestのようにlatestタグを使うと、いつの間にかメジャーバージョンが上がってしまい、予期せぬ互換性の問題を引き起こす可能性があります。postgres:14やpostgres:14.5-alpineのように、バージョンを明示的に指定することで、環境の再現性を高めましょう。
-
開発用と本番用で設定を分ける
- 開発環境ではソースコードのホットリロードやデバッグツールが必要ですが、本番環境では不要です。
docker-compose.yml(共通設定)とdocker-compose.override.yml(開発用設定の上書き)やdocker-compose.prod.yml(本番用設定)のようにファイルを分割して管理するのが一般的です。docker-composeはデフォルトでdocker-compose.ymlとdocker-compose.override.ymlを自動的に読み込んで設定をマージしてくれます。
- 開発環境ではソースコードのホットリロードやデバッグツールが必要ですが、本番環境では不要です。
-
depends_onの限界を理解するdepends_onはコンテナの起動順序を保証するだけで、コンテナ内のアプリケーションがリクエストを受け付けられる準備完了状態になるまで待ってくれるわけではありません。データベースは起動に時間がかかることがあり、appが接続しようとした時点ではまだ準備ができておらず、接続エラーになることがあります。- これを解決するには、
healthcheckを使うか、アプリケーション側でリトライ処理を実装する、あるいはwait-for-it.shのようなスクリプトを導入するなどの対策が必要です。
まとめ:Composeで快適な開発環境を
この記事では、Docker Composeとdocker-compose.ymlの基本から、実践的な複数コンテナアプリケーションの構築、そしてより良い開発のためのベストプラクティスまでを網羅的に解説しました。
最初は覚えることが多く感じるかもしれませんが、重要なポイントは限られています。
servicesが中心。コンテナの設計図を定義する。imageで既存イメージを、buildで自作イメージを使う。portsで外部に公開し、volumesでデータを永続化・同期する。environmentやenv_fileで設定を注入する。- コンテナ間通信はサービス名で行える。
depends_onで起動順序を制御する。
docker runの長いコマンドを叩いていた日々はもう終わりです。docker-compose.ymlという強力な「楽譜」を手に入れたあなたは、複雑なアプリケーション環境であっても、docker-compose upという「指揮棒」の一振りで、見事なハーモニーを奏でることができるようになったはずです。
ぜひ、あなたの次のプロジェクトからDocker Composeを導入し、その生産性の高さを実感してみてください。もう、docker-compose.ymlの書き方で迷うことはありません。快適なコンテナ開発ライフをお楽しみください!