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

NGINX | 2021年11月11日

Transport Layer Security(TLS)は、非常に広く利用されている暗号化プロトコルです。TLSをカーネルに実装するkTLSは、ユーザー空間とカーネル間のコピー処理を大幅に削減し、パフォーマンスを向上させます。

kTLSとsendfile()を組み合わせることで、データは送信用のネットワークスタックに渡される前に、カーネル空間で直接暗号化されます。これにより、TLSライブラリで暗号化するためにデータをユーザー空間へコピーし、送信のために再びカーネル空間へ戻す必要がなくなります。また、kTLSでは、TLSの対称暗号処理をネットワークデバイスにオフロードすることを含め、TLS処理をハードウェアにオフロードできます。

最新のLinuxおよびFreeBSDカーネルは、TLS処理のカーネルへのオフロードをサポートしています。そして今回、NGINX Open Sourceも対応しました。NGINX 1.21.4では、kTLSのサポートが導入され、SSL_sendfile()を使用して静的ファイルやキャッシュされたレスポンスを配信する際のパフォーマンスを大幅に向上できる可能性があります。以下で詳しく説明するように、NGINXでSSL_sendfile()を使用するには、カーネルとOpenSSLの両方をkTLS対応でビルドする必要があります。

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

注:kTLSの実装はまだ比較的新しく、急速に進化しています。このブログでは2021年11月時点のkTLSサポートについて説明していますが、ここで提供する情報や手順の変更については、nginx.orgおよびNGINXブログ<.htmla>での発表をご確認ください。

一般要件

  • オペレーティングシステム – 以下のいずれか:
    • FreeBSD 13.0以降。2021年11月時点で、OpenSSL 3.0.0以降を組み込むためにNGINXを手動でビルドしなくても、NGINXでkTLSをサポートする唯一のOSはFreeBSD 13.0以降です。詳しくは「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以降(メインライン)[編集者 – NGINX Plus R27以降では、対象となるLinuxベースのオペレーティングシステムのバージョンでkTLSがサポートされます。NGINX Plus R26以降では、対象となるFreeBSDのバージョンでkTLSがサポートされます。サポート対象OSの詳細については、NGINX Plusの「リリース」ページを参照してください。]

オペレーティングシステムのサポート

kTLSをサポートするOS

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

 TLSv1.2暗号TLSv1.3
暗号スイート
TLS_CHACHA20_POLY1305_SHA256暗号Linuxカーネルバージョン
Amazon Linux 2*✅✅❌5.10
CentOS 8**✅❌❌4.18
FreeBSD 13.x✅✅❌ ***該当なし
RHEL 8✅❌❌4.18
SLES 15 SP2✅✅✅5.3
Ubuntu 20.04 LTS✅❌​​❌5.4
Ubuntu 21.04✅✅✅5.11
Ubuntu 21.10✅✅✅5.13

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

kTLSをサポートしていないOS

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

  • Alpine Linux 3.11~3.14 – カーネルはCONFIG_TLS=nオプションを指定してビルドされているため、kTLSをモジュールまたはカーネルの一部としてビルドできません。
  • Amazon Linux 2 – デフォルトのAmazon Linux 2 AMIでは、Linuxカーネルのバージョンは4.14です(Amazon Linux 2 FAQを参照)。
  • CentOS 7.4+ – Linuxカーネルのバージョンは3.10です。アップストリームであるRHEL 7.4+のkTLSサポート状況を継承します。
  • Debian 10および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(kTLSをサポートするOSに記載されている一部のOSのみ)

OpenSSLでサポートされているTLS暗号スイートのうち、NGINXバイナリで有効になっているものを確認するには、NGINXをビルドしたディレクトリ(ホームディレクトリなど)で、openssl-3.0.0/.openssl/bin/openssl ciphersコマンドを実行します。

NGINXでのkTLSの有効化

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

NGINXでのカーネルTLS(kTLS)の図

カーネルへのkTLSのロード

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

  • FreeBSDでは、次のコマンドをrootユーザーとして実行します。
# kldload ktls_ocf.ko# sysctl kern.ipc.tls.enable=1
  • FreeBSDのコマンドオプションの詳細については、ktls(4)のmanページを参照してください。
  • Linuxディストリビューションでは、このコマンドをrootユーザーとして実行します:
# modprobe tls

FreeBSDでNGINXのkTLSを有効化

FreeBSD上のNGINXでkTLSサポートを有効にするには、Linuxディストリビューションと同じ「手順」を使用できます。ただし、security/openssl-develポートにあるkTLS対応NGINXビルドをFreeBSD Ports Collectionで利用するため、以下の手順を実行することを推奨します。kTLSの概要など、詳細については、FreeBSD Webサイトの「カーネルでのTLSオフロード」を参照してください。

  1. 設定メニューで適切なオプションを選択し、kTLSをサポートするOpenSSL 3.0をビルドします。
# cd /usr/ports/security/openssl-devel && make config && make install
  1. デフォルトのSSLライブラリとして「/etc/make.conf」で「openssl-devel」を使用するように変更します:
# echo "DEFAULT_VERSIONS+=ssl=openssl-devel" >> /etc/make.conf
  1. NGINXをビルドします:
# cd /usr/ports/www/nginx-devel && make install

LinuxディストリビューションでkTLS対応のNGINXをビルドする

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

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を有効にするには、ssl_conf_commandディレクティブにOptionsのKTLSパラメーターを指定し、server{}コンテキストに含めます。以下は、F5のテストで使用した設定例です。

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使用率を削減できる可能性もあります。

パフォーマンスのテスト

お使いの環境でのパフォーマンス向上を測定するには、以下の手順に従って、簡単なシングルスレッドテストを実行してください。以下に詳しく示すとおり、テスト結果では、特別なチューニングを行わなくても最大で約30%のパフォーマンス向上が確認されています。

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

  • 以下を備えたAWS t3.mediumインスタンス:
    • 4 GB RAM
    • 20 GBの汎用SSD
    • Intel® Xeon® Platinum 8259CL CPU @ 2.50GHz、2コア
  • FreeBSD 13.0およびUbuntu 21.10
  • TLSv1.3 with the TLS_AES_256_GCM_SHA384暗号スイートを使用するTLSv1.3
  • NGINX 1.21.4, built and configured as specified in NGINXでのkTLSの有効化の説明に従ってビルドおよび設定されたNGINX 1.21.4。

テストを実行するには、次の手順に従います。

  1. ディスクキャッシュに完全に収まる大きなファイルを作成します:
# truncate -s 1g /data/1G
  1. 次のコマンドを実行してスループットを確認します。より正確な結果を得るため、基本コマンドは複数回繰り返されます。出力を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/秒単位のダウンロード速度です。読みやすいように、出力を2行に分けています。

kTLSを使用しないFreeBSD 13.0のスループット:

    N           Min           Max        Median     ...x  10        532225        573348        555616     ...     ...      Avg        Stddev     ... 555155.6     10239.137

kTLSを使用したFreeBSD 13.0のスループット:

    N           Min           Max        Median     ...x  10        629379        723164        717349     ...         ...      Avg        Stddev   ... 708600.4     28304.766

kTLSを使用しないUbuntu 21.10のスループット:

    N           Min           Max        Median     ...x  10        529199        705720        662354     ...         ...      Avg        Stddev   ... 654321.6     48025.103

kTLSを使用したUbuntu 21.10のスループット:

    N           Min           Max        Median     ...x  10        619105        760208        756278     ...         ...      Avg        Stddev   ... 741848.3     43255.246

F5のテストでは、kTLSによるパフォーマンス向上効果はUbuntuよりもFreeBSDで大きくなりました。向上率は次のとおりです。

 最小最大中央値平均
FreeBSD 13.018%26%29%28%
Ubuntu 21.1016%8%14%13%

まとめ

NGINX 1.21.4では、静的ファイルおよびキャッシュされたレスポンスをSSL_sendfile()で配信する際のkTLSサポートが導入されました。F5のテストでは、オペレーティングシステムに応じてパフォーマンスが8%~29%向上することが確認されています。

kTLSとNGINXを使用したご経験、特に他のOSでのテスト結果をぜひお聞かせください。下のコメント欄で共有をお願いします。


共有
タグ: F5 NGINX, 2021, News, Tech

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります