はじめに
この記事は、リバースプロキシの動作イメージを固めることを目的としています。
そのために、ローカルにリバースプロキシを立てて実際に目で見て触っていきます。
以下のような悩みを持っているITエンジニアが対象です。
- リバースプロキシがどんなものなのかわからない
- リバースプロキシがどんなものなのかなんとなく知っているが動作イメージが湧かない
- リバースプロキシを実際に触ってみたいがやり方がわからない
以下が触れる環境なら本記事で紹介するソースを動かせます。
- Git
- Docker Compose
今回作成する環境の概要は次の通りです。
全体像
今回作成する環境は次の通りです。
流れは次の通りです。
- クライアントは
127.0.0.1:9000
にアクセス - ポートフォワーディングを設定しているのでリバースプロキシの80番ポートでHTTPリクエストを受け取る
- 8080番ポートで待ち受けているWebサーバに転送する
- クライアントにHTTPレスポンスが届く
環境構築
GitHubにリポジトリを作ったので以下コマンドを使ってクローンしてください。
git clone git@github.com:jnkmtsd/reverse-proxy-sample.git -b v0.1.0
リポジトリは以下です。
次に、Dockerコンテナを立ち上げましょう。
cd reverse-proxy-sample
docker compose up -d
一応問題なく立ち上がっているかdocker compose ps
で確認しましょう。
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
reverse-proxy-sample-backend1-1 ghcr.io/gihyodocker/echo:v0.1.0 "go run main.go" backend1 9 seconds ago Up 7 seconds
reverse-proxy-sample-backend2-1 ghcr.io/gihyodocker/echo:v0.1.0 "go run main.go" backend2 9 seconds ago Up 7 seconds
reverse-proxy-sample-nginx-1 reverse-proxy-sample-nginx "/docker-entrypoint.…" nginx 9 seconds ago Up 7 seconds 0.0.0.0:9000->80/tcp
リバースプロキシとは
動かす準備ができたところで、いったんリバースプロキシという概念について学んでおきましょう。
といっても、リバースプロキシの解説記事は数多くあるので、ここではおすすめの解説記事を紹介した上で重要な点をまとめる形式にします。
おすすめの解説記事は以下です。なので、まずは以下をご覧ください。
読みましたか?では、重要な点について言及していきましょう。二つあります。
第一に、リバースプロキシはサーバ側に配置される、という特徴があります。そこが(フォワード)プロキシとの大きな違いですね。
第二に、リバースプロキシを使うモチベーションです。具体的には以下です。
- キャッシュ
- 負荷分散
- セキュリティ
これら三つについて実際に触って体感していきます。
リバースプロキシを使うモチベーション
今回Nginxをリバースプロキシとして使います。なので、Nginxの設定ファイルをお見せした上で、実際に動かして動作イメージを固めていきます。ただし、Nginxの解説記事ではないので、Nginxについて詳細に触れることはしません。この記事の主題には影響しません。
キャッシュ
キャッシュを使うことで、バックエンドのサーバに負荷をかけることなくクライアントに必要な情報を返すことができます。では、キャッシュに関連する設定ファイルを見ていきましょう。reverse-proxy-sample
からの相対パスはnginx/etc/nginx/conf.d/backend.conf
です。
proxy_cache_path /var/cache/nginx keys_zone=one:1m max_size=1g;
proxy_temp_path /var/cache/nginx_tmp;
server {
listen 80;
server_name nginx.jnkmtsd.local;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_cache one;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 10m;
add_header X-Nginx-Cache $upstream_cache_status;
access_log /dev/stdout main;
error_log /dev/stderr;
}
}
proxy_cache_path
でキャッシュの基本的な設定を明示して、proxy_cache
でその使用を宣言します。
では、実際にアクセスしてキャッシュの効果を確認します。ターミナルを三つ開きます。それぞれの目的と実行するコマンドは次の通りです。コマンドは上から順番に実行します。
目的 | コマンド |
---|---|
一つ目のWebサーバのログを表示 | docker compose logs -f backend1 |
二つ目のWebサーバのログを表示 | docker compose logs -f backend2 |
リバースプロキシにアクセス | curl -i -H 'Host: nginx.jnkmtsd.local' 127.0.0.1:9000 |
まずは、Webサーバのログを表示した上で、リバースプロキシにアクセスしてみましょう。実行結果は以下です。
タイムゾーンが日本ではないので平日の日中にサンプルアプリを動かしているわけではないよ。その時間はちゃんと仕事しているよ。
HTTPレスポンスヘッダーX-Nginx-Cache
がMISS
なのでキャッシュは使われていません。
一つ目のWebサーバにアクセスログは一つ追加されたことも確認できます。
次に、再度リバースプロキシにアクセスしてみましょう。実行結果は以下です。
HTTPレスポンスヘッダーX-Nginx-Cache
がHIT
なのでキャッシュが使われています。
HTTPレスポンスが返ってきているものの、Webサーバのアクセスログは一つも増えていないことがわかります。Webサーバは一切働かずともクライアントにHTTPレスポンスを返せています。これがキャッシュの力です。数多くのHTTPリクエストが飛んでくるサーバにおいて、キャッシュの効果は時に絶大です。
負荷分散
負荷分散によって、一台のWebサーバで捌ききれないHTTPリクエストが飛んできてもサービス継続することができます。では、負荷分散に関連する設定ファイルを見ていきましょう。reverse-proxy-sample
からの相対パスはnginx/etc/nginx/conf.d/upstream.conf
です。
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=10s;
server backend2:8080 max_fails=3 fail_timeout=10s;
}
この設定で、二つのWebサーバにアクセスを振り分けることができます。先ほどのbackend.conf
でproxy_pass http://backend;
という設定がありましたが、ここのbackend
がupstream backend
のbackend
と対応しています。
では、実際にアクセスしてキャッシュの効果を確認します。Webサーバのログ表示のターミナルは何もいじらず、curl
コマンドを少し変更した上で実行します。具体的には以下のようなコマンドを実行します。
curl -i -H 'Host: nginx.jnkmtsd.local' "127.0.0.1:9000?$(date +%s)"
クエリストリングスを実行タイミングで異なるようにすることで、キャッシュを効かないようにしています。負荷分散の動作を確認するのにWebサーバにアクセスを届ける必要があるからです。
上記curl
コマンドを10回ほど実行します。その結果が以下です。
見てわかる通り、二つのWebサーバにアクセスが振り分けられていることがわかります。負荷分散する装置はロードバランサーと呼ばれます。リバースプロキシとは別物なのですが、今回リバースプロキシはロードバランサーの役割も担っています。
セキュリティ
リバースプロキシのバックエンドにいるサーバを隠蔽することで、セキュリティ強化というメリットを享受することができます。クライアントから見たら、今回構築したサーバ群は以下のように見えています。
127.0.0.1:9000
というIPアドレスとポート番号が見えているだけです。その中身がどうなっているのかは確認のしようがありません。
今回私たちはDocker Composeで、一つのリバースプロキシと二つのWebサーバを構築したことを知っています。しかし、実際は外部の人間にはそのことについて知りようがありません。
HTTPレスポンスヘッダーを見ても、せいぜいNginxが使われているというくらいしかわからないでしょう。
HTTP/1.1 200 OK
Server: nginx/1.27.0
Date: Sat, 10 Aug 2024 13:38:37 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 17
Connection: keep-alive
X-Nginx-Cache: MISS
このことから、背後に「いるかもしれない」WebサーバやDBサーバを攻撃することは非常に困難です。いるかどうかわからない相手を攻撃することの難しさは、深く語らずとも理解できると思います。
さいごに
冒頭に目的として掲げた、リバースプロキシの動作イメージを固めることができたなら嬉しいです。最後にまとめです。
- リバースプロキシはサーバ側に配置される
- リバースプロキシを使うことで以下メリットを享受できる
- キャッシュの利用
- 負荷分散の実現
- セキュリティの強化
最後までご覧いただき、誠にありがとうございました。
コメント