サービス事業者向けリバースプロキシ フレームワーク Pingora ことはじめ

本ブログではリバースプロキシのフレームワークPingoraがもたらすものとその特徴、最初の始め方をご紹介します。
2024.03.31

ども、大瀧です。

先日、Cloudflareが自社で利用しているソフトウェアPingoraをOSSとして公開しました。彼らはPingoraを"フレームワーク"と呼ぶようによくあるリバースプロキシとは建付けが異なるので、本ブログではPingoraがもたらすものとその特徴、最初の始め方をご紹介します。

Pingoraはフレームワーク

フレームワークというと、Ruby on RailsのようなWebアプリケーションフレームワークを連想すると思います。PingoraはWebアプリケーションではなくリバースプロキシやサービスゲートウェイのためのフレームワークです。

リバースプロキシというとNginxやHAProxyをイメージしますが、それらの完成されたリバースプロキシソフトウェアは最小限の構成ファイルで手軽に動作できる反面、他のシステムとの連携やパフォーマンスチューニングに独特な作法があったり、動的な構成変更に制約があります。

一方最近のプログラミング言語はリバースプロキシのためのライブラリを組み込みでサポートし、リバースプロキシソフトウェアをスクラッチで開発することが容易になっていますが、ライブラリの肥大化や依存関係、パフォーマンスなど超えるべき課題も多いです。

Pingoraは、リバースプロキシのよく利用する機能をRustのCrate(モジュールのようなもの)として提供するフレームワークです。Rustの豊富な Crateとの組み合わせや、ソフトウェアへの組み込みを前提とした柔軟な設計とCloudflareの実践的なプラクティスが盛り込まれた非同期処理などいわゆる「巨人の肩に乗る」いいとこ取りのリバースプロキシ開発ができるわけです。Pingoraの特徴やアーキテクチャはCloudflareのブログが詳しいです。

動かしてみよう

動作確認環境

  • OS: macOS Sonoma バージョン 14.3.1
  • Rust: バージョン 1.77.0

下準備

手元のMacにはRustがインストールされていなかったので、公式の手順に沿ってインストールします。また、Pingoraのビルドにはcmakeが必要なので、Homebrewであらかじめインストールしておきます(Homebrew自体のセットアップは割愛)。

$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
$ brew install cmake

サンプルの実行

では、サンプルを動かしてみましょう。GitHubのリポジトリにサンプルがあるので、git pullで持ってきます。

$ git clone https://github.com/cloudflare/pingora.git
$ cd pingora/

Crateごとにたくさんのサンプルがありますが、READMEのCrateの説明に拠るとPingoraCrateのサンプルが良さそうです。

Pingora: the "public facing" crate to build networked systems and proxies

眺めてみた感じpingora/example/server.rsがコマンドラインオプションやプロキシのひと通りを動かせそうなので、今回はこちらを試してみます。このサンプルでは、HTTPSおよびTLSで待ち受けるためのTLS証明書が以下のパスに配置されることを期待します。

  • サーバー証明書: ./pingora/tests/keys/server.crt
  • 秘密鍵: ./pingora/tests/keys/key.pem

mkcertなどであらかじめ作成しておきます。

$ mkdir pingora/tests/keys
$ cd pingora/tests/keys
$ mkcert "yakiniku.local"
$ ln -s yakiniku.local.pem server.crt
$ ln -s yakiniku.local-key.pem key.pem

これで証明書の準備ができました。では、実行してみましょう。

$ cargo run --release --package pingora --example server
      :(略)
   Compiling pingora-openssl v0.1.0 (/Users/xxxx/Repos/pingora/pingora-openssl)
   Compiling pingora-core v0.1.0 (/Users/xxxx/Repos/pingora/pingora-core)
   Compiling pingora v0.1.0 (/Users/xxxx/Repos/pingora/pingora)
    Finished release [optimized] target(s) in 1m 50s
     Running `target/release/examples/server`
Usage
port 6142: TCP echo server
nc 127.0.0.1 6142

port 6143: TLS echo server
openssl s_client -connect 127.0.0.1:6143

port 6145: Http echo server
curl http://127.0.0.1:6145 -v -d 'hello'

port 6148: Https echo server
curl https://127.0.0.1:6148 -vk -d 'hello'

port 6141: TCP proxy
curl http://127.0.0.1:6141 -v -H 'host: 1.1.1.1'

port 6144: TLS proxy
curl https://127.0.0.1:6144 -vk -H 'host: one.one.one.one' -o /dev/null

port 6150: metrics endpoint
curl http://127.0.0.1:6150

この状態でもう一つのターミナルを開き、UsageにあるcURLコマンド例を試してみます。前半の4つは"echo server"とあるので、ターミナルからの入力やcURLの-dオプションで文字列を送信すると、同じ文字列を返す実装になっていますね。TCP、HTTPおよびTLSをサポートする様子がわかります。

$ nc 127.0.0.1 6142
test
test
[Ctrl+C]
$ curl http://127.0.0.1:6145 -v -d 'hello'
*   Trying 127.0.0.1:6145...
* Connected to 127.0.0.1 (127.0.0.1) port 6145
> POST / HTTP/1.1
> Host: 127.0.0.1:6145
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Length: 5
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 5 bytes
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 5
< Date: Sat, 30 Mar 2024 22:32:24 GMT
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
hello
$

5,6個目のコマンド例はリバースプロキシの例です。それぞれ`https://1.1.1.1/`と`https://one.one.one.one/`へのHTTP/HTTPSリクエストと同じレスポンスボディが返ってくることが確認できます。

$ curl http://127.0.0.1:6141 -v -H 'host: 1.1.1.1'
*   Trying 127.0.0.1:6141...
* Connected to 127.0.0.1 (127.0.0.1) port 6141
> GET / HTTP/1.1
> Host: 1.1.1.1
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Server: cloudflare
< Date: Sat, 30 Mar 2024 22:19:25 GMT
< Content-Type: text/html
< Content-Length: 167
< Connection: keep-alive
< Location: https://1.1.1.1/
< CF-RAY: 86cb7b2cfd933511-NRT
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>cloudflare</center>
</body>
</html>
* Connection #0 to host 127.0.0.1 left intact
$

最後のコマンド例は、Prometheus Exporterへのリクエスト例です。サンプルではHTTP echo serverへのリクエスト数をメトリックとして取るようになっています。TCP echoやプロキシはカウントされないので注意しましょう。

$ curl http://127.0.0.1:6150
# HELP reg_counter Number of requests
# TYPE reg_counter counter
reg_counter 1
$

Prometheus対応はリバースプロキシ専用のExporterを扱うことが多かったので独自のメトリックを定義して作り込んでいくのは楽しそうな反面、統計情報を数える実装はいろいろ考えることが多そうで今からちょっとげんなりしている自分もいます(苦笑)。

気を取り直して、今回はサンプルを実行しましたが、ソースプログラムをイチから作るチュートリアルもあります。

また、今回触れなかったYAML形式の構成ファイルやSystemdのサービスへの登録などの解説、HTTPプロキシの実装ガイドなど情報満載のユーザーガイドもあります。現時点で作成中(WIP)となっているAdvanced topicsの項目がどれも面白そうで、追加されるのを楽しみにしています。

まとめ

リバースプロキシのフレームワークPingoraをご紹介しました。リバースプロキシを自作するケースはあまり多くないと思いますが、クラウドを活用するユーザーやインテグレーターの方も裏の仕組みを理解するために試しに触ってみるのも有用ではないでしょうか。プロキシをマルチテナントサービスとして提供するサービス事業者や大規模なKubernetes環境を運用する組織ではリバースプロキシの開発現場もあったりするので、興味があればそれらの話を聞いてみてもいいかもしれませんよ。

参考URL