BLOG | NGINX

Kernel TLSとSSL_sendfileによるパフォーマンス向上

NGINX-Part-of-F5-horiz-black-type-RGB
Mikhail Isachenkov サムネール
Mikhail Isachenkov
Published November 11, 2021
Timo Stark サムネール
Timo Stark
Published November 11, 2021

TLS(Transport Layer Security)は、非常に有名な暗号プロトコルです。TLSをkernelに実装する(kTLS)ことで、ユーザー空間とkerne l空間のコピー動作の必要性を大幅に減らし、パフォーマンスを向上させることができます。

kTLSとsendfile()を組み合わせることで、データ転送でネットワークスタックに渡される前に、kernel空間で直接暗号化されます。これにより、データをユーザー空間にコピーしTLS ライブラリで暗号化し、その後kernel空間に戻す必要が無くなります。又、kTLS は TLS 処理をハードウェアにオフロードすることができ、TLS暗号処理をネットワークデバイスにオフロードすることもできます。

最近のLinuxとFreeBSDのカーネルはTLSをカーネルにオフロードすることをサポートしていますが、NGINXオープンソースもサポートしています。NGINX 1.21.4では、SSL_sendfile()で静的ファイルを配信する際にkTLSのサポートを導入しており、パフォーマンスを大幅に向上させることができます。以下に詳述するように、NGINXがSSL_sendfile()を使用するには、カーネルとOpenSSLの両方がkTLSでビルドされていることが必要です。

このブログでは、どのオペレーティングシステムとOpenSSLのバージョンがkTLSをサポートしているかを詳しく説明し、kTLS用にカーネルとNGINXを構築し設定する方法を紹介します。また、kTLSによって期待できるパフォーマンスの向上について、FreeBSDとUbuntuで行ったテストの仕様と結果も紹介します。

注意:kTLSの実装は非常に新しく、急速に進化しています。本ブログでは2021年11月現在のkTLSのサポートについて説明していますが、ここで提供する情報や手順の変更については、nginx.orgNGINXブログ<.htmla>でのアナウンスを確認してください。

一般的な要件

  • いずれかのオペレーティングシステム

    • FreeBSD 13.0+. 2021年11月現在、FreeBSD 13.0+は、OpenSSL 3.0.0+を組み込むためにNGINXを手動でビルドしなくてもNGINXでkTLSをサポートする唯一のOSです。FreeBSDでNGINXをkTLSで有効にするを参照してください。

    • Linuxカーネルバージョン4.17以降で構築されたLinuxディストリビューション、ただし可能な限りバージョン5.2以降で構築されたものを使用することを推奨します。(kTLSのサポートは実際にはバージョン4.13で利用可能ですが、OpenSSL 3.0.0はカーネルヘッダーバージョン4.17以降が必要です)。

  • OpenSSL – バージョン3.0.0またはそれ以降

  • NGINX – バージョン 1.21.4 または以降 (mainline)

対応オペレーティングシステム

kTLSをサポートするOS

2021年11月現在、NGINX Open SourceがサポートするOSのうち、以下はkTLSと表記されている暗号をサポートしています。暗号のサポートの詳細については、「TLS プロトコルと暗号のサポート」を参照してください。

 TLSv1.2 ciphersTLSv1.3
cipher suites
TLS_CHACHA20_POLY1305_SHA256 cipherLinux kernel version
Amazon Linux 2*5.10
CentOS 8**4.18
FreeBSD 13.0❌ ***N/A
RHEL 84.18
SLES 15 SP25.3
Ubuntu 20.04 LTS❌​​5.4
Ubuntu 21.045.11
Ubuntu 21.105.13

  * カーネルのバージョンは4.14ではなく5.10である必要があります。kTLSをサポートしないOS及びAmazon Linux 2 FAQを参照してください。
** kTLSのサポート状況は、アップストリームのRHEL 8から継承しています。
*** FreeBSD のコミットログを参照してください。

kTLSをサポートしないOS

以下のOSは、以下の理由により、kTLSをサポートしていません。

  • Alpine Linux 3.11-3.14 – カーネルは CONFIG_TLS=n オプションでビルドされており、モジュールまたはカーネルの一部としての kTLS のビルドは無効になっています。
  • Amazon Linux 2 – Linux カーネルバージョンは、デフォルトの Amazon Linux 2 AMI では 4.14 です (Amazon Linux 2 FAQ を参照してください)。
  • CentOS 7.4+ – Linuxカーネルバージョンは3.10です。アップストリームソースとしてRHEL 7.4+からそのkTLSサポートステータスを継承しています。
  • Debian 10 and 11 – カーネルは CONFIG_TLS=n オプションでビルドされています (Debian のバグ報告ログを参照してください)。
  • RHEL 7.4+ – Linux カーネルのバージョンは 3.10 です。
  • SLES 12 SP5+ – Linuxカーネルバージョンは4.12です。
  • Ubuntu 18.04 LTS – Linuxカーネルバージョンは4.15です。

TLS プロトコルと暗号のサポート

上記のように、kTLSをサポートしているOSは、TLSのプロトコルや暗号のサポートに違いがあります。

TLSv1.2において、kTLSモジュールは以下の暗号をサポートしています。

  • AES128-GCM-SHA256
  • AES256-GCM-SHA384
  • ECDHE-RSA-AES128-GCM-SHA256
  • ECDHE-RSA-AES256-GCM-SHA384

TLSv1.3では、kTLSモジュールは以下の暗号スイートをサポートしています。

  • TLS_AES_128_GCM_SHA256
  • TLS_AES_256_GCM_SHA384
  • TLS_CHACHA20_POLY1305_SHA256 (一部のOSのみ。kTLSをサポートしているOSで指定されています。)

OpenSSLによってサポートされているTLS暗号がNGINXバイナリで有効になっているか確認するには、NGINXを構築したディレクトリ(たとえば、ホームディレクトリ)でopenssl-3.0.0/.openssl/bin/openssl ciphersコマンドを実行してください。

NGINXでのkTLSの有効化

冒頭で述べたように、kTLSはすべての暗号化と復号化がカーネルで行われるため、NGINXのパフォーマンスを向上させます。データ転送の為にネットワークスタックに渡される前に、カーネル空間で直接暗号化されます。これにより、データをユーザー空間にコピーしてTLSライブラリで暗号化し、転送の為にカーネル空間に戻す必要がなくなります。

Diagram of kernel TLS (kTLS) with NGINX

カーネルでのkTLSのロード

最近のFreeBSDやLinuxのディストリビューションでは、kTLSは通常モジュールとして(CONFIG_TLS=mオプションで)ビルドされています。NGINXを起動する前に、明示的にkTLSモジュールをカーネルにロードする必要があります。

  • FreeBSDでは、これらのコマンドをrootユーザーで実行します。

    # kldload ktls ocf# sysctl kern.ipc.tls.enable=1

    reeBSDのコマンドオプションの詳細については、ktls(4)のマニュアルページを参照してください。

  • Linuxディストリビューションでは、rootユーザでこのコマンドを実行します。

    # modprobe tls

FreeBSDでNGINXをkTLSで使用可能にする

FreeBSDのNGINXでkTLSサポートを有効にするには、Linuxディストリビューションと同じ手順を使用することができます。ただし、FreeBSD Ports Collectionsecurity/openssl-develポートにあるkTLS付きNGINXのビルドを活用するために、以下の手順を実行することをお勧めします。kTLSの概要を含む詳細については、FreeBSDウェブサイトの「TLS Offload in the Kernel」を参照してください。

  1. kTLSをサポートするOpenSSL 3.0を、configメニューで適切なオプションを選択しながらビルドします。

    # cd /usr/ports/security/openssl-devel && make config && make install
  2. openssl-devel をデフォルトのSSLライブラリとして使用するように /etc/make.conf を変更します。

    # echo "DEFAULT_VERSIONS+=ssl=openssl-devel >> /etc/make.conf
  3. NGINXをビルドします。

    # cd /usr/ports/www/nginx-devel && make install

ディストリビューションでkTLSを使用したNGINXの構築

現在ほとんどのLinuxディストリビューションには、3.0.0より前のバージョンのOpenSSL(一般的には、バージョン1.1)が含まれています。そのため、NGINXはOpenSSL 3.0.0を使用してソースからビルドする必要があります。

kTLSサポートを有効にするconfigureコマンドの重要なオプションは、以下の2つです。

  • --with-openssl=../openssl-3.0.0
  • --with-openssl-opt=enable-ktls

その他のconfigureオプションは、nginx.orgで公開されているNGINXの公式バイナリパッケージに含まれるモジュールに対応したものです。代わりにカスタムモジュールのセットを指定することもできます。現在使用しているNGINXバイナリに使用されているビルドオプションを確認するには、nginx -Vを実行してください。

OpenSSL 3.0.0を使用してNGINXをビルドするには、以下のコマンドを実行します。

$ wget https://nginx.org/download/nginx-1.21.4.tar.gz$ wget https://www.openssl.org/source/openssl-3.0.0.tar.gz$ tar xzf openssl-3.0.0.tar.gz$ cd nginx-1.21.4$ ./configure \--with-debug \--prefix=/usr/local \--conf-path=/usr/local/etc/nginx/nginx.conf \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--pid-path=/var/run/nginx.pid \--lock-path=/var/run/nginx.lock \--http-client-body-temp-path=/var/cache/nginx/client_temp \--http-proxy-temp-path=/var/cache/nginx/proxy_temp \--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \--http-scgi-temp-path=/var/cache/nginx/scgi_temp \--user=nginx \--group=nginx \--with-compat \--with-file-aio \--with-threads \--with-http_addition_module \--with-http_auth_request_module \--with-http_dav_module \--with-http_flv_module \--with-http_gunzip_module \--with-http_gzip_static_module \--with-http_mp4_module \--with-http_random_index_module \--with-http_realip_module \--with-http_secure_link_module \--with-http_slice_module \--with-http_ssl_module \--with-http_stub_status_module \--with-http_sub_module \--with-http_v2_module \--with-mail \--with-mail_ssl_module \--with-stream \--with-stream_realip_module \--with-stream_ssl_module \--with-stream_ssl_preread_module \--with-openssl=../openssl-3.0.0 \--with-openssl-opt=enable-ktls \--with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' \-with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'$ make –j4$ make install

注:NGINXバイナリは、OpenSSL 3.0.0ライブラリと静的にリンクされています。OpenSSLにパッチを適用する必要がある場合は、新しいOpenSSLソースアーカイブをダウンロードして解凍し、上記のコマンドを実行してNGINXバイナリをリビルドする必要があります。

NGINXの設定

kTLSを有効にするには、テストに使用したこのサンプル構成のように、server{}コンテキストにOptions KTLSパラメータを持つssl_conf_commandディレクティブを含めます。

worker_processes auto;error_log /var/log/nginx/error.log debug;events {}http {    sendfile on;    server {        listen 443 ssl;        ssl_certificate ssl/example.crt;        ssl_certificate_key ssl/example.key;        ssl_conf_command Options KTLS;        ssl_protocols TLSv1.3;        location / {            root /data;    	}    }}

kTLSが有効であることを確認する

NGINXがkTLSを使用していることを確認するには、デバッグモードを有効にして、エラーログにBIO_get_ktls_send()SSL_sendfile()があるかどうかを確認します。

$ grep BIO /var/log/nginx/error.log2021/11/10 16:02:46 [debug] 274550#274550: *2 BIO_get_ktls_send(): 12021/11/10 16:02:49 [debug] 274550#274550: *3 BIO_get_ktls_send(): 1$ grep SSL_sendfile /var/log/nginx/error.log2021/11/10 16:02:46 [debug] 274550#274550: *2 SSL_sendfile: 10485762021/11/10 16:02:49 [debug] 274550#274550: *3 SSL_sendfile: 1048576

注:これらのチェックを行った後、特に実稼働環境では、デバッグモードを無効にすることをお勧めします。デバッグ・ログは、大量の書き込み操作を行うため、パフォーマンス・ペナルティが発生します。また、デバッグ・ログは巨大になりやすく、ディスク・パーティションの空き容量をすぐに使い果たしてしまいます。

kTLSによるパフォーマンス向上

高負荷下で静的ファイルを提供する場合、SSL_sendfile()はユーザー空間のTLSと比較して最大2倍までスループットを向上させることができますが、性能向上の大きさは様々な要因(ディスク性能、システム負荷など)に大きく依存します。また、ネットワークカードがTLSオフロードをサポートしていれば、CPU使用率を下げることが可能です。

性能テスト

以下の手順で1スレッドテストを実施することで、お使いのセットアップでのパフォーマンス向上を測定することができます。以下のテスト結果によると、特別なチューニングを行わなくても、最大で約30%の性能向上が確認されています。

使用したハードウェアとソフトウェア

  • AWS t3.medium instance with:
    • 4 GB RAM
    • 20 GB general purpose SSD
    • Intel® Xeon® Platinum 8259CL CPU @ 2.50GHz with 2 cores
  • FreeBSD 13.0 and Ubuntu 21.10
  • TLSv1.3 with the TLS_AES_256_GCM_SHA384 cipher suite
  • NGINX 1.21.4, built and configured as specified in Enabling kTLS in NGINX.

テストを実行するには

  1. ディスクキャッシュに完全に収まるような大きなファイルを作成します。

    # truncate -s 1g /data/1G
  2. このコマンドを実行してスループットを確認します。より正確な結果を得るために、ベースとなるコマンドを複数回繰り返し実行します。出力をministatユーティリティ[FreeBSD][Ubuntu]にパイプし、基本的な統計分析に使用します。

    # for i in 'seq 1 100'; do curl -k -s -o /dev/null -w '%{speed_download}\n' https://localhost/1G | ministat

パフォーマンステスト結果

以下のテスト結果は、ministatの出力として表示され、各値はkBytes/secondでのダウンロード速度です。

FreeBSD 13.0、kTLS使用しない場合のスループット。

    N           Min           Max        Median           Avg        Stddevx  10        532225        573348        555616      555155.6     10239.137

FreeBSD 13.0、kTLS使用した場合のスループット。

    N           Min           Max        Median           Avg        Stddevx  10        629379        723164        717349      708600.4     28304.766

Ubuntu 21.10でkTLSを使用しない場合のスループット。

    N           Min           Max        Median           Avg        Stddevx  10        529199        705720        662354      654321.6     48025.103

Ubuntu 21.10でkTLSを使用した場合のスループット。

    N           Min           Max        Median           Avg        Stddevx  10        619105        760208        756278      741848.3     43255.246

我々のテストでは、kTLSはUbuntuよりもFreeBSDでより性能を向上させました。改善率は以下の通りです。

 MinMaxMedianAvg
FreeBSD 13.018%26%29%28%
Ubuntu 21.1016%8%14%13%

概要

NGINX 1.21.4では、SSL_sendfile()を使用して静的ファイルを配信する際のkTLSのサポートが導入されました。当社のテストでは、オペレーティングシステムによって8%から29%、パフォーマンスが向上することが確認されています。

kTLSとNGINXの使用経験、特に他のOSでのテスト結果についてお聞かせください。特に他のOSでのテスト結果をお待ちしています!


"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."