🐧 Linux 総合学習プラットフォーム
コンテナ Docker/Podman ・ 中級

Docker Composeで複数コンテナ

実務のアプリはコンテナ1つでは完結せず、アプリ本体とデータベースのように複数のコンテナが組んで動くのが普通です。Docker Composeは、この「組」をYAML1枚で定義し、まとめて起動・停止できるようにする道具です。長いdocker runコマンドを何度も打つ生活から、宣言的な設定ファイルを読む・書く生活へ移る、実務コンテナ運用の入口になります。

アプリ本体だけが動いていても、実務ではそれで完結しないことが多い。裏にデータベースが必要だったり、キャッシュ用のコンテナが必要だったりする。

コンテナが2つ3つと増えていくと、docker run のコマンドをそれぞれ手で打つのはつらい。ネットワーク名・ポート番号・環境変数と、覚えることが増え、打ち間違いも増える。

つまずき長いdocker runコマンドをメモ帳にコピペして使い回している状態は、そろそろComposeへ移る合図だ。
🔗
たとえdocker runの手打ちは、注文のたびに材料を口頭で伝える料理と同じだ。Composeはレシピを1枚の紙に書いておくようなもので、同じ料理を毎回同じ手順で正確に再現できる。

📄 compose.yamlという1枚の設計図

Docker Composeは、複数のコンテナの構成をYAML形式の1つのファイルにまとめて書き、それをまとめて起動・停止できるようにする道具だ。ファイル名は慣習的にcompose.yaml(旧称docker-compose.yml)とする。

このファイルの中心はservicesという項目で、動かしたいコンテナを1つずつ「サービス」として定義していく。アプリ用のサービス、データベース用のサービスといった具合だ。

compose.yamlservices: web: db:1枚の設計図webコンテナアプリ本体dbコンテナデータベースまとめて起動

1つのサービスには、使うイメージ(imageまたはbuild)、公開するポート(ports)、永続化するボリューム(volumes)、渡す環境変数(environment)などを書いていく。

サービス定義の例。servicesの下にwebというサービスを作り、build: .でカレントのDockerfileを使う指定をし、ports: に 8080:80 のように公開ポートを書く。dbというサービスにはimage: postgres:16のように既製イメージを指定し、environment: に POSTGRES_PASSWORD のような値を渡す。
💡
ポイントservices配下の各項目は、いままでdocker runのオプションとして打っていたもの(-p, -v, -e など)に対応している。docker runの知識がそのままYAMLの語彙になる。

💾 volumesとportsの書き方

compose.yamlの中でも、volumesとportsはよく使う項目だ。volumesはホスト側のディレクトリやDocker管理下のボリュームをコンテナの中にマウントする指定で、データベースのデータを消えないようにするために欠かせない。

portsは、ホスト側のポート番号とコンテナ側のポート番号の対応をhost:container の形式で書く。この書き方はdocker run -pで指定するのと同じ意味を持つ。

コツvolumesの下でトップレベルにも名前付きボリュームを宣言しておくと、複数のサービスから同じボリュームを共有したり、docker compose downのあとも中身を残したりできる。

▶️ 起動・停止・ログの確認

compose.yamlを書いたら、そのファイルがあるディレクトリでdocker compose upを実行するとservicesに書いた全コンテナがまとめて起動する。バックグラウンドで動かしたいときは-dオプションを付ける。

止めるときはdocker compose downを使う。これはコンテナを停止するだけでなく、Composeが作ったネットワークなども一緒に片付けてくれる。個別のコンテナのログを流し続けて見たいときはdocker compose logs -fが便利だ。

よく使う一連の流れ。$ docker compose up -d でバックグラウンド起動、$ docker compose ps で起動状況を確認、$ docker compose logs -f web でwebサービスのログを流し見る、$ docker compose down で全部まとめて停止・削除。
つまずきdocker compose downは既定でボリュームまでは消さない。ボリュームの中身ごと消したいときは明示的に-vオプションを付ける必要があり、うっかり付けるとデータベースの中身も消えるので注意する。

🔗 依存順序——depends_on

データベースを使うアプリの場合、アプリのコンテナがデータベースのコンテナより先に起動してしまうと、接続エラーで落ちることがある。この起動順序を制御するのがdepends_onだ。

depends_onにサービス名を書いておくと、Composeはそのサービスを先に起動してから対象のサービスを起動する。ただし、これは「先にコンテナが起動し始める」順序を保証するだけで、「中のアプリ(例えばデータベース)が接続を受け付けられる状態になった」ことまでは保証しない点に注意がいる。

①db起動depends_on対象②web起動dbの後に開始接続失敗の可能性起動=準備完了ではない
つまずき本当に「準備完了」を待ちたい場合は、depends_onにcondition: service_healthyを組み合わせ、healthcheckで接続可能になったことを確認してから次を起動する設定が必要になる。単純なdepends_onだけでは起動直後の接続エラーが起こりうる。

🚀 コマンドの引数地獄からの卒業

ここまで見てきたように、Composeは「毎回長いdocker runの引数を打つ」という繰り返し作業を、「1枚のYAMLを読む・書く」という作業に置き換える。

🔗
たとえdocker runを並べて起動するのは、レシピを暗唱しながら料理するようなもの。compose.yamlはレシピをノートに書いておくようなもので、次に作るときも、他の人に渡すときも、同じ結果を再現しやすい。

複数のコンテナを組にして扱えるようになったら、次に気になるのは「コンテナ同士はどうやってお互いを見つけて話しているのか」という点だ。次のトピックではコンテナのネットワークの仕組みを見ていく。

この項目に出てくる用語

Docker Composeどっかーこんぽーず
複数コンテナの構成をYAML1枚で定義し、まとめて起動・停止できるようにする道具。
サービス(Compose)さーびす
compose.yamlのservices配下で定義する、1つのコンテナ構成の単位。

関連コマンド

docker compose

▶ 学習アプリでこの続きを学ぶ・演習する