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 upCtrl+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.dreverse_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/greetingcurl -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