Skip to content

Docker Composeを使う

Docker Compose を使うことで、複数のコンテナをまとめてあつかったり、コンテナを接続したりすることが簡単になります。

https://docs.docker.jp/compose/toc.html

Docker Composeの使い方

compose.yamlというファイルに設定を書き、docker compose {操作} [オプション]のようなコマンドを入力することで使えます。

下のファイルは前ページのdocker runコマンドを再現する設定ファイルです。

yaml
services:
  greeting:
    build: .
    environment: #docker run の -e
      GREETING_MESSAGE: こんにちは
      PORT: 8080
    ports: #docker run の -p
      - "127.0.0.1:3000:8080"

yaml ファイルは、インデントでオブジェクト(ひとまとまりの情報)を表現します。

services以下を増やしていくことで、複数のコンテナを一度に制御できます。

buildオプションでは、どのディレクトリの Dockerfile を使ってコンテナを起動するか指定できます。また、既にあるイメージからも起動できます。

起動する

compose.yamlが存在するディレクトリでdocker compose upとすることで、コンテナを一括で起動できます。

sh
docker compose up

Ctrl+Cでコンテナを停止できますが、削除は行われません。

-dオプションを追加することで、デタッチモードで起動でき、バックグラウンドでコンテナを実行できます。実際のサーバーなどで運用する場合はバックグラウンドで起動することになります。

コンテナを一括で停止・削除するためには、downを実行します。

sh
docker compose down

他の Docker Compose のコマンド

updown以外によく使う Docker Compose のコマンドを紹介します。

docker compose logs [オプション] [サービス名]

コンテナからのログを出力して確認できます。サービスはcompose.yamlservices以下に書かれているものを指します。サービス名を指定しない場合は全てのサービスからのログを出力します。

docker compose exec [オプション] {サービス名} {コマンド}

起動しているコンテナの中でコマンドを実行できます。例えば、docker compose exec app lsとすると、appコンテナの中でlsコマンドを実行し、その結果を出力できます。また、コマンドでshbashを指定すると、コンテナの中に入って普段のターミナルのようにコマンドを実行できます。


他にも様々なコマンドが存在します。docker composeと実行するとコマンド一覧を確認できます。

基本演習(複数コンテナ)

下の条件を満たすようにcompose.yamlを書きかえてみましょう。

  • 2 つのコンテナを起動する。
  • どちらも前のページで作った Dockerfileを用いる。
  • 1 つ目のコンテナはgreeting1という名前で、localhost:3000/greetingにアクセスすると「こんにちは」と表示される。
  • 2 つ目のコンテナはgreeting2という名前で、localhost:3001/greetingにアクセスすると「Hello」と表示される。

試してみる前にdocker compose downを実行して既存のコンテナを削除しておきましょう。

答え

compose.yaml

yaml
services:
  greeting1:
    build: .
    environment:
      GREETING_MESSAGE: こんにちは
      PORT: 8080
    ports:
      - "127.0.0.1:3000:8080"
  greeting2:
    build: .
    environment:
      GREETING_MESSAGE: Hello
      PORT: 8081
    ports:
      - "127.0.0.1:3001:8081"

コンテナ側のポート番号は、2 つのコンテナで異なっていればよいです。

バインドマウントを使う

バインドマウントという機能を使ってホストマシンのファイルやフォルダをコンテナにマウントする(ホストとコンテナでファイルやフォルダを結び付ける)ことができます。

https://docs.docker.jp/storage/bind-mounts.html

nginx の設定ファイルをバインドマウントして、リバースプロキシしてみましょう。

リバースプロキシとは、サーバーへのアクセスを受け取り、サーバーアプリケーションに振り分けて中継することです。リクエスト内容に応じて異なるサーバーアプリケーションにリクエストを振り分けることができるので、負荷の軽減につながります。

DockerHub にある nginx の公式イメージを使います。 https://hub.docker.com/_/nginx/

設定ファイルを書く

nginx の設定ファイルとして、./nginx/conf.d/greeting.conf を下のように書きます。

txt
server{
	server_name hello.local

	proxy_set_header    Host    $host;
	proxy_set_header    X-Real-IP    $remote_addr;
	proxy_set_header    X-Forwarded-Host       $host;
	proxy_set_header    X-Forwarded-Server    $host;
	proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

	location / {
		proxy_pass    http://greeting:8080; #greetingはコンテナ名
	}
}

今はファイルの意味がわからなくてもよいですが、 nginx に来たリクエストのヘッダーのHosthello.localのときにgreetingコンテナの 8080 番ポートにリクエストを飛ばしているということだけ理解してください。

Docker Compose の設定を追加する

compose.yamlnginxを使えるように書き換えます。

yaml
services:
  greeting:
    build: .
    environment:
      GREETING_MESSAGE: こんにちは
      PORT: 8080
  reverse_proxy:
    image: nginx
    ports:
      - "127.0.0.1:3000:80"
    volumes:
      - ./nginx/conf.d/:/etc/nginx/conf.d

reverse_proxyコンテナでnginxイメージを使って、volumesから設定ファイルのディレクトリをマウントしています。volumes{ホスト側のパス}:{コンテナ側のパス}のように、コロンで区切って指定します。

greetingコンテナは nginx 経由のアクセス経路を作ったので、ポートの開放設定を消しています。reverse_proxyコンテナのコンテナ側ポートが80なのは、http プロトコルのデフォルトのポートが80であり、 nginx もそれに従っているからです。

ここまでやったらコンテナを立ち上げてcurlコマンドでリクエストを送ってみましょう。

sh
curl -H "Host:hello.local" http://localhost:3000/greeting

本来はHostヘッダーにはドメイン名がのりますが、今回は都合により直接Hostヘッダーを指定しています。 (DNS が解決できないので、サーバーに到達できないため。/etc/hosts とかに書いてやるのでも良かったのですが、結構概念が難しいので今回は見送りました)

txt
ikura-hamu@Laptop-hk:~/naro_server$ curl -H "Host:hello.local" http://localhost:3000/greeting
こんにちはikura-hamu@Laptop-hk:~/naro_server$

このように挨拶が表示されたら成功です。

今回の設定では、下のような流れでリクエストが処理されています。

  1. reverse_proxyコンテナが 80 番ポートでリクエストを受け取る。
  2. nginx がリクエストのHostヘッダーを見て、greetingコンテナの 8080 番ポートにプロキシする。
  3. greetingコンテナはレスポンスをreverse_proxyコンテナを通して返す。

絶対パスと相対パス

コンピューター内におけるファイルやフォルダの位置を表す情報を「パス」と言います。Linux や Mac ではスラッシュ/区切りで階層構造を表していますが、この表し方には起点の選び方によって「絶対パス」と「相対パス」の 2 種類があります。

絶対パスは、階層構造の頂点(ルートディレクトリ)からのパスを表します。上のcompose.yamlvolumesで指定しているコンテナ側のパスは、/etc/nginx/conf.dで、一番最初の/がルートディレクトリを指します。

相対パスは、現在見ているディレクトリ(カレントディレクトリ)からの相対的な位置を表します。カレントディレクトリはpwdコマンドで確認できます。.でカレントディレクトリ、..で 1 つ上のディレクトリを指定できます。上のcompose.yamlvolumesで指定しているホスト側のパスは./nginx/conf.d/となっており、カレントディレクトリ(compose.yamlファイルが存在するディレクトリ)のnginxフォルダの中のconf.dフォルダのことです。

基本演習(リバースプロキシ)

下の条件を満たすように設定しましょう。compose.yamlを編集し、 nginx の設定ファイルを追加する必要があります。

  • http://localhost:3000で nginx がリクエストを受け付ける。
  • Hostヘッダーがhello1.localであれば、greeting1コンテナにリクエストを飛ばし、/greetingに対して「こんにちは」と返ってくる。
  • Hostヘッダーがhello2.localであれば、greeting2コンテナにリクエストを飛ばし、/greetingに対して「Hello」と返ってくる。
sh
curl -H "Host:hello1.local" http://localhost:3000/greeting
sh
curl -H "Host:hello2.local" http://localhost:3000/greeting
答え

naro_server/nginx/conf.d/greeting1.conf

txt
server{
	server_name hello1.local

	proxy_set_header    Host    $host;
	proxy_set_header    X-Real-IP    $remote_addr;
	proxy_set_header    X-Forwarded-Host       $host;
	proxy_set_header    X-Forwarded-Server    $host;
	proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

	location / {
		proxy_pass    http://greeting1:8080;
	}
}

naro_server/nginx/conf.d/greeting2.conf

txt
server{
	server_name hello2.local

	proxy_set_header    Host    $host;
	proxy_set_header    X-Real-IP    $remote_addr;
	proxy_set_header    X-Forwarded-Host       $host;
	proxy_set_header    X-Forwarded-Server    $host;
	proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

	location / {
		proxy_pass    http://greeting2:8080;
	}
}

naro_server/compose.yaml

yaml
services:
  greeting1:
    build: .
    environment:
      GREETING_MESSAGE: こんにちは
      PORT: 8080
  greeting2:
    build: .
    environment:
      GREETING_MESSAGE: Hello
      PORT: 8080
  reverse_proxy:
    image: nginx
    ports:
      - "127.0.0.1:3000:80"
    volumes:
      - ./nginx/conf.d/:/etc/nginx/conf.d