リバースプロキシの役割を体感しよう

目次

はじめに

この記事は、リバースプロキシの動作イメージを固めることを目的としています。

そのために、ローカルにリバースプロキシを立てて実際に目で見て触っていきます。

以下のような悩みを持っているITエンジニアが対象です。

  • リバースプロキシがどんなものなのかわからない
  • リバースプロキシがどんなものなのかなんとなく知っているが動作イメージが湧かない
  • リバースプロキシを実際に触ってみたいがやり方がわからない

以下が触れる環境なら本記事で紹介するソースを動かせます。

  • Git
  • Docker Compose

今回作成する環境の概要は次の通りです。

全体像

今回作成する環境は次の通りです。

全体像

流れは次の通りです。

  1. クライアントは127.0.0.1:9000にアクセス
  2. ポートフォワーディングを設定しているのでリバースプロキシの80番ポートでHTTPリクエストを受け取る
  3. 8080番ポートで待ち受けているWebサーバに転送する
  4. クライアントにHTTPレスポンスが届く

環境構築

GitHubにリポジトリを作ったので以下コマンドを使ってクローンしてください。

git clone git@github.com:jnkmtsd/reverse-proxy-sample.git -b v0.1.0

リポジトリは以下です。

GitHub
Build software better, together GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over 420 million projects.

次に、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

ちなみにghcr.io/gihyodocker/echo:v0.1.0というイメージは「Docker/Kubernetes実践コンテナ開発入門」という書籍で使う練習用のコンテナイメージです。今回は学習用途なので使わせてもらいました。

リバースプロキシとは

動かす準備ができたところで、いったんリバースプロキシという概念について学んでおきましょう。

といっても、リバースプロキシの解説記事は数多くあるので、ここではおすすめの解説記事を紹介した上で重要な点をまとめる形式にします。

おすすめの解説記事は以下です。なので、まずは以下をご覧ください。

ネットアテスト
リバースプロキシとは? わかりやすく10分で解説 | ネットアテスト リバースプロキシとは?リバースプロキシについて解説します。ウェブ上の多くの通信を仲介するこのシステムの概念、その設置場所、他の類似のシステムとの主な違い、その一...

読みましたか?では、重要な点について言及していきましょう。二つあります。

第一に、リバースプロキシはサーバ側に配置される、という特徴があります。そこが(フォワード)プロキシとの大きな違いですね。

第二に、リバースプロキシを使うモチベーションです。具体的には以下です。

  • キャッシュ
  • 負荷分散
  • セキュリティ

これら三つについて実際に触って体感していきます。

リバースプロキシを使うモチベーション

今回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-CacheMISSなのでキャッシュは使われていません。

一つ目のWebサーバにアクセスログは一つ追加されたことも確認できます。

次に、再度リバースプロキシにアクセスしてみましょう。実行結果は以下です。

HTTPレスポンスヘッダーX-Nginx-CacheHITなのでキャッシュが使われています。

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.confproxy_pass http://backend;という設定がありましたが、ここのbackendupstream backendbackendと対応しています。

では、実際にアクセスしてキャッシュの効果を確認します。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サーバを攻撃することは非常に困難です。いるかどうかわからない相手を攻撃することの難しさは、深く語らずとも理解できると思います。

さいごに

冒頭に目的として掲げた、リバースプロキシの動作イメージを固めることができたなら嬉しいです。最後にまとめです。

  • リバースプロキシはサーバ側に配置される
  • リバースプロキシを使うことで以下メリットを享受できる
    • キャッシュの利用
    • 負荷分散の実現
    • セキュリティの強化

最後までご覧いただき、誠にありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

歴1年のプラットフォームエンジニアで、その前は7年程度ソフトウェアエンジニアをしていました。
HR系の大手SaaS企業に所属しています。
プラットフォームエンジニアリングに興味あり。

コメント

コメントする

目次