Help us understand the problem. What is going on with this article?

nghttpx - HTTP/2 リバースプロキシー / L7 ロードバランサー

More than 3 years have passed since last update.

nghttpx - HTTP/2 リバースプロキシー / L7 ロードバランサー

nghttpx は nghttp2 プロジェクトで開発している HTTP/2 対応のリバースプロキシー / L7 ロードバランサーです.

この文書は v1.11.0 開発バージョンを元に執筆しました.

リバースプロキシーとして使ってみよう

nghttpx のもっとも簡単な使い方はサーバー証明書を用意して:

$ nghttpx server.key server.crt

を実行することです. server.key はサーバーの秘密鍵ファイル, server.crt は証明書のファイルです. どちらも PEM 形式です. この構成では nghttpx は全インターフェースのポート 3000 番でTLS 接続を受け付け, バックエンドサーバー 127.0.0.1:80 にフォワードします. バックエンドの接続は TLS なしになります. またはフォワードは HTTP リクエスト毎に行います.

nghttpx はすべての設定をコマンドラインオプションで指定できますが, 設定ファイルに書くこともできます. シェルで特別に意味を持つ文字を入力することもあるので以下では設定ファイルを使うことにします. 以下のような設定ファイル nghttpx.conf を用意し:

nghttpx.conf
private-key-file=server.key
certificate-file=server.crt

nghttpx を以下のように実行します:

$ nghttpx --conf nghttpx.conf

デフォルトでは /etc/nghttpx/nghttpx.conf を見に行くようになっています. 設定ファイルの内容はコマンドラインオプションで上書き(一部のオプションは上書きではなく追加)できます.

対応プロトコルはどうなっているのでしょうか. 上記の場合, HTTP/2, HTTP/1.1 をフロントエンドでサポート1しています. ALPN または NPN でプロトコルのネゴシエーションをします. バックエンドは HTTP/1.1 になります. バックエンドで HTTP/2 を使う方法はすぐ後で書きます.

TLS に関しては nghttpx は充実しています2. OCSPステープリング3, TLS セッションチケット秘密鍵の自動ローテーション, 複数 nghttpx インスタンス間の TLS セッションチケット秘密鍵および TLS セッションキャッシュの共有4もサポートしています.

バックエンドの接続先を変更してみましょう. この場合 backend オプションを使います. 127.0.0.1:8080 にフォワードするには以下のように設定ファイルに書きます:

nghttpx.conf
backend=127.0.0.1,8080

アドレスとポートの間は ":" ではなくて "," なので注意してください.

バックエンドで使うアプリケーションプロトコルはデフォルトでは HTTP/1.1 でした. HTTP/2 を使う場合は backend オプションで:

nghttpx.conf
backend=127.0.0.1,8080;;proto=h2

とします. ";" が二個ありますが誤植ではありません. これについてはすぐ後で説明します. この文書の執筆時点ではバックエンドのアプリケーションプロトコルは明示的に指定する必要があります.

nghttpx では proto=h2 など backend オプション等で使用する追加設定をパラメーターと称しています. backend オプションでパラメーターを使ってバックエンド接続の設定を変更することができます. パラメーターは ";" で区切ります.

バックエンド接続を TLS で暗号化するには tls パラメーターを使います:

nghttpx.conf
backend=127.0.0.1,8080;;proto=h2;tls

UNIX ドメインソケットにも対応しています. "unix:" に続けて接続先の UNIX ドメインソケットのパスを書きます:

nghttpx.conf
backend=unix:/tmp/h2o.sock;;proto=h2

慣れてきましたか? では ";" が二個あった件についてお話しましょう.

nghttpx ではリクエストパスとホストでフォワード先を切り替えることができます. backend オプションを複数回つかってフォワード先を指定します. 例えば以下の例をみてください:

nghttpx.conf
backend=127.0.0.1,6000;ws.example.com/ws/
backend=127.0.0.1,8080;/;proto=h2

上記の例では, ホストが ws.example.com でリクエストパスが /ws/ で始まる場合, 127.0.0.1:6000 へ HTTP/1.1 でフォワードする, それ以外は 127.0.0.1:8080 へ HTTP/2 でフォワードする, という設定になります.

フォワード先を切り替えるためのリクエストパスとホストの組をルーティングパターンと称しています. 書式について説明しましょう. リクエストパスは必ず "/" で始まります. "/" で始まらない場合, それはホスト名であると認識されます. ホスト名だけ指定されていて, パスが指定されていない場合は, パスが "/" であるとみなされます. ポート番号とキーワードの間にルーティングパターンを書き, ";" で区切るという書式になっています. ルーティングパターンを省略すると "/" が指定されたことになり, すべてのリクエストがマッチするようになります. nghttpx ではこのようにすべてのリクエストがマッチするルーティングパターンを含めなければなりません.

ルーティングパターンのマッチングルールは Go の net/http パッケージの ServeMux によく似たものです5. パスのマッチについては, 長いものが優先されます. ホスト名がマッチするとパスのマッチよりも優先され, その中でパスのマッチで最も長いものが選ばれます. "/" で終わる場合, 前方一致となります. 一つ例外があってリクエストパスに "/" を末尾に追加した時にマッチする場合も含むということです (e.g., ルーティングパターン "/ws/" は, リクエストパス "/ws" にもマッチする). "/" で終わらないルーティングパターンは完全一致になります.

同じパターンを持つ backend を複数回つかってロードバランス先のバックエンドサーバーを追加できます:

nghttpx.conf
backend=127.0.0.1,8080;/;
backend=192.168.0.1,50051;/greeter/;proto=h2
backend=192.168.0.2,50051;/greeter/;proto=h2
backend=192.168.0.3,50051;/greeter/;proto=h2

上記の例では, ルーティングパターン "/greeter/" で 3 個のバックエンドサーバー 192.168.0.1, 192.168.0.2, 192.168.0.3 を追加しました6.

バックエンドの接続数は nghttpx が自動で調整します. バックエンドの HTTP/2 接続を複数のクライアントで共有する場合もあります.

フロントエンド, バックエンドで共に HTTP/2 をサポートしているので例えば gRPC のような HTTP/2 ベースの RPC のリクエストを L7 でロードバランスすることができます.

backend オプションについて詳しく知りたい方はマニュアルを参照してください.

ではフロントエンドの話をしましょう. デフォルトでは全インターフェースでポート 3000 番を listen し TLS が必須なのでした. listen するアドレスを変更するには frontend オプションを使います:

nghttpx.conf
frontend=*,80;no-tls

上記の例では全インターフェースでポート 80 番を listen し, TLS 無しの平文という設定内容になります. "*" は全インターフェースを意味する記号です. 0.0.0.0 のように IP アドレスを書くことももちろんできます. no-tls はキーワードで, TLS 無しを意味します.

frontend オプションを複数書いて listen するアドレスを増やすことができます:

nghttpx.conf
frontend=*,80;no-tls
frontend=*,443

上記の例では, 80 番ポートで平文通信, 443 番ポートで TLS 暗号化通信という設定になります.

UNIX ドメインソケットにも対応しています. "unix:" に続けて UNIX ドメインソケットのパスを書きます:

nghttpx.conf
frontend=unix:/tmp/nghttpx.sock

frontend オプションについて詳しく知りたい方はマニュアルを参照してください.

ほとんどの Web サーバーによるリバースプロキシーではフロントエンドとバックエンドが関連付いて設定できる (あるいはそう強制される) ようになっています. nghttpx はフロントエンドとバックエンドは分離されていてどのフロントエンドアドレスから入ってきても同じようにバックエンドにルーティングします. これについては賛否両論あるでしょう. 後述する mruby スクリプトを使ってルーティングを制御することができます.

HTTP/2 セキュアプロキシーとして使ってみよう

HTTP/2 セキュアプロキシーは, ブラウザーとフォワードプロキシーの間は原則的に 1 本の HTTP/2 TLS 接続になり, すべてのリクエストはこの接続を通してフォワードされます. フォワードプロキシーはクライアントの要求に従い代理としてオリジンサーバーへリクエストをフォワードします. https URI の場合は同じ接続でトンネリングされます.

バックエンドには Squid やフォワードプロキシーとして設定した Apache Traffic Server が必要です. nghttpx は HTTP/2 と TLS の終端だけ行い, キャッシングなど残りの処理はすべてバックエンドに委譲します.

nghttpx を HTTP/2 セキュアプロキシーとして使うためには http2-proxy オプションを使います:

nghttpx.conf
http2-proxy=yes

http2-proxy=yes の場合, backend オプションのルーティングパターンはすべて無視されます.

Chromium と Mozilla Firefox が HTTP/2 セキュアプロキシーに対応しています. 両者ともに TLS 接続が必須になっています.

対応ブラウザで HTTP/2 セキュアプロキシーを使うには, 以下のような proxy.pac スクリプトを作成します:

proxy.pac
function FindProxyForURL(url, host) {
    return "HTTPS <SERVERADDR>:<PORT>";
}

<SERVERADDR><PORT> は nghttpx が listen しているアドレスとポート番号になります. ブラウザは正しい SSL/TLS 証明書を要求します. self-signed な証明書を使う場合はインポートするなりします.

Mozilla Firefox では Preference を開いて, Advanced, Network タブをクリックします. Connection Settings ボタンをクリックして表示されるダイアログで "Automatic proxy configuration URL" を選択して proxy.pac へのパスを以下のように指定します:

file:///path/to/proxy.pac

Chromium では以下のようなコマンドラインを使います:

$ chromium --proxy-pac-url=file:///path/to/proxy.pac --use-npn

mruby スクリプトによる拡張

nghttpx は mruby スクリプトで機能を拡張することができます7. この文書の執筆時点では以下のようなことができます:

  • リクエストヘッダー, レスポンスヘッダーの書き換え
  • リクエストヘッダーを書き換えることによりバックエンドへのルーティングを制御
  • バックエンドを使わずにレスポンスを返す

mruby スクリプトでは, 以下の 2 つのメソッドを持つオブジェクトを返すようにします:

  • on_req(ctx): クライアントからリクエストヘッダーを受信した時に実行される
  • on_resp(ctx): バックエンドからレスポンスヘッダーを受信した時に実行される

ctx はリクエストの情報が入ったオブジェクトです. nghttpx は返されたオブジェクトのメソッドを上記に書いたイベントが発生すると呼び出します.

例として, すべてのリクエストパスの前に "/apps" というパスコンポーネントを挿入する mruby スクリプトは以下のようになります:

mod.rb
class App
  def on_req(env)
    env.req.path = "/apps#{env.req.path}"
  end
end

App.new

mod.rb を mruby-file オプションで指定します.

mruby スクリプトの詳しい使い方についてはマニュアルを参照してください.

参考文献

nghttpx について詳しく知りたい方には以下の文書をおすすめします.

脚注


  1. spdylay ライブラリと共にビルドするとフロントエンドで SPDY プロトコルもサポートします. 

  2. https://istlsfastyet.com/#server-performance 

  3. https://nghttp2.org/documentation/nghttpx.1.html#ocsp-stapling 

  4. https://nghttp2.org/documentation/nghttpx.1.html#tls-session-resumption 

  5. https://golang.org/pkg/net/http/#ServeMux 

  6. この文書執筆時点では同じルーティングパターンを共有する場合, prototls キーワードの設定がバックエンドですべて同じである必要があります. ルーティングパターンが違う場合はこの限りではありません. 

  7. mruby を利用するためには, --with-mruby オプションを configure スクリプトに与えて nghttp2 をビルドします. デフォルトでは有効になっていません. 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away