三つのアプリへの、一つの入り口
リバースプロキシは、アプリの前に立つサーバーです。訪問者のリクエストを受けて、アプリへ渡します。いくつものアプリに、一つの入り口、一つの記録、守る場所一つを与えます。
ある会社は、店、ブログ、社内ツールを動かしています。それぞれが別のポートで動く小さなプログラムで、どれも一つだけでインターネットに顔を出すべきではありません。
入り口を一つにして、訪問者を受け、正しいプログラムへ渡し、記録を一つだけ書きます。その入り口を、今日は一つ作ります。
このレッスンのゴール
メモ
アプリの前にnginxを置き、アプリに何が見えるかを説明できる。
重要な言葉
| ことば | いみ | れい |
|---|---|---|
| リバースプロキシ | 後ろにあるアプリへ、リクエストを渡すサーバー | |
| アップストリーム | プロキシがリクエストを渡す先のアプリ | |
| 負荷分散 | リクエストを、複数のサーバーに分けること | |
| 502 | プロキシがアプリから返事をもらえなかったことを表す番号 | Bad Gateway |
| X-Forwarded-For | 本当の訪問者のアドレスを運ぶヘッダー | |
| リロード | 動いているサーバーに、設定を読み直させること |
学生が入力するセルは、このプレビューでは空白です。
リバースプロキシはアプリの代わりに答える
訪問者はリバースプロキシと話します。プロキシは、アプリ、つまりアップストリームに聞き、答えを返します。訪問者がアプリに会うことはありません。
ここでは、アプリもnginx自身で作ります。127.0.0.1 のポート9000にあり、この機器からしか届きません。プロキシはポート8080です。それぞれが一つのファイルを使います。
ポート9000のアプリを書く
docker exec web sh -c "printf '%s\n' 'server {' ' listen 127.0.0.1:9000;' ' location / { return 200 \"アプリのサーバー: host=\$host forwarded=\$http_x_forwarded_for from=\$remote_addr\n\"; }' '}' > /etc/nginx/conf.d/app.conf"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ポート8080のプロキシを書く
docker exec web sh -c "printf '%s\n' 'server {' ' listen 8080;' ' location / {' ' proxy_pass http://127.0.0.1:9000;' ' proxy_set_header Host \$host;' ' proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;' ' }' '}' > /etc/nginx/conf.d/proxy.conf"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
使う前に設定を試す
docker exec web nginx -tここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
新しいファイルを読むようnginxをリロードする
docker exec web nginx -s reloadここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
プロキシ経由で聞く
curl -s http://web:8080/ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
プロキシを通さず、アプリに直接聞く
curl -sS -m 2 http://web:9000/; echo "exit code $?"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ヘッダーが、誰が頼んだかを伝える
アプリは、プロキシを送り主だと見ました。本当に誰が頼んだかを知らせるため、プロキシは訪問者のアドレスを、ヘッダーX-Forwarded-Forに書きます。
訪問者が入力した名前も、Host ヘッダーで渡します。その行が無いと、nginxは代わりにアップストリームの名前を送ります。
偽の送り主を名乗るリクエストを送る
curl -s -H 'X-Forwarded-For: 203.0.113.9' http://web:8080/ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
「X-Forwarded-Forの最初のアドレスは、いつも本当の訪問者だ」と考える人が多いのですが、訪問者はそのヘッダーを自分で書けます。信じてよいのは、自分のプロキシが足したアドレス、つまり一覧の最後のものだけです。
例: プロキシが渡すもの
| proxy.confの行 | アプリが知ること |
|---|---|
| proxy_set_header Host $host; | 訪問者が入力した名前。ここでは web |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | 一覧の最後に足された、訪問者のアドレス |
| Hostの行が無い | アップストリームの名前。例えば pool |
学生が入力するセルは、このプレビューでは空白です。
アプリが止まると502になる
アップストリームが答えないと、プロキシはページを取ってこられません。502、Bad Gatewayで答えます。プロキシは無事で、後ろのアプリが無事ではないという意味です。
一つの名前の後ろに二つのアプリを置くのが負荷分散です。nginxは順番にリクエストを渡すので、一つが落ちても、もう一つが答えます。
誰も待っていないポートへ向ける
docker exec web sh -c "printf '%s\n' 'server {' ' listen 8080;' ' location / { proxy_pass http://127.0.0.1:9009; }' '}' > /etc/nginx/conf.d/proxy.conf && nginx -t && nginx -s reload"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
プロキシ経由で聞き、ステータスを読む
curl -s -i http://web:8080/ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
失敗についてnginxが書いたものを読む
docker logs web 2>&1 | grep -F "Connection refused" | tail -n 1ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
一つの名前の後ろに二つのアプリを置く
docker exec web sh -c "printf '%s\n' 'upstream pool {' ' zone pool 64k;' ' server 127.0.0.1:9000;' ' server 127.0.0.1:9001;' '}' 'server {' ' listen 127.0.0.1:9001;' ' location / { return 200 \"二つ目のアプリのサーバー\n\"; }' '}' > /etc/nginx/conf.d/pool.conf && sed -i 's#127.0.0.1:9009#pool#' /etc/nginx/conf.d/proxy.conf && nginx -t && nginx -s reload"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
4回聞いて、順番に答えるのを見る
for i in 1 2 3 4; do curl -s http://web:8080/; doneここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
三つのファイルを消してリロードする
docker exec web sh -c "rm -f /etc/nginx/conf.d/app.conf /etc/nginx/conf.d/proxy.conf /etc/nginx/conf.d/pool.conf; nginx -s reload"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
発展: host=pool と出た答え
四つの答えをもう一度見ましょう。ある種類の答えは、host=web ではなく host=pool と言っています。
ヘッダーの表のどの行が、それを説明しますか。アプリがまた host=web と言うように、プロキシの location ブロックに足す一行を書きましょう。