nginxリバースプロキシを構成する
目標
nginxをリバースプロキシとして構成し、転送ヘッダー・タイムアウト・ボディサイズ・圧縮・静的ファイルの分離・アクセス遮断・ログフォーマットを、実務の基準で設定できるようになります。
なぜ重要なのか
プロキシ設定の6行は、暗記するのではなく、ないときに何が起きるかで記憶する必要があります。Hostを渡さないと、アプリケーションが作る絶対URLが127.0.0.1:8080になり、決済コールバックが戻ってきません。X-Forwarded-Forがないと、接続履歴がすべてプロキシIPなので、監査で答える言葉がありません。X-Forwarded-Protoがないと、リダイレクトの無限ループが起きます。そしてclient_max_body_sizeのデフォルト1MBを知らないと、バックエンドのログに何も残らないファイルアップロード障害で、何日も費やします。
ステップ
- 準備: Tomcatを起動して、
/opt/lab/samples/labhub-1.0.0.warを/opt/tomcat/webapps/labhub.warにデプロイしておいてください。nginxの作業ディレクトリは/root/ngを使います。 /root/ng/nginx.confを作成して、nginxを起動します。ポート8088でlistenし、/をhttp://127.0.0.1:8080にプロキシします。http://127.0.0.1:8088/labhub/versionが200である必要があります。- プロキシヘッダーを4つ設定して、reloadします。
Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Protoです。確認:curl -s http://127.0.0.1:8088/labhub/echoのJSONに、x-forwarded-forとx-forwarded-protoがあり、hostの値が127.0.0.1:8080でない必要があります。 proxy_connect_timeout 3s、proxy_send_timeout 30s、proxy_read_timeout 60sを設定します。client_max_body_sizeを20mに設定して、reloadします。5MBのボディのPOSTは413ではなく、25MBのボディのPOSTは413である必要があります。gzip on、gzip_min_length 1000、gzip_vary onを設定して、gzip_typesにapplication/jsonを含めます。curl -H 'Accept-Encoding: gzip' -I http://127.0.0.1:8088/labhub/big.htmlのレスポンスに、Content-Encoding: gzipがある必要があります。/root/ng/static/app.jsファイルを作成し(内容は自由)、/assets/パスでサービスされるように設定します。つまり、http://127.0.0.1:8088/assets/app.jsが200である必要があり、レスポンスヘッダーにExpiresまたはCache-Controlがある必要があります。/manager/で始まるすべてのリクエストが、403を返すように設定します。http://127.0.0.1:8088/manager/htmlが403である必要があります。log_formatを定義して、$upstream_addr、$upstream_status、$upstream_response_time、$request_timeの4つの変数を含め、アクセスログを/root/ng/logs/access.logに残します。reload後にリクエストを1回送って、ログが実際に溜まる必要があります。
参考
- 起動:
nginx -c /root/ng/nginx.conf -p /root/ng - 文法チェック:
nginx -t -c /root/ng/nginx.conf -p /root/ng - 再適用:
nginx -s reload -c /root/ng/nginx.conf -p /root/ng - 大きなボディの送信:
head -c 26000000 /dev/zero | curl -s -o /dev/null -w '%{http_code}' --data-binary @- <URL> - よくあるミス1:
proxy_passの後ろにスラッシュを付けたり外したりして、404を作ってしまうミスです。URI部分があると、locationにマッチした部分が切り落とされます。 - よくあるミス2:
events {}ブロックを書き忘れるミスです。nginx.confには必須です。 - よくあるミス3: ログディレクトリを作っておかずにreloadして、起動に失敗するミスです。
最小のプロキシ設定と起動
/root/ng/nginx.confを作成して、nginxを起動します。ポート8088でlistenし、/をhttp://127.0.0.1:8080にプロキシします。http://127.0.0.1:8088/labhub/versionが200である必要があります。
nginxは、-cで設定ファイルを、-pでprefix(相対パスの基準)を指定できます。この環境は、1024未満のポートにバインドできないことを覚えておいてください。起動前に、-tで文法チェックから行う習慣を付けてください。
転送ヘッダーの設定
プロキシヘッダーを4つ設定して、reloadします。Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Protoです。確認: curl -s http://127.0.0.1:8088/labhub/echoのJSONに、x-forwarded-forとx-forwarded-protoがあり、hostの値が127.0.0.1:8080でない必要があります。
バックエンドが実際に何を受け取っているかを確認できる、エンドポイントがあります。Hostを渡さないと、バックエンドが見るHostが何になるかを、自分で確認してみてください。
プロキシのタイムアウト3種
proxy_connect_timeout 3s、proxy_send_timeout 30s、proxy_read_timeout 60sを設定します。
接続・送信・読み取りのタイムアウトは、それぞれ別の区間を計ります。504は、このうちどれと関係が深いかを、考えてみてください。
リクエストボディのサイズ制限
client_max_body_sizeを20mに設定して、reloadします。5MBのボディのPOSTは413ではなく、25MBのボディのPOSTは413である必要があります。
この制限を超えると、nginxが直接応答するため、バックエンドのログには何の痕跡も残りません。デフォルト値がいくつかを知っていれば、ファイルアップロード障害を素早く捕まえられます。
レスポンスの圧縮
gzip on、gzip_min_length 1000、gzip_vary onを設定して、gzip_typesにapplication/jsonを含めます。curl -H 'Accept-Encoding: gzip' -I http://127.0.0.1:8088/labhub/big.htmlのレスポンスに、Content-Encoding: gzipがある必要があります。
最小長の基準より小さいレスポンスは、圧縮されません。キャッシュの誤動作を防ぐディレクティブも、1つ一緒に有効にする必要があります。
静的ファイルの分離
/root/ng/static/app.jsファイルを作成し(内容は自由)、/assets/パスでサービスされるように設定します。つまり、http://127.0.0.1:8088/assets/app.jsが200である必要があり、レスポンスヘッダーにExpiresまたはCache-Controlがある必要があります。
locationのパスと実際のディレクトリ名が違うときに使うディレクティブがあります。キャッシュ期限のヘッダーも、一緒に付けてください。
管理者パスの遮断
/manager/で始まるすべてのリクエストが、403を返すように設定します。http://127.0.0.1:8088/manager/htmlが403である必要があります。
プロキシで止めれば、アプリケーションをデプロイせずに、ポリシーを変更できます。正規表現のlocationと、前方一致のlocationの優先順位を、考えてみてください。
障害分析用のログフォーマット
log_formatを定義して、$upstream_addr、$upstream_status、$upstream_response_time、$request_timeの4つの変数を含め、アクセスログを/root/ng/logs/access.logに残します。reload後にリクエストを1回送って、ログが実際に溜まる必要があります。
バックエンドが使った時間と全体の時間を一緒に残してこそ、「バックエンドが遅い」と「転送が遅い」を区別できます。ログを残すには、リクエストを実際に送る必要があります。