PAK86_kushideyakaretadango20130814

作って覚えるリバースプロキシ

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存

今回はリバースプロキシについて勉強してみました。
「リバースプロキシってなんなのさ」という方は一緒に手を動かしながらご覧ください。

なお、今回の目的はあくまで「プロキシサーバとはなにかを理解すること」なので、実際に運用するときに大事な設定をすっ飛ばしたりしています。
そのあたりの詳細の理解については参考リンクなどをもとにして深めていただければと思います。

まず、「リバースプロキシ」とはなにか

ひとまず愚直にWikipediaで調べてみましょう。

リバースプロキシ(英: Reverse proxy)または逆プロキシは、特定のサーバへの要求を必ず経由するように設置されたプロキシサーバ。一般的なプロキシとは異なり不特定多数のサーバを対象としない。リバースプロキシは、不特定多数のクライアントから寄せられる要求に対して、応答を肩代わりすることにより特定のサーバの負担を軽減したり、アクセスを制限することにより特定のサーバのセキュリティを高めたりする目的に用いられる。

参考:リバースプロキシ – Wikipedia

一般的なプロキシとリバースプロキシとの違いに着目すると理解がしやすいです。

一般的なプロキシが、クライアントからプロキシサーバに要求することで任意の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.htmlHello, 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.ymllinksで設定をしておいたので、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とかいろいろ端折っていますが、図にしてみるとこんな感じの構成ができあがりました。

revpro2

さて、それではちゃんとリバースプロキシの設定ができたかどうか確認してみましょう。
docker-compose restartでコンテナの再起動をして設定を反映させた後、先ほどと同じようにWebブラウザから80番ポートにアクセスしてみます。

画面にnginxコンテナのindex.htmlHello, 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_pathproxy_temp_pathはそれぞれキャッシュと一時ファイルで使用するディレクトリを指定するディレクティブです。
キャッシュファイルを作成する際、いったん一時ファイルを作成するのでどちらも適当な値を指定してください。

そして同じくproxyコンテナの、80番ポート設定ファイル(default.conf)のlocationブロックに三行追加します。

proxy_cache proxy;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;

proxy_cacheのパラメータは共有メモリ領域の定義です。値はなんでも構いませんが、nginx.confproxy_cache_path keys_zoneで指定した値を使用してください。

proxy_cache_validはHTTPステータスコードに応じたキャッシュ時間を設定します。

以上の設定ができたらコンテナを再起動し、docker execコマンドでproxyコンテナの中を確認してみましょう。
ブラウザでアクセスした後にproxy_cache_pathで設定したディレクトリの中身を確認すると、キャッシュファイルが生成されているはずです。

このキャッシュファイルが存在している間、リバースプロキシはクライアントからリクエストを受け取ったときにわざわざWebサーバにリクエストを転送せずに、キャッシュファイルを使用してレスポンスを返してくれます。

ちなみに、Nginxのキャッシュ設定についてもまだまだ設定項目があるので、詳しいことはしっかりマニュアルなどで確認しましょう。

参考:
ngx_http_proxy_module モジュール 日本語訳

ロードバランサとして利用する

ロードバランサとは、外部から送られてくるデータや処理要求を、同等に機能する複数の装置に振り分けて一台あたりの負荷を抑える装置。単にロードバランサという場合は、ネットワーク上でサーバの負荷を分散するサーバロードバランサを指すことが多い。

参考:ロードバランサとは|負荷分散装置|サーバロードバランサ|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と命名することができます。
設定したmyappdefault.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アドレスに基づいてサーバを割り当てたりすることもできます。詳しくは下記のマニュアルに目を通してみてください。

参考:
nginxをHTTPロードバランサのように使う 日本語訳

SSLオフローダとして利用する

記事の本旨とずれてしまいそうなので今回は紹介にとどめておきますが、SSLを高速化するSSLオフローダとして利用することができます。
SSL通信をするにあたり、サーバは暗号化をする必要があるのですが、こうした処理をリバースプロキシが肩代わりすることでWebサーバをSSL処理にかかわる負荷から解放させます。

おわりに

いかがでしたでしょうか。

リバースプロキシは、しくみ自体はWebサーバのリクエストを肩代わりするという単純なものですが、サーバ構成を考えるうえでは主に負荷軽減の面で鍵を握る、かなり重要な存在のようです。

それにしても、「リバースプロキシ」という単語自体はそこかしこで聞きつつも、どんなものかがわからず勝手に苦手意識を持っていたのですが、Nginxの設定項目を書き換えるだけでほとんどの機能が使えてしまうので予想以上に簡単でした。

やっぱり何事も変に怖気づかず、とりあえず実際に手を動かしてやってみるのが一番ですね。
以上、今回はリバースプロキシについて取り上げてみました。

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存