Docker Composeを使う
Docker Compose を使うことで、複数のコンテナをまとめてあつかったり、コンテナを接続したりすることが簡単になります。
https://docs.docker.jp/compose/toc.html
Docker Composeの使い方
compose.yaml
というファイルに設定を書き、docker compose {操作} [オプション]
のようなコマンドを入力することで使えます。
下のファイルは前ページのdocker run
コマンドを再現する設定ファイルです。
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
とすることで、コンテナを一括で起動できます。
docker compose up
Ctrl+C
でコンテナを停止できますが、削除は行われません。
-d
オプションを追加することで、デタッチモードで起動でき、バックグラウンドでコンテナを実行できます。実際のサーバーなどで運用する場合はバックグラウンドで起動することになります。
コンテナを一括で停止・削除するためには、down
を実行します。
docker compose down
他の Docker Compose のコマンド
up
、down
以外によく使う Docker Compose のコマンドを紹介します。
docker compose logs [オプション] [サービス名]
コンテナからのログを出力して確認できます。サービスはcompose.yaml
のservices
以下に書かれているものを指します。サービス名を指定しない場合は全てのサービスからのログを出力します。
docker compose exec [オプション] {サービス名} {コマンド}
起動しているコンテナの中でコマンドを実行できます。例えば、docker compose exec app ls
とすると、app
コンテナの中でls
コマンドを実行し、その結果を出力できます。また、コマンドでsh
やbash
を指定すると、コンテナの中に入って普段のターミナルのようにコマンドを実行できます。
他にも様々なコマンドが存在します。docker compose
と実行するとコマンド一覧を確認できます。
基本演習(複数コンテナ)
下の条件を満たすようにcompose.yaml
を書きかえてみましょう。
- 2 つのコンテナを起動する。
- どちらも前のページで作った
Dockerfile
を用いる。 - 1 つ目のコンテナは
greeting1
という名前で、localhost:3000/greetingにアクセスすると「こんにちは」と表示される。 - 2 つ目のコンテナは
greeting2
という名前で、localhost:3001/greetingにアクセスすると「Hello」と表示される。
試してみる前にdocker compose down
を実行して既存のコンテナを削除しておきましょう。
答え
compose.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
を下のように書きます。
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 に来たリクエストのヘッダーのHost
がhello.local
のときにgreeting
コンテナの 8080 番ポートにリクエストを飛ばしているということだけ理解してください。
Docker Compose の設定を追加する
compose.yaml
をnginx
を使えるように書き換えます。
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
コマンドでリクエストを送ってみましょう。
curl -H "Host:hello.local" http://localhost:3000/greeting
本来はHost
ヘッダーにはドメイン名がのりますが、今回は都合により直接Host
ヘッダーを指定しています。 (DNS が解決できないので、サーバーに到達できないため。/etc/hosts
とかに書いてやるのでも良かったのですが、結構概念が難しいので今回は見送りました)
ikura-hamu@Laptop-hk:~/naro_server$ curl -H "Host:hello.local" http://localhost:3000/greeting
こんにちはikura-hamu@Laptop-hk:~/naro_server$
このように挨拶が表示されたら成功です。
今回の設定では、下のような流れでリクエストが処理されています。
reverse_proxy
コンテナが 80 番ポートでリクエストを受け取る。- nginx がリクエストの
Host
ヘッダーを見て、greeting
コンテナの 8080 番ポートにプロキシする。 greeting
コンテナはレスポンスをreverse_proxy
コンテナを通して返す。
絶対パスと相対パス
コンピューター内におけるファイルやフォルダの位置を表す情報を「パス」と言います。Linux や Mac ではスラッシュ/
区切りで階層構造を表していますが、この表し方には起点の選び方によって「絶対パス」と「相対パス」の 2 種類があります。
絶対パスは、階層構造の頂点(ルートディレクトリ)からのパスを表します。上のcompose.yaml
のvolumes
で指定しているコンテナ側のパスは、/etc/nginx/conf.d
で、一番最初の/
がルートディレクトリを指します。
相対パスは、現在見ているディレクトリ(カレントディレクトリ)からの相対的な位置を表します。カレントディレクトリはpwd
コマンドで確認できます。.
でカレントディレクトリ、..
で 1 つ上のディレクトリを指定できます。上のcompose.yaml
のvolumes
で指定しているホスト側のパスは./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」と返ってくる。
curl -H "Host:hello1.local" http://localhost:3000/greeting
curl -H "Host:hello2.local" http://localhost:3000/greeting
答え
naro_server/nginx/conf.d/greeting1.conf
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
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
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