使用内核TLS和SSL_sendfile( )提升NGINX性能

NGINX | 2021年11月11日

传输层安全性(TLS)是一种应用非常广泛的加密协议。在内核中实现TLS(kTLS),可显著减少用户空间与内核之间的数据复制操作,从而提升性能。

结合使用kTLS和sendfile(),数据会先直接在内核空间中加密,再传递到网络协议栈进行传输。这样就无需将数据复制到用户空间,由TLS库加密后再复制回内核空间进行传输。kTLS还支持将TLS处理卸载到硬件,包括将TLS对称加密处理卸载到网络设备。

现代Linux和FreeBSD内核支持将TLS处理卸载到内核,现在NGINX Open Source也支持这一功能。NGINX 1.21.4新增了对kTLS的支持,可在使用SSL_sendfile()提供静态文件和缓存响应时显著提升性能。如下文所述,内核和OpenSSL都必须在构建时启用kTLS,NGINX才能使用SSL_sendfile()。

本文将详细介绍支持kTLS的操作系统和OpenSSL版本,并说明如何针对kTLS构建和配置内核及NGINX。为帮助您了解kTLS可能带来的性能提升,我们还将分享在FreeBSD和Ubuntu上进行测试时所用的配置及测试结果。

注意:kTLS实现尚处于早期阶段,并且发展迅速。本博客介绍了截至2021年11月对kTLS的支持情况,但请留意nginx.org和NGINX博客<.htmla>发布的公告,以了解本文所提供信息和说明的变更。

一般要求

  • 操作系统:以下任一项
    • FreeBSD 13.0+。截至2021年11月,FreeBSD 13.0+是唯一无需手动构建NGINX以集成OpenSSL 3.0.0+,即可在NGINX中支持kTLS的操作系统。请参阅“在FreeBSD上为NGINX启用kTLS”。
    • 基于 Linux 内核 4.17 或更高版本构建的 Linux 发行版,但条件允许时,建议使用基于 5.2 或更高版本构建的发行版。(实际上,4.13 版本已支持 kTLS,但 OpenSSL 3.0.0 要求内核头文件版本为 4.17 或更高版本。)
  • OpenSSL,版本3.0.0或更高版本
  • NGINX,版本1.21.4或更高版本(主线版)[编辑:NGINX Plus R27及更高版本在符合条件的Linux操作系统版本上支持kTLS;NGINX Plus R26及更高版本在符合条件的FreeBSD版本上支持kTLS。有关受支持操作系统的详细信息,请参阅NGINX Plus的“版本发布”页面。]

操作系统支持

支持kTLS的操作系统

截至2021年11月,在NGINX Open Source支持的操作系统中,以下操作系统支持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

* 内核版本必须为5.10,而非4.14;请参阅不支持kTLS的操作系统和Amazon Linux 2常见问题解答
** 其上游源为RHEL 8,因此沿用RHEL 8的kTLS支持状态
*** 请参阅FreeBSD提交日志

不支持kTLS的操作系统

以下操作系统因所述原因不支持kTLS:

  • Alpine Linux 3.11–3.14:内核使用CONFIG_TLS=n选项构建,因此无法将kTLS构建为模块或内核的一部分。
  • Amazon Linux 2:默认Amazon Linux 2 AMI的Linux内核版本为4.14(请参阅Amazon Linux 2常见问题解答)。
  • CentOS 7.4+:Linux内核版本为3.10。由于其上游源为RHEL 7.4+,因此沿用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的操作系统对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的操作系统”)

要验证NGINX二进制文件中启用了哪些OpenSSL支持的TLS密码套件,请在构建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)手册页。
  • 在Linux发行版上,以root用户身份运行此命令:
# modprobe tls

在FreeBSD上为NGINX启用kTLS

要在FreeBSD上的NGINX中启用kTLS支持,可以使用与Linux发行版相同的说明。不过,建议按照以下步骤操作,以使用security/openssl-devel port中支持kTLS的NGINX构建版本。该port位于FreeBSD Ports Collection中。有关更多信息(包括kTLS概述),请参阅FreeBSD网站上的内核中的TLS卸载。

  1. 构建支持kTLS的OpenSSL 3.0,并在配置菜单中选择适当的选项:
# cd /usr/ports/security/openssl-devel && make config && make install
  1. 修改“/etc/make.conf”,以使用“openssl-devel”作为默认SSL库:
# echo "DEFAULT_VERSIONS+=ssl=openssl-devel" >> /etc/make.conf
  1. 构建NGINX:
# cd /usr/ports/www/nginx-devel && make install

在Linux发行版上构建支持kTLS的NGINX

目前大多数Linux发行版附带的OpenSSL版本低于3.0.0(通常为1.1版)。因此,需要使用OpenSSL 3.0.0从源代码构建NGINX。

用于启用kTLS支持的两个关键configure命令选项是:

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

其他configure选项适用于NGINX官方二进制软件包中包含的模块,这些软件包可从nginx.org获取。也可以改为指定一组自定义模块。要查看当前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{}上下文中,如我们用于测试的以下示例配置所示:

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()可使吞吐量最高提升至 2 倍(与用户空间TLS相比),但实际性能提升幅度在很大程度上取决于多种因素(磁盘性能、系统负载等)。如果网卡支持TLS卸载,还可降低CPU使用率。

测试性能

要衡量您的配置可获得的性能提升,请按照以下说明运行简单的单线程测试。如下文所述,我们的测试结果表明,无需任何特定调优,性能提升幅度可接近30%。

使用的硬件和软件:

  • AWS t3.medium实例,配置如下:
    • 4 GB RAM
    • 20 GB通用型SSD
    • Intel® Xeon® Platinum 8259CL CPU,主频 2.50 GHz,配备 2 个核心
  • FreeBSD 13.0和Ubuntu 21.10
  • 采用TLS_AES_256_GCM_SHA384密码套件的TLSv1.3
  • NGINX 1.21.4,按照在NGINX中启用kTLS中的说明构建和配置。

执行测试:

  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/秒。为便于阅读,输出分为两行。

未启用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

在我们的测试中,kTLS在FreeBSD上的性能提升幅度高于Ubuntu。具体提升百分比如下:

 最小值最大值中位数平均值
FreeBSD 13.018%26%29%28%
Ubuntu 21.1016%8%14%13%

总结

NGINX 1.21.4新增了对kTLS的支持,可在使用SSL_sendfile()提供静态文件和缓存响应时启用。测试结果显示,性能提升幅度为8%至29%,具体取决于操作系统。

我们很想了解您使用kTLS和NGINX的体验,尤其是在其他操作系统上的测试结果。欢迎在下方评论区分享。


分享
标签: F5 NGINX, 2021, News, Tech

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。