Envoy を使って既存の Web アプリケーションを HTTP3 対応してみる

目次

はじめに

2021年5月に QUIC が RFC 9000 として標準化され、HTTP/3 は現在は ドラフト34 で間もなく標準化が完了するでしょうから、自分の身の回りの Web アプリケーションを HTTP/3 に対応させていきたいですね。

Web アプリケーションサーバが HTTP/3 対応するまで待てば End-to-End の HTTP/3 通信を行えて良いのですが、いつになるかわかりませんので、リバースプロキシ型で HTTP/3 対応を試してみます。

現時点で HTTP/3 が利用可能な選択肢は限られていますが、その中でも今回は Envoy をリバースプロキシとして利用してみます。

環境構築

今回構築した構成は下図の通りです。

Ubuntu 20.04 のホストマシン上に Docker をインストールし、Envoy と Web アプリケーションをそれぞれコンテナとして実行することにしています。

ブラウザからホストマシンの 443 番ポートに HTTP/3 でアクセスすると、Envoy コンテナの 8443 番ポートにパケット転送され、そこから Envoy がリバースプロキシとして働いて Web アプリケーションに HTTP/1.1 (または HTTP/2) のリクエストが送信されることを想定しています。

Envoy は最新リリースの v1.21.1 を利用し、ダウンストリームで HTTP/3 を利用する設定例 をもとに envoy.yaml を作成しました。

ブラウザは Web サーバに対して最初は HTTP/1.1 や HTTP/2 でアクセスし、その後 Web サーバ側が HTTP/3 に対応していれば HTTP/3 に移行してアクセスするという動作を行うので、Envoy には HTTP/3 だけでなく HTTP/1.1 や HTTP/2 でもアクセスできるようにしてあります。 また、HTTP/1.1 や HTTP/2 でのアクセスに対して、レスポンスヘッダに alt-svc を設定することで、HTTP/3 に移行可能であることをクライアントに教えるようになっています。

Envoy で利用する TLS 証明書は、今回は次のように自己署名の認証局とサーバ証明書として用意しました。

1$ openssl genrsa -out ca.key 2048
2$ openssl req -x509 -new -nodes -sha256 -days 365 -subj "/CN=ca" -key ca.key -out ca.crt
3
4$ openssl genrsa -out server.key 2048
5$ openssl req -sha256 -new -subj "/CN=127.0.0.1" -key server.key -out server.csr
6$ echo "subjectAltName = IP:172.17.0.1, IP:192.168.1.203" > server.ext
7$ openssl x509 -req -sha256 -days 365 -CA ca.crt -CAkey ca.key -CAcreateserial -extfile server.ext -in server.csr -out server.crt

あとは、設定ファイル (envoy.yaml) とサーバ証明書 (server.key, server.crt) を適当に作成した envoy-conf ディレクトリに入れ、次のように /etc/envoy にマウントして Envoy コンテナを実行しました。

1$ sudo docker run -d --name envoy -p 443:8443 -p 443:8443/udp \
2        -v ~/envoy-conf:/etc/envoy envoyproxy/envoy:v1.21.1

最終的にアクセスしたい Web アプリケーションは何でもいいですが、Nuxt のチュートリアルで作成できる初期のテンプレートアプリを動作させています。

動作確認

環境を構築したので、さっそくブラウザからアクセスしてみましょう。

無事に Web アプリケーションが表示されました!

Firefox の開発ツールも見てみると、最初のリクエストは HTTP/2 で行われ、その後のリソースファイルへのリクエストは HTTP/3 で行われていますね。 ただ、Chrome だとなぜか HTTP/3 に切り替わってくれませんでした…。

また、クライアントのPC上で Wireshark でもパケットキャプチャして見てみましょう。

通信が途中から QUIC に切り替わって行われていることも確認できました。

ついでに、ホストマシン上で次のように tcpdump を実行し、Envoy のコンテナの eth0 を流れるパケットをキャプチャしてみましょう。 (※QUIC パケットを識別できるようにするため、最新の tcpdump をビルドして利用しています。)

1$ sudo nsenter -t $(sudo docker inspect envoy | jq -r ".[0].State.Pid") \
2        -n tcpdump -i eth0 -nn -T quic

クライアントとの通信や、背後の Web アプリケーションとの通信も期待通り行われているように見えますね!

というわけで

今回作った Envoy の環境は QUIC や HTTP/3 の勉強・検証用に使うことはできそうです。

ただ、実際のシステムで使うには考慮不足なことが多いため、検証してそのうちまとめるかもしれません。