WebサーバーのHTTP/3対応をNginxのリバースプロキシでするためのDockerイメージが出来ました


やりたいこと

好きなWebサーバーをHTTP/3に対応させたいです。
HTTP/3はGoogleやCloudflareなどで使われ徐々に普及しています。HTTP/3では
UDPで通信が行われ、HTTP/1.xやHTTP/2やより高速に通信することができるようになります。詳しくは「HTTP/3が出るらしいという話を雑に書く - Qiita」などが参考になります。

今回はNginx + HTTP/3をDockerイメージにしてすぐに手軽にHTTP/3対応が出来るようにしました。Docker Composeで手軽にHTTP/3対応できるサンプルも載せたいと思います。

Cloudflare Quiche

CDNや1.1.1.1やWarpなどで活躍しているCloudflareからQuicheというQUICやHTTP/3のためのOSSがリリースされています。Cloudflare公式がNginxをHTTP/3対応する記事があり、それを元にDockerイメージを作成しました。

Nginx + HTTP/3のDockerイメージ

docker pull nwtgck/nginx-http3でDockerイメージが手に入ります。
Dockerfileの内容はhttps://github.com/nwtgck/docker-nginx-http3/blob/master/Dockerfileです。
Dockerfileは公式のGitHubにあるNginx + HTTP/3をする説明を(quiche/extras/nginx at master · cloudflare/quiche)を元に作成しました。

Docker Hub上でのサイズは47MBでnginx:latestと同じくらいのサイズです。

例: docker run

docker runで起動させる例についてです。
以下のnginx.confを今いるディレクトリに作成します。

./nginx.conf
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    server {
        # Enable QUIC and HTTP/3.
        listen 443 quic reuseport;

        # Enable HTTP/2 (optional).
        listen 443 ssl http2;

        ssl_certificate      /etc/ssl/certs/server.crt;
        ssl_certificate_key  /etc/ssl/private/server.key;

        # 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';
    }
}

以下のようにHTTP/3ではUDPも使うため-p 443:443/udpでUDPも解放します。

docker run -it -p 443:443 -p 443:443/udp \
  -v $PWD/nginx.conf:/usr/local/nginx/conf/nginx.conf \
  -v /etc/letsencrypt/live/nwtgck-nginx-http3.tk/fullchain.pem:/etc/ssl/certs/server.crt \
  -v /etc/letsencrypt/live/nwtgck-nginx-http3.tk/privkey.pem:/etc/ssl/private/server.key \
  nwtgck/nginx-http3

上記はLet's Encryptの証明書を指定してます。.../fullchain.pem.../privkey.pemは適切に存在しているものを指定してください。最初はローカル環境で自己証明書を使いたかったのですが、後述のChromeでHTTP/3対応の確認が出来なかったのでLet's Encryptを使いました。

好きなWebサーバーをリバースプロキシしてHTTP/3対応するには

上記のように好きにnginx.confが書けるためlocation / {proxy_pass ...}などを使ってリバースプロキシの設定すれば良いと思います。
docker run --net=host ...を使って起動すればホスト環境で起動している他のWebサーバーのリバースプロキシを作れると思います。
Docker Composeでリバースプロキシする例は後述します。

HTTP/3対応できているか確認する

このCloudflareの公式ブログ「HTTP/3: the past, the present, and the future」で触れられている方法を使ってHTTP/3の対応を確認します。Chrome Canaryに--enable-quic --quic-version=h3-23フラグを付けて起動します。

Macユーザーは以下のコマンドで起動できます。

/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --enable-quic --quic-version=h3-23

以下のように開発者ツールのネットワークタブでhttp/2+quic/99となっていてHTTP/3で通信できていることが確認できました。

Chrome CanaryはVersion 80.0.3959.0 (Official Build) canary (64-bit)を使いました。GUIのChrome CanaryではなくCLIで確認したいときは以下の方法が使えるかも知れません。
http3-client - HTTP/3 Docs

Docker Composeを使った例

HTTP/3の対応するのははGhostというOSSのブログのプラットフォームです。https://hub.docker.com/_/ghost/でDockerイメージが配布されています。

このGitHubリポジトリhttps://github.com/nwtgck/ghost-nginx-http3-docker-composeに例を作りました。

docker-compose.ymlnginx.confがあるだけのシンプルな構成で内容は以下のとおりです。

docker-compose.yml
version: '3.1'
services:
  nginx:
    image: nwtgck/nginx-http3
    ports:
      - '80:80'
      - '443:443'
      - '443:443/udp'
    depends_on:
      - ghost
    restart: always
    volumes:
      - ./nginx.conf:/usr/local/nginx/conf/nginx.conf
      # 適切に証明書のパスを指定してください。
      - /etc/letsencrypt/live/nwtgck-nginx-http3.tk/fullchain.pem:/etc/ssl/certs/server.crt
      - /etc/letsencrypt/live/nwtgck-nginx-http3.tk/privkey.pem:/etc/ssl/private/server.key

  ghost:
    image: ghost
    restart: always
    expose:
      - "2368"
nginx.conf
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://ghost:2368;
        }
    }

    server {
        # Enable QUIC and HTTP/3.
        listen 443 quic reuseport;

        # Enable HTTP/2 (optional).
        listen 443 ssl http2;

        ssl_certificate      /etc/ssl/certs/server.crt;
        ssl_certificate_key  /etc/ssl/private/server.key;

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

        location / {
            proxy_pass http://ghost:2368;
        }

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

自分の環境で試す方法

# リポジトリをクローンする
git clone https://github.com/nwtgck/ghost-nginx-http3-docker-compose.git
cd ghost-nginx-http3-docker-compose

<docker-compose.ymlの...fullchain.pemと...privkey.pemのパスを適切に書き換える>

# サーバーを起動する
docker-compose up

Chrome Canaryの開発者ツールのネットワークタブで確認するとhttps://nwtgck-nginx-http3.tk/...のリクエストがhttp/2+quic/99となっていてHTTP/3で通信されていることが分かります。

おまけ

以下にNginx + HTTP/3でビルドしたり、Dockerイメージにするために少し苦労したことなどを書きました。
Nginx + HTTP/3 (Quiche)のDockerイメージを生成するためにしたこと - nwtgck / Ryo Ota