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 博客上的公告。
通用要求
-
操作系统 —— 以下任一一个:
-
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.log
2021/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 Stddev
x 10 532225 573348 555616 555155.6 10239.137
支持 kTLS 的 FreeBSD 13.0 的吞吐量:
N Min Max Median Avg Stddev
x 10 629379 723164 717349 708600.4 28304.766
支持 kTLS 的 Ubuntu 21.10 的吞吐量:
N Min Max Median Avg Stddev
x 10 529199 705720 662354 654321.6 48025.103
支持 kTLS 的 Ubuntu 21.10 的吞吐量:
N Min Max Median Avg Stddev
x 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 使用体验,尤其是您在其他操作系统上的测试结果!您可以在下方的评论区中分享您的结果。