TLS(传输层安全协议)是一种极为受欢迎的加密协议。在内核 (kernel) 中实施 TLS (即kTLS) 可显著降低在用户空间与内核之间复制操作的需求,从而提高 NGINX 的性能。
通过结合使用 kTLS 和 sendfile()
,数据可以直接在内核空间加密,然后再传递到网络堆栈进行传输。这使得我们不再需要将数据复制到用户空间、利用 TLS 库进行加密、再返回内核空间进行传输的这一过程。kTLS 还可以将 TLS 的处理过程卸载到硬件,包括将 TLS 对称加密处理任务卸载到网络设备。
现代 Linux 和 FreeBSD 内核支持将 TLS 卸载到内核,而 NGINX 开源版现在也同样能做到!NGINX 1.21.4 在使用 SSL_sendfile()
传输静态文件时引入了 kTLS 支持,可以极大地改善性能。如下所述,内核和 OpenSSL 只有采用 kTLS 构建,才能让 NGINX 使用 SSL_sendfile()
。
本文详细介绍了支持 kTLS 的操作系统以及 OpenSSL 版本,并展示了如何针对 kTLS 构建和配置内核与 NGINX。为了向您展示 kTLS 的性能改善效果,我们还将分享在 FreeBSD 和 Ubuntu 上进行测试的具体说明和结果数据。
注:kTLS 的实施这一新兴事物正在迅速发展中。本文描述了截至 2021 年 11 月 NGINX 对 kTLS 的支持情况,但本文提供的信息和说明可能会在之后进行更新,请随时关注 nginx.org 和 NGINX 博客<.htmla>上的公告。
通用要求
操作系统 —— 以下任一一个:
FreeBSD 13.0+。截至 2021 年 11 月,FreeBSD 13.0+ 是唯一一个在 NGINX 中支持 kTLS 的操作系统,且无需手动构建 NGINX 即可整合 OpenSSL 3.0.0+。请参阅在 FreeBSD 上启用支持 kTLS 的 NGINX。
可以使用在 Linux 内核版本 4.17 或更高版本上构建的 Linux 发行版,但我们建议尽可能使用在版本 5.2 或更高版本上构建的发行版。(实际上,版本 4.13 便提供了 kTLS 支持,但 OpenSSL 3.0.0 需要内核头版本 4.17 或更高版本。)
OpenSSL —— 版本 3.0.0 或更高版本
NGINX —— 版本 1.21.4 或更高版本(主线版)
操作系统支持
支持 kTLS 的操作系统
截至 2021 年 11 月,在 NGINX 开源版支持的所有操作系统中,以下操作系统支持 kTLS 和指定的密码。有关密码支持的详细信息,请参阅 TLS 协议和密码支持。
TLSv1.2 密码 | TLSv1.3 密码套件 | TLS_CHACHA20_POLY1305_SHA256 密码 | Linux 内核版本 | |
---|---|---|---|---|
Amazon Linux 2* | ✅ | ✅ | ❌ | 5.10 |
CentOS 8** | ✅ | ❌ | ❌ | 4.18 |
FreeBSD 13.0 | ✅ | ✅ | ❌ *** | N/A |
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 的 kTLS 支持特性
*** 请参阅 FreeBSD 操作日志
不支持 kTLS 的操作系统
以下操作系统不支持 KTLS,原因如下:
- Alpine Linux 3.11–3.14 —— 内核使用
CONFIG_TLS=n
选项构建,该选项禁止将 kTLS 构建为模块或内核的一部分。 - Amazon Linux 2 —— 默认 Amazon Linux 2 AMI(请参阅 Amazon Linux 2 常见问题解答)的 Linux 内核版本是 4.14。
- 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 的操作系统对 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 库对其加密、再返回内核空间进行传输的这一过程的必要性。
加载内核中的 kTLS
在现代 FreeBSD 和 Linux 发行版中,kTLS 通常被构建为一个模块(使用 CONFIG_TLS=m
选项)。在启动 NGINX 之前,必须将 kTLS 模块显式加载到内核。
在 FreeBSD 上,以
root
用户身份运行以下命令:# kldload ktls ocf# sysctl kern.ipc.tls.enable=1
有关 FreeBSD 命令选项的详细信息,请参阅
ktls(4)
的手册页。在 Linux 发行版上,以
root
用户身份运行以下命令:# modprobe tls
在 FreeBSD 上启用支持 kTLS 的 NGINX
如要在 FreeBSD 上启用 NGINX 的 kTLS 支持,您可以使用与 Linux 发行版相同的操作说明。但我们建议您执行以下步骤,以便在 FreeBSD 端口集合的 openssl-devel 树中使用支持 kTLS 的 NGINX。有关更多信息(包括 kTLS 概述),请参阅 FreeBSD 网站上的将 TLS 卸载到内核。
-
构建支持 kTLS 的 OpenSSL 3.0,在配置菜单中选择适当的选项:
# cd /usr/ports/security/openssl-devel && make config && make install
修改 /etc/make.conf,将 openssl-devel 用作 SSL 库:
# echo "DEFAULT_VERSIONS+=ssl=openssl-devel >> /etc/make.conf
-
构建 NGINX:
# cd /usr/ports/www/nginx-devel && make install
在 Linux 发行版上构建支持 kTLS 的 NGINX
大多数当前的 Linux 发行版都包含早于 3.0.0(通常是版本 1.1)的 OpenSSL 版本。因此,您需要使用 OpenSSL 3.0.0 从源代码构建 NGINX。
configure
命令中启用 kTLS 支持的两个关键选项是:
--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,在 server{}
中的 ssl_conf_command
指令中添加 Options
KTLS
参数,如我们的测试中使用的示例配置:
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(): 1
2021/11/10 16:02:49 [debug] 274550#274550: *3 BIO_get_ktls_send(): 1
$ grep SSL_sendfile /var/log/nginx/error.log
2021/11/10 16:02:46 [debug] 274550#274550: *2 SSL_sendfile: 1048576
2021/11/10 16:02:49 [debug] 274550#274550: *3 SSL_sendfile: 1048576
注: 我们建议您在进行这些检查后关闭调试模式(尤其是在生产环境中)。由于存在大量的写入操作,调试日志会导致性能下降;此外,调试日志可能很大,很快就会耗尽磁盘分区上的可用空间。
借助 kTLS 提高性能
当在高负载下提供静态文件时,与用户空间 TLS 相比,SSL_sendfile()
可将吞吐量提高最多 2 倍,但性能提升的幅度在很大程度上取决于多种因素(磁盘性能、系统负载等)。如果您的网卡支持 TLS 卸载,它还可以减少 CPU 使用率。
测试性能
如要测量您的设置的性能提升情况,可以依循以下说明来运行简单的单线程测试。如下所述,我们的测试结果表明,无需进行任何特定调整,kTLS 即可将性能提升近 30%。
使用的硬件和软件:
- AWS t3.medium 实例采用了:
- 4 GB RAM
- 20 GB 通用 SSD
- 英特尔® 至强® 白金 8259CL CPU,2.50GHz 双核处理器
- FreeBSD 13.0 和 Ubuntu 21.10
- TLSv1.3,采用
TLS_AES_256_GCM_SHA384
密码套件 - NGINX 1.21.4,根据启用 NGINX 中的 kTLS 部分的要求构建和配置。
执行测试的方法:
创建一个完全适合磁盘缓存的大文件:
# truncate -s 1g /data/1G
-
运行以下命令检查吞吐量;多次重复执行基本命令以获得更准确的结果。将输出结果输出到
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 Avg Stddevx 10 532225 573348 555616 555155.6 10239.137
支持 kTLS 的 FreeBSD 13.0 的吞吐量:
N Min Max Median Avg Stddevx 10 629379 723164 717349 708600.4 28304.766
支持 kTLS 的 Ubuntu 21.10 的吞吐量:
N Min Max Median Avg Stddevx 10 529199 705720 662354 654321.6 48025.103
支持 kTLS 的 Ubuntu 21.10 的吞吐量:
N Min Max Median Avg Stddevx 10 619105 760208 756278 741848.3 43255.246
在我们的测试中,与 Ubuntu 相比,FreeBSD 上的 kTLS 提升的性能更高。性能改进比例如下:
最小值 | 最大值 | 中位数 | 平均数 | |
---|---|---|---|---|
FreeBSD 13.0 | 18% | 26% | 29% | 28% |
Ubuntu 21.10 | 16% | 8% | 14% | 13% |
结语
NGINX 1.21.4 在使用 SSL_sendfile()
提供静态文件时引入了 kTLS 支持。我们的测试表明,NGINX 性能可提高 8% 到 29%,具体因操作系统而异。
我们很想知道您的 kTLS 和 NGINX 使用体验,尤其是您在其他操作系统上的测试结果!您可以在下方的评论区中分享您的结果。