nginx でリクエストを複製できるモジュール「ngx_http_mirror_module」を使うと,簡易的な「Shadow Proxy」を構築することができる.例えば,本番環境のリクエストの一部を開発環境に流せるようになる.この「ngx_http_mirror_module」は nginx 1.13.4 で実装された機能で,2017年8月リリースなので,最近のバージョンだとデフォルトで使えるようになっている.今回は「リクエストの複製」を試すため,Docker Compose を使って検証環境を構築した.
検証環境
今回 Docker Compose を使って,nginx と Sinatra を起動する検証環境を構築した.コンテナは計3種類で,以下の構成図にまとめた.基本は Frontend と Backend でリクエストを処理し,今回は Mirror にもリクエストを複製する構成にしている.ドキュメントにも記載されている通り,Mirror からのレスポンスは無視される仕様になっている.
- Frontend (nginx)
- Backend (Sinatra)
- Mirror (Sinatra)
特に設定はなく,すぐに docker-compose up
で起動できるようにした.今回エンドポイントは Sinatra で /
と /ping
を実装している.なお,Docker Compose の設定などは GitHub に公開した.
$ docker-compose up $ curl http://localhost Hello, backend! $ curl http://localhost/ping Pong, backend!
Frontend (nginx)
ドキュメントを参考に nginx.conf
に mirror
を設定した.リクエストは proxy_pass
で Backend に転送し,さらに複製されたリクエストは proxy_pass
で Mirror に転送される.ポイントは複製用の /mirror
ロケーションを内部用にしておくことと,転送先の URL に $request_uri
を追加する点で,ドキュメントにも記載されている.なお,デフォルトで mirror_request_body
が on
になっているため body
も転送される.
location / { mirror /mirror; proxy_pass http://backend; } location /mirror { internal; proxy_pass http://mirror$request_uri; }
動作確認
さっそく /
と /ping
にリクエストを投げる.
$ curl http://localhost Hello, backend! $ curl http://localhost/ping Pong, backend!
すると,Docker Compose のログに以下のようなアクセスログが出力される.今回 WEBrick のアクセスログは抑止しているため,nginx と Sinatra のアクセスログとなる.そして,期待通りに Backend と Mirror に転送されていることが確認できる.簡単にリクエストを複製できる「ngx_http_mirror_module」は便利だ!
mirror_1 | 172.22.0.4 - - [22/Jul/2019:13:17:44 +0000] "GET / HTTP/1.1" 200 14 0.0073 backend_1 | 172.22.0.4 - - [22/Jul/2019:13:17:44 +0000] "GET / HTTP/1.1" 200 15 0.0074 frontend_1 | 172.22.0.1 - - [22/Jul/2019:13:17:44 +0000] "GET / HTTP/1.1" 200 14 "-" "curl/7.54.0" "-" backend_1 | 172.22.0.4 - - [22/Jul/2019:13:18:20 +0000] "GET /ping HTTP/1.1" 200 14 0.0071 mirror_1 | 172.22.0.4 - - [22/Jul/2019:13:18:20 +0000] "GET /ping HTTP/1.1" 200 14 0.0118 frontend_1 | 172.22.0.1 - - [22/Jul/2019:13:18:20 +0000] "GET /ping HTTP/1.1" 200 14 "-" "curl/7.54.0" "-"
nginx : resolver 127.0.0.11
検証中にうまくリクエストを Mirror に転送できず,nginx から出力される no resolver defined to resolve mirror
というエラーに悩まされていた.実際に出力されていたエラーは以下となる.
frontend_1 | 2019/07/22 13:28:06 [error] 6#6: *3 no resolver defined to resolve mirror, client: 172.22.0.1, server: localhost, request: "GET / HTTP/1.1", subrequest: "/mirror", host: "localhost" frontend_1 | 2019/07/22 13:28:11 [error] 6#6: *1 no resolver defined to resolve mirror, client: 172.22.0.1, server: localhost, request: "GET /ping HTTP/1.1", subrequest: "/mirror", host: "localhost"
調査したところ,Docker の Embedded DNS に関係していることに気付き,今回は nginx.conf
に resolver
を指定して解決をした.Embedded DNS の詳細は以下のドキュメントに載っている.正直結構ハマった!
server { (中略) resolver 127.0.0.11; (中略) }
まとめ
nginx でリクエストを複製できるモジュール「ngx_http_mirror_module」を試した.簡易的な「Shadow Proxy」を構築することができる.今回は Docker Compose を使って検証環境を構築して,疎通確認をしたけど,トラフィックを増やした場合に複製による負荷がどの程度なのか,追加で検証してみたいと思う.