🕌

go + nginx でリバースプロキシを体験する

2023/07/17に公開

今回は go と nginx でリバースプロキシを構築してみてリバースプロキシを理解しようと思います。

リバースプロキシとは

リバースプロキシはユーザーのリクエストを受け取り、上位サーバーに転送する役割を持ちます。

リバースプロキシの役割は大きく2通りあります。

負荷分散のため

一つ目は負荷分散のためです。以下の3つのような役割を持ちます。

  • ロードバランス
  • コンテンツキャッシュ
  • HTTPS 通信の終端化

ロードバランスは複数のサーバーにリクエストを振り分けてアプリケーションサーバーの負荷を分散させます。
コンテンツキャッシュと HTTPS 通信の終端化はユーザーに近いサーバーで行うのが一般的とされているようです。

Web アプリケーションでのリバースプロキシ

2つ目が Web アプリケーションにおけるリバースプロキシです。
以下のような役割を持ちます。

  • 静的ファイルの配信
  • アクセス制限、不正なアクセスのフィルタリング
  • リクエストの置き換え

などです。
Web アプリケーションではなく Web サーバーで上記を担保すればアプリケーション側に実装する必要がなくなります。実装しなくて良いと言うことはバグや脆弱性がある可能性の減少、メンテナンスコストの減少などの恩恵があります。

なぜリバースプロキシが必要なのか?

前述したリバースプロキシの役割を見れば一目瞭然なのですが、負荷分散や Web アプリケーションではアプリケーション側の責務を減らすために web サーバー側でリクエストの置き換えやアクセス制限などを行うためです。

Web アプリケーションという括りで考えると、リクエストやレスポンスのバッファリングもあります。
ユーザーのリクエストが直接アプリケーションサーバーに行くと、アプリケーションサーバー時はレスポンスするまで処理をする必要がありますね。その間はスレッドをブロックし続けるので、他の処理を行うことができません。

プロキシサーバーがバッファリングを行うことでアプリケーションのブロックを最小化できるのと、他のリクエストもバッファリングすることで、同じ時間でより多くのリクエストを処理できるようになります。

今回の構成

https://github.com/UserKazun/go_proxy

使用技術

  • Docker:Docker version 23.0.5
    • docker-compose をで go と nginx のコンテナを作成します。
  • go:1.20
  • nginx version: nginx/1.25.1

docker-compose.yml

https://github.com/UserKazun/go_proxy/blob/main/docker-compose.yml

go と nginx のコンテナを作成します。

以下がそれぞれ Dockerfile です。
https://github.com/UserKazun/go_proxy/blob/main/Docker/app/Dockerfile
https://github.com/UserKazun/go_proxy/blob/main/Docker/nginx/Dockerfile

nginx の設定

https://github.com/UserKazun/go_proxy/blob/main/Docker/nginx/nginx.conf

今回は上記のような設定を行いました。
重要なのは16行目です。ここでプロキシ先を指定しています。
この指定がないと当たり前ですが転送する先が見つからずに構成として成立しません。

docker で環境構築する場合コンテナ名をドメインとして指定する必要があります。

27行目の設定はちゃんとアプリケーション側に転送できているかをチェックするためのログ出力です。

39行目で設定ファイルの読み込みをコメントアウトしているのは、今回はこの1ファイルで設定を完結させたかったからです。今回の構成の範囲では余計なファイルを読み込ませないためにコメントアウトしています。

go のコード

ここは本件のコアな部分ではないので、最小にしました。
https://github.com/UserKazun/go_proxy/blob/main/main.go

動作確認

では実際に up してアクセスしたいと思います。

まずはプロキシ経由でのアクセスで http://localhost:80 にアクセスして、Hello World が画面に出ることを確認します。

確認できましたね。この時コンテナ名で言うと web → app からプロキシされています。
このあとアプリケーション側の実装をどんどん進めて行く場合は nginx 側にバッファリングの設定やタイムアウトの設定を行いアプリケーション側での実装が必要ないようにします。

ここで nginx の設定に行った upstream のログを確認してみます。

.
.
.
log_format  upstreamlog '[$time_local] $remote_addr $host $upstream_addr "$request"';
.
.
.

remote_addr から upstream_addr にリクエストが流れていることがわかります。

一応アプリケーションサーバーへの直リクエストもやってみます。
http://localhost:8080 にアクセスします。こちらも結果変わらず Hello World が表示されるはずです。

しかし、先ほどの upstream_log を確認してみるとログが増えていないことがわかると思います。これはアプリケーションに直アクセスしたからですね。

最後に

このようにアプリケーションサーバーの前段にサーバーをおいてプロキシしてもらうことで色々な恩恵が得られることがわかりました。
今回は単純にプロキシしただけなので、もっと設定を追記してバッファリングについてもみてみたいなと思います。

参考

https://gihyo.jp/book/2016/978-4-7741-7866-0

Discussion