今回はリバースプロキシについて勉強してみました。
「リバースプロキシってなんなのさ」という方は一緒に手を動かしながらご覧ください。
なお、今回の目的はあくまで「プロキシサーバとはなにかを理解すること」なので、実際に運用するときに大事な設定をすっ飛ばしたりしています。
そのあたりの詳細の理解については参考リンクなどをもとにして深めていただければと思います。
まず、「リバースプロキシ」とはなにか
ひとまず愚直にWikipediaで調べてみましょう。
リバースプロキシ(英: Reverse proxy)または逆プロキシは、特定のサーバへの要求を必ず経由するように設置されたプロキシサーバ。一般的なプロキシとは異なり不特定多数のサーバを対象としない。リバースプロキシは、不特定多数のクライアントから寄せられる要求に対して、応答を肩代わりすることにより特定のサーバの負担を軽減したり、アクセスを制限することにより特定のサーバのセキュリティを高めたりする目的に用いられる。
一般的なプロキシとリバースプロキシとの違いに着目すると理解がしやすいです。
一般的なプロキシが、クライアントからプロキシサーバに要求することで任意のWebサーバへのリクエストを肩代わりするのに対し、
リバースプロキシはWebサーバの前に設置しておくことでWebサーバに寄せられた不特定多数のリクエストを一旦代わりに受け取ったり、クライアントへのレスポンスを代わりに送ってくれたりします。
まだピンと来ない場合は以下の記事がわかりやすいのでおすすめです。
参考:
リバースプロキシ (reverse proxy)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
リバースプロキシを作ってみる
それでは早速、実際にリバースプロキシサーバを作ってみましょう。
環境が作りやすいのでDockerとdocker-composeを使用し、Nginxをリバースプロキシとして動作させてみます。
多少手間はかかりますが、一応Vagrantだけでも環境は作れるかと思います。
ですが、Dockerがよくわからない方は是非こちらの記事もご覧ください。
なぜNginxなのか
今回はリバースプロキシとWebサーバ両方にNginxを使用しています。
Apacheでもリバースプロキシの機能はありますが、単純なリクエストを大量に捌くのが得意というNginxの特徴をうまく活かせる点から、Nginxを採用する事例が多いようです。
今回はWebサーバにもNginxを使用しますが、複雑なアプリケーション処理はApacheで行い、NginxによるリバースプロキシがApacheにリクエストを転送するという構成が用いられることもあります。
Nginxコンテナの作成
今回はリバースプロキシの話なのでDocker周りはさらっと紹介するにとどめておきます。
こんな感じのdocker-compose.yml
を作成しておきました。
nginx: image: nginx:latest volumes: - /vagrant/reverseProxy/nginx/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/nginx/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/nginx/html:/usr/share/nginx/html proxy: image: nginx:latest volumes: - /vagrant/reverseProxy/proxy/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/proxy/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/proxy/html:/usr/share/nginx/html links: - nginx:nginx ports: - 80:80
ひとつがWebサーバとして機能するNginxコンテナ、もうひとつがリバースプロキシとして機能するNginxコンテナです。
ドキュメントルートのほかに、設定編集しやすいようNginxの設定ファイルをボリュームしています。
まずはわかりやすいように、nginxコンテナ、proxyコンテナそれぞれのドキュメントルート(/usr/share/nginx/html
)下に以下のようなindex.html
を作成しておきました。
Hello, Nginx Server!
Hello, Proxy Server!
docker-compose up -d
でコンテナを起動させたらWebブラウザからDockerホストに80番ポートでアクセスしてみましょう。
docker-compose.yml
のports設定でDockerホストの80番ポートとproxyコンテナの80番ポートが紐づいているので、先ほど作成したproxyコンテナのindex.html
(Hello, Proxy Server!
)が表示されているはずです。
プロキシサーバの設定
Nginxコンテナはデフォルトでは/etc/nginx/nginx.conf
で共通の設定、/etc/nginx/conf.d
ディレクトリ内でserverブロックごとの設定がされています。
また、/etc/nginx/conf.d
の中には80番ポートをlistenするデフォルトの設定が書かれたdefault.conf
があります。
それではNginxの設定を変更して、proxyコンテナにアクセスしたときにnginxコンテナの内容を表示させるようにしてみましょう。
まずはproxyコンテナのdefault.conf
で設定を行います。
server { listen 80; server_name localhost; location / { proxy_pass http://nginx:8080; root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
デフォルトの設定ほとんどそのままに、locationブロックにproxy_pass
ディレクティブを追加しています。
proxy_pass http://nginx:8080;
proxy_pass
ディレクティブを設定すると、指定したlocation
にアクセスした際にリクエストを任意のサーバに転送することができます。
今回の設定ではlisten
ディレクティブで80番ポートをlistenしているので、proxyコンテナに80番ポートへ送られたリクエストをすべてnginxコンテナの8080番ポートへ転送するようになります。
ちなみに、nginx
というホスト名が指し示すのはDocker内で割り当てられているnginxコンテナのプライベートIPです。
docker-compose.yml
のlinks
で設定をしておいたので、proxyコンテナ側の/etc/hosts
にホスト名が登録されています。
続いてはngixコンテナ側に、8080番ポートで待ち受ける設定を作成しましょう。
nginxコンテナの/etc/nginx/conf.d/
下に、プロキシ用のproxy.conf
を作成します。
server { listen 8080; root /usr/share/nginx/html; location / { } }
8080番ポートでアクセスした場合の最低限の設定を記述してみました。
これでnginxコンテナは8080番ポートからのアクセスを待ち受けることができるようになったはずです。
それから、nginxコンテナには80番ポートからアクセスしない想定なので、80番ポートの設定を記述したdefault.conf
が不要になります。
勉強用に作ったコンテナですし、80番ポートで来られても困るのでさくっと削除してしまいましょう。
Vagrantとかいろいろ端折っていますが、図にしてみるとこんな感じの構成ができあがりました。
さて、それではちゃんとリバースプロキシの設定ができたかどうか確認してみましょう。
docker-compose restart
でコンテナの再起動をして設定を反映させた後、先ほどと同じようにWebブラウザから80番ポートにアクセスしてみます。
画面にnginxコンテナのindex.html
(Hello, Nginx Server!
)が表示されていたら見事リバースプロキシの完成です!
proxyコンテナに送られたリクエストがnginxコンテナに転送されているのを確認することができました。
参考:
nginx ドキュメント
リバースプロキシをどのように使うのか
Webサーバへのリクエストを肩代わりするだけのリバースプロキシを作成してみました。
しかしこのリバースプロキシ、いったいどのように活用すればいいのでしょうか。
キャッシュサーバとして利用する
Webサーバ内の画像などをリバースプロキシにキャッシュしておくことで、Webサーバへの負荷を軽減することができます。
nginxにはデフォルトでキャッシュ機能を備えているので、簡単な設定をするだけでキャッシュサーバにすることが可能です。
まずは先ほどのproxyコンテナの全体設定ファイル(nginx.conf
)のhttpブロックに以下の二行を追加しましょう。
proxy_cache_path /var/cache/nginx/cache keys_zone=proxy:15m; proxy_temp_path /var/cache/nginx/temp 1 2;
proxy_cache_path
とproxy_temp_path
はそれぞれキャッシュと一時ファイルで使用するディレクトリを指定するディレクティブです。
キャッシュファイルを作成する際、いったん一時ファイルを作成するのでどちらも適当な値を指定してください。
そして同じくproxyコンテナの、80番ポート設定ファイル(default.conf
)のlocationブロックに三行追加します。
proxy_cache proxy; proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m;
proxy_cache
のパラメータは共有メモリ領域の定義です。値はなんでも構いませんが、nginx.conf
のproxy_cache_path keys_zone
で指定した値を使用してください。
proxy_cache_valid
はHTTPステータスコードに応じたキャッシュ時間を設定します。
以上の設定ができたらコンテナを再起動し、docker exec
コマンドでproxyコンテナの中を確認してみましょう。
ブラウザでアクセスした後にproxy_cache_path
で設定したディレクトリの中身を確認すると、キャッシュファイルが生成されているはずです。
このキャッシュファイルが存在している間、リバースプロキシはクライアントからリクエストを受け取ったときにわざわざWebサーバにリクエストを転送せずに、キャッシュファイルを使用してレスポンスを返してくれます。
ちなみに、Nginxのキャッシュ設定についてもまだまだ設定項目があるので、詳しいことはしっかりマニュアルなどで確認しましょう。
ロードバランサとして利用する
ロードバランサとは、外部から送られてくるデータや処理要求を、同等に機能する複数の装置に振り分けて一台あたりの負荷を抑える装置。単にロードバランサという場合は、ネットワーク上でサーバの負荷を分散するサーバロードバランサを指すことが多い。
参考:ロードバランサとは|負荷分散装置|サーバロードバランサ|SLB|サーバ負荷分散装置 – 意味/定義 : IT用語辞典
リバースプロキシがリクエストを待ち受け、その転送を複数のWebサーバに分散して行うことで負荷分散を実現することができます。
こちらも実際に試してみましょう。
まずは負荷分散を再現するために、nginxコンテナと同様のnginx2コンテナ, nginx3コンテナを作成します。
nginx: image: nginx:latest volumes: - /vagrant/reverseProxy/nginx/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/nginx/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/nginx/html:/usr/share/nginx/html nginx2: image: nginx:latest volumes: - /vagrant/reverseProxy/nginx2/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/nginx2/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/nginx2/html:/usr/share/nginx/html nginx3: image: nginx:latest volumes: - /vagrant/reverseProxy/nginx3/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/nginx3/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/nginx3/html:/usr/share/nginx/html proxy: image: nginx:latest volumes: - /vagrant/reverseProxy/proxy/conf.d:/etc/nginx/conf.d - /vagrant/reverseProxy/proxy/nginx.conf:/etc/nginx/nginx.conf - /vagrant/reverseProxy/proxy/html:/usr/share/nginx/html links: - nginx:nginx - nginx2:nginx2 - nginx3:nginx3 ports: - 80:80
docker-compose.yml
はこんな感じになりました。proxyコンテナとnginx2, nginx3コンテナのコンテナ間リンクも設定してあります。
設定ファイルなども先ほどいじったnginxコンテナのものをそのままコピーしてきましょう。
ついでにわかりやすくするため、nginx2, nginx3コンテナのドキュメントルート下に新しくindex.html
を作成しておきます。
Hello, Nginx2 Server!
Hello, Nginx3 Server!
続いてロードバランサのためのNginxの設定を作っていきます。
まずはproxyコンテナの全体設定ファイル(nginx.conf
)のhttpブロックの中にupstream
ディレクティブを追加します。
upstream myapp { server nginx:8080; server nginx2:8080; server nginx3:8080; }
このようにしてupstream
ディレクティブの中にWebサーバ群のリストを記述し、myapp
と命名することができます。
設定したmyapp
はdefault.conf
の中でこのように使用します。
server { listen 80; server_name localhost; location / { proxy_pass http://myapp; proxy_redirect default; root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
proxy_pass
ディレクティブのところをhttp://myapp
にしました。
こうすることでリバースプロキシが先ほど指定したWebサーバ群からひとつのサーバを選んでリクエストを転送してくれます。
では、実際にdocker-compose up -d
でコンテナを起動し、Webブラウザからアクセスしてみましょう。
Hello, Nginx Server!
, Hello, Nginx2 Server!
, Hello, Nginx3 Server!
が順番に表示されていれば成功、ロードバランサの出来上がりです。
ちなみに今回はロードバランシングの方法を指定していないため、デフォルトのラウンドロビン(すべてのWebサーバに順に割り振られる)方式でしたが、
アクティブ接続数がもっとも少ないサーバに割り当てるようにしたり、クライアントのIPアドレスに基づいてサーバを割り当てたりすることもできます。詳しくは下記のマニュアルに目を通してみてください。
SSLオフローダとして利用する
記事の本旨とずれてしまいそうなので今回は紹介にとどめておきますが、SSLを高速化するSSLオフローダとして利用することができます。
SSL通信をするにあたり、サーバは暗号化をする必要があるのですが、こうした処理をリバースプロキシが肩代わりすることでWebサーバをSSL処理にかかわる負荷から解放させます。
おわりに
いかがでしたでしょうか。
リバースプロキシは、しくみ自体はWebサーバのリクエストを肩代わりするという単純なものですが、サーバ構成を考えるうえでは主に負荷軽減の面で鍵を握る、かなり重要な存在のようです。
それにしても、「リバースプロキシ」という単語自体はそこかしこで聞きつつも、どんなものかがわからず勝手に苦手意識を持っていたのですが、Nginxの設定項目を書き換えるだけでほとんどの機能が使えてしまうので予想以上に簡単でした。
やっぱり何事も変に怖気づかず、とりあえず実際に手を動かしてやってみるのが一番ですね。
以上、今回はリバースプロキシについて取り上げてみました。