ちょっと話題の記事

Nginx+NLBでHTTP/3環境をAWSに作ってみた

つい最近(2019/10)CloudFlareがQUIC対応のQuicheをNginxで利用出来るパッチを公開したりHTTP-over-QUICがHTTP/3に改名したりで、HTTP/3がかなりアツくなってきています。 このビッグウェーブに乗るべく、CloudFlareのQuicheパッチを適用したNginxを用意して、NLB越しでQUICを喋ってみたいと思います。
2019.12.29

もこ@札幌オフィスです。

つい最近、(2019/10)CloudFlareがQUIC対応のQuicheをNginxで利用出来るパッチを公開したりHTTP-over-QUICがHTTP/3に改名したりで、HTTP/3がかなりアツくなってきています。

このビッグウェーブに乗るべく、CloudFlareのQuicheパッチを適用したNginxを用意して、NLB越しでHTTP/3を喋ってみたいと思います。

NLBを利用してHTTP/3のレスポンスを返却することは出来ますが、インスタンス増減時のリハッシュにより既存コネクションが切断されるなどの問題があります。 HTTP/3+NLBを利用する際の問題点について検証して頂いたエントリーがございますので、合わせてご確認ください。 HTTP/3をUDPロードバランサで分散するときの問題点 (AWS NLBで試してみた)

AWS環境

NLBの下にEC2を置くシンプルな構成です。

NLBでは TCP_UDP443 を開け、EC2を追加したターゲットグループを設定します。

Route53でNLBに対してAliasレコードを作成します。

下ごしらえ

Nginxのビルド、証明書の発行、Nginxの実行もすべてAmazon Linux 2で行っていきます。

まずはじめに、証明書とNginxをビルドする環境を用意します。

証明書を用意する

オレオレ証明書でもなんでもいいとは思いますが、折角なのでLet's Encryptで証明書を発行していきます。

certbotをインストールして、DNS検証で証明書を発行します。

$ sudo yum install certbot -y
$ sudo certbot certonly --manual -d quic.mokomoko.dev --preferred-challenges dns

TXTレコードを設定するように言われますので、Route53でレコードを追加してDNS検証を完了させます。

Nginx(+quiche)をビルドする環境を用意

一部EPELで取得するので、 amazon-linux-extras でEPELを有効化しましょう。

$ # EPELリポジトリを有効化する
$ sudo amazon-linux-extras install -y epel
$ sudo yum update -y 
$ # 必要なパッケージをまとめてインストール
$ sudo yum install git patch gcc pcre-devel zlib-devel cmake3 gcc-c++ libunwind-devel golang cargo llvm7.0 -y 

Nginxを用意する

CloudFlareのブログで詳細に書かれていますので、基本的にはこちらの手順通りにやっていきますが、--prefix--sbin-path はCloudFlareのブログで紹介されてる内容から少し書き換えています。

$ curl -O https://nginx.org/download/nginx-1.16.1.tar.gz
$ tar xvzf nginx-1.16.1.tar.gz
$ git clone --recursive https://github.com/cloudflare/quiche
$ cd nginx-1.16.1 
$ patch -p01 < ../quiche/extras/nginx/nginx-1.16.patch
$ ./configure \
    --prefix=/etc/nginx \
    --sbin-path=/usr/sbin/nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_v3_module \
    --with-openssl=../quiche/deps/boringssl \
    --with-quiche=../quiche
$ sed -i -e 's/cmake/cmake3/g' objs/Makefile
$ make
$ sudo make install
$ sudo mkdir /var/log/nginx
$ # 確認
$ sudo nginx 
$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Sat, 28 Dec 2019 20:21:52 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Sat, 28 Dec 2019 19:32:46 GMT
Connection: keep-alive
ETag: "5e07adde-264"
Accept-Ranges: bytes

nginxとquiche取得して、quicheのpatchを当ててconfigure、make、make installです。簡単ですね。

nginx.configを編集する

やっと本題です。nginx.confを編集して、HTTP/3通信を出来るようにしてみましょう。

設定ファイルはこんな感じにしました。

$ cat /etc/nginx/conf/nginx.conf
events {
    worker_connections  1024;
}

error_log /var/log/nginx/error.log;

http {
    access_log /var/log/nginx/access.log;
    server {
        # Enable QUIC and HTTP/3.
        listen 443 quic reuseport;
        ssl_certificate      /etc/letsencrypt/live/quic.mokomoko.dev/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/quic.mokomoko.dev/privkey.pem;

        # Enable all TLS versions (TLSv1.3 is required for QUIC).
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

        # Add Alt-Svc header to negotiate HTTP/3.
        add_header alt-svc 'h3-23=":443"; ma=86400';
        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}

接続確認

cURLを使ってテストしてみます。

brewを使うと簡単にHTTP/3 Supprtのcurlが手に入るようなので、楽していきましょう。

HTTP/3: the past, the present, and the future

$ brew install --HEAD -s https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb

コマンド一発で入れることが出来ました。

早速テストしていきます。

$ cd /usr/local/opt/curl/bin
$ ./curl -I https://quic.mokomoko.dev/ --http3
HTTP/3 200
server: nginx/1.16.1
date: Sun, 29 Dec 2019 01:19:54 GMT
content-type: text/html
content-length: 612
last-modified: Sat, 28 Dec 2019 19:32:46 GMT
etag: "5e07adde-264"
alt-svc: h3-23=":443"; ma=86400
accept-ranges: bytes
$ ./curl https://quic.mokomoko.dev/ --http3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

HTTP/3で通信することが出来ました。

アクセスログでも正常にHTTP/3になっていることを確認できます!

xxx.xxx.xxx.xxx - - [29/Dec/2019:01:27:53 +0000] "GET / HTTP/3" 200 612 "-" "curl/7.68.0-DEV

追記

Network Load BalancerのUDPのルーティングは下記のような仕様となっているようですが、ご指摘いただいてる通り、NLBを挟むだけでは完全なHTTP/3のロードバランシングは難しいと実感しました。(QUICの知識不足でした..。)

UDP トラフィックの場合、ロードバランサーは、プロトコル、送信元 IP アドレス、送信元ポート、宛先 IP アドレス、および宛先ポートに基づいて、フローハッシュアルゴリズムを使用してターゲットを選択します。UDP フローは送信元と宛先が同じであるため、その存続期間を通じて一貫して単一のターゲットにルーティングされます。異なる UDP フローは異なる送信元 IP アドレスとポートを持つため、それらは異なるターゲットにルーティングできます。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/network/introduction.html

HTTP/3+NLBを利用する際の問題点について下記サイトでまとめて頂いていますので、合わせてご確認ください。

HTTP/3をUDPロードバランサで分散するときの問題点 (AWS NLBで試してみた)

まとめ

CloudFlareが提供しているNginxのパッチを利用して、NLB越しでもHTTP/3で通信することができました。

まだまだHTTP/3をフル活用したサイトが現れることは無いとは思いますが、NLB越しでAuto Scalingする環境でも利用できるかと思います。(要検証)

HTTP/3をUDPロードバランサで分散するときの問題点 (AWS NLBで試してみた)

今後のHTTP/3の動向に期待です。

おまけ(しょうもないハマった内容)

NLBがUDPを使えることしか考えていませんでしたが、きちんとEC2のSecurity GroupでUDPを開放してあげましょう。

(普段UDP使わないせいか、443(TCP)開けてるのになんで疎通できないんや!と15分くらい溶けたとか絶対言えない)

参考

https://japan.zdnet.com/article/35128543/ https://qiita.com/inductor/items/61b824289ac9b2f11b3e https://blog.cloudflare.com/experiment-with-http-3-using-nginx-and-quiche/ https://bagder.gitbook.io/http3-explained/ja