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

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

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

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

AWS環境

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

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

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

下ごしらえ

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

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

証明書を用意する

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

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

1
2
$ 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を有効化しましょう。

1
2
3
4
5
$ # 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のブログで紹介されてる内容から少し書き換えています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ 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通信を出来るようにしてみましょう。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ 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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ 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になっていることを確認できます!

1
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

また後日詳細について検証したいと思います。

まとめ

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

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

今後の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

Well Architected 動画セミナー
PRAWSの技術支援ならクラスメソッド