博客 | NGINX

在动态 Kubernetes 云环境中对 NGINX Ingress 控制器进行性能测试

NGINX-F5-horiz-black-type-RGB 的一部分
Amir Rawdat 缩略图
阿米尔·罗达特
2020 年 9 月 22 日发布

编辑– 这篇文章是10 篇系列文章的一部分:

  1. 使用生产级 Kubernetes 降低复杂性
  2. 如何通过高级流量管理提高 Kubernetes 的弹性
  3. 如何提高 Kubernetes 中的可见性
  4. 使用流量管理工具保护 Kubernetes 的六种方法
  5. 选择入口控制器的指南,第 1 部分: 确定您的需求
  6. 选择入口控制器的指南,第 2 部分: 风险与未来保障
  7. 选择入口控制器的指南,第 3 部分: 开源与…… 默认与…… 商业的
  8. 选择入口控制器的指南,第 4 部分: NGINX 入口控制器选项
  9. 如何选择服务网格
  10. 在动态 Kubernetes 云环境中对 NGINX Ingress 控制器进行性能测试(本帖)

您还可以下载完整的博客作为免费的电子书 -将 Kubernetes 从测试带到生产

随着越来越多的企业在生产中运行容器化应用程序,Kubernetes 继续巩固其作为容器编排标准工具的地位。 与此同时,由于新冠疫情引发的在家办公举措加速了互联网流量的增长,云计算需求也提前了几年。 由于客户正遭遇严重的网络中断和过载,因此各公司正在迅速升级其基础设施。

为了在基于云的微服务环境中达到所需的性能水平,您需要快速、完全动态的软件,以利用下一代超大规模数据中心的可扩展性和性能。 许多使用 Kubernetes 管理容器的组织依赖基于 NGINX 的 Ingress 控制器向用户提供他们的应用程序。

在此博客中,我们报告了在真实的多云环境中对三个 NGINX Ingress 控制器进行性能测试的结果,测量了 Internet 上客户端连接的延迟:

测试协议和收集的指标

我们使用负载生成程序wrk2来模拟客户端,在定义的时间段内通过 HTTPS 发出持续请求。 所测试的 Ingress 控制器(社区 Ingress 控制器、基于 NGINX 开源的 NGINX Ingress Controller 或基于 NGINX Plus 的 NGINX Ingress Controller)将请求转发到部署在 Kubernetes Pod 中的后端应用,并将应用生成的响应返回给客户端。 我们生成了稳定的客户端流量来对 Ingress 控制器进行压力测试,并收集了以下性能指标:

  • 延迟——客户端生成请求和接收响应之间的时间量。 我们以百分分布的形式报告延迟。 例如,如果有 100 个来自延迟测试的样本,则第 99 个百分位数的值是 100 次测试运行中响应延迟第二慢的值。
  • 连接超时——由于 Ingress 控制器未能在一定时间内响应请求,TCP 连接被悄悄断开或丢弃。
  • 读取错误——尝试读取连接,但由于来自 Ingress 控制器的套接字已关闭而失败。
  • 连接错误——客户端和 Ingress 控制器之间的 TCP 连接未建立。

拓扑

对于所有测试,我们使用在 AWS 中的客户端机器上运行的wrk2实用程序来生成请求。 AWS 客户端连接到 Ingress 控制器的外部 IP 地址,该控制器作为 Kubernetes DaemonSet部署在 Google Kubernetes Engine (GKE) 环境中的GKE-node-1上。 Ingress 控制器配置为 SSL 终止(引用 Kubernetes Secret )和第 7 层路由,并通过类型LoadBalancer的 Kubernetes服务公开。 后端应用作为 Kubernetes部署GKE-node-2上运行。

有关云机器类型和软件配置的完整详细信息,请参阅附录

测试方法

客户端部署

我们在 AWS 客户端机器上运行了以下wrk2 (版本 4.0.0)脚本。 它产生 2 个wrk线程,共同与 GKE 中部署的 Ingress 控制器建立 1000 个连接。 在每次 3 分钟的测试运行期间,该脚本每秒会生成 30,000 个请求 (RPS),我们认为这可以很好地模拟生产环境中 Ingress 控制器上的负载。

wrk -t2 -c1000 -d180s -L -R30000 https://app.example.com:443/

在哪里:

  • -t – 设置线程数(2)
  • -c – 设置 TCP 连接数(1000)
  • -d – 设置测试运行的持续时间(以秒为单位)(180 秒或 3 分钟)
  • -L – 生成详细的延迟百分位数信息以导出到分析工具
  • -R – 设置 RPS 数量(30,000)

对于 TLS 加密,我们使用了具有2048 位密钥大小和完美前向保密的 RSA。

来自后端应用的每个响应(访问https://app.example.com:443 )包含大约 1 KB的基本服务器元数据,以及200好的HTTP 状态代码。

后端application部署

我们对后端应用的静态和动态部署进行了测试运行。

在静态部署中,有五个 Pod 副本,并且未使用 Kubernetes API 应用任何更改。

对于动态部署,我们使用以下脚本定期将后端nginx部署从 5 个 Pod 副本扩展到 7 个,然后再缩减到 5 个。 这模拟了动态的 Kubernetes 环境并测试了 Ingress 控制器如何有效地适应端点变化。

while [ 1 -eq 1 ]
执行
kubectl scale 部署 nginx --replicas=5
休眠 12
kubectl scale 部署 nginx --replicas=7
休眠 10
完成

性能结果

静态部署的延迟结果

如图所示,所有三个 Ingress 控制器在静态部署后端应用的情况下都实现了类似的性能。 这是有道理的,因为它们都是基于 NGINX 开源,并且静态部署不需要从 Ingress 控制器重新配置。

动态部署的延迟结果

该图显示了动态部署中每个 Ingress 控制器产生的延迟,我们定期将后端应用从 5 个副本 Pod 扩展到 7 个,然后再扩展到 7 个(有关详细信息,请参阅后端应用部署)。

很明显,只有基于 NGINX Plus 的Ingress 控制器在这种环境中表现良好,直到第 99.99 百分位几乎没有延迟。 社区和基于 NGINX 开源的 Ingress 控制器在相当低的百分位数下都会经历明显的延迟,尽管模式有所不同。 对于社区 Ingress 控制器,延迟缓慢但稳定地上升到第 99 个百分位,然后稳定在 5000 毫秒(5 秒)左右。 对于基于 NGINX 开源的 Ingress 控制器,第 99 个百分位的延迟急剧上升至约 32 秒,第 99.99 个百分位的延迟再次飙升至 60 秒

正如我们在动态部署的超时和错误结果中进一步讨论的那样,社区和基于 NGINX 开源的 Ingress 控制器所遇到的延迟是由 NGINX 配置为响应后端应用端点的变化而更新和重新加载后发生的错误和超时引起的。

以下是社区 Ingress 控制器和基于 NGINX Plus 的Ingress 控制器在与上图相同的测试条件下的更细粒度的结果视图。 基于 NGINX Plus 的Ingress 控制器在 99.99 百分位数之前几乎没有引入延迟,而从 99.9999 百分位数开始上升至 254 毫秒。 社区 Ingress 控制器的延迟模式在第 99 个百分位稳步增长到 5000 毫秒的延迟,此时延迟趋于平稳。

动态部署的超时和错误结果

该表更详细地显示了延迟结果的原因。

  NGINX 开源 社区 NGINX Plus
连接错误 33365 0 0
连接超时 309 8809 0
读取错误 4650 0 0

使用基于 NGINX 开源的 Ingress 控制器时,每次对后端应用程序端点进行更改时都需要更新和重新加载 NGINX 配置,这会导致许多连接错误、连接超时和读取错误。 当客户端尝试连接不再分配给 NGINX 进程的套接字时,NGINX 重新加载的短暂时间内会发生连接/套接字错误。 当客户端与 Ingress 控制器建立连接但后端端点不再可用时,就会发生连接超时。 错误和超时都会严重影响延迟,在第 99 个百分位时延迟会飙升至 32 秒,在第 99.99 个百分位时又会飙升至 60 秒

使用社区 Ingress 控制器时,由于后端应用的扩大和缩小导致端点发生变化,从而导致 8,809 次连接超时。 社区 Ingress 控制器使用 Lua 代码来避免端点发生变化时重新加载配置。 结果表明,在 NGINX 内部运行 Lua 处理程序来检测端点变化解决了基于 NGINX 开源版本的一些性能限制,这些限制是由于它需要在每次更改端点后重新加载配置。 尽管如此,连接超时仍然会发生,并导致较高百分位数的显著延迟。

使用基于 NGINX Plus 的Ingress 控制器不会出现错误或超时——动态环境几乎对性能没有影响。 这是因为它使用 NGINX Plus API 在端点发生变化时动态更新 NGINX 配置。 如上所述,最高延迟为 254 毫秒,并且仅发生在 99.9999 百分位数。

结论

性能结果表明,为了完全消除动态 Kubernetes 云环境中的超时和错误,Ingress 控制器必须动态调整以适应后端端点的变化,而无需事件处理程序或配置重新加载。 根据结果,我们可以说NGINX Plus API是在动态环境中动态重新配置 NGINX 的最佳解决方案。 在我们的测试中,只有基于 NGINX Plus 的Ingress 控制器在高度动态的 Kubernetes 环境中实现了完美的性能,而这正是让您的用户满意所需要的。

附录

云端机器规格

机器 云提供商 机器类型
客户 AWS m5a.4xlarge
GKE-节点-1 地理信息中心 e2-标准-32
GKE-节点-2 地理信息中心 e2-标准-32

NGINX 开源和 NGINX Plus Ingress 控制器的配置

Kubernetes 配置

apiVersion:apps/v1
种类: DaemonSet
元数据:
名称:nginx-ingress
命名空间:nginx-ingress
规范:
选择器:
匹配标签:
应用程序:nginx-ingress
模板:
元数据:
标签:
应用程序:nginx-ingress
#注释:
#prometheus.io/scrape:“true”
#prometheus.io/port: “9113”
规范:
serviceAccountName:nginx-ingress
nodeSelector:
kubernetes.io/hostname:gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork:true
容器:
- 图像:gcr.io/nginx-demos/nap-ingress:edge
imagePullPolicy: 始终
名称:nginx-plus-ingress
端口:
- 名称:http
容器端口: 80
主机端口: 80
- 名称:https
容器端口: 443
主机端口: 443
- 名称:readiness-port
容器端口: 8081
#- 名称:prometheus
#containerPort: 9113
readinessProbe:
httpGet:
路径:/nginx-ready
端口:readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
功能:
删除:
- 全部
添加:
- NET_BIND_SERVICE
环境:
- 名称: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- 名称: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -nginx-plus
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret

笔记:

  • 此配置适用于 NGINX Plus。 在 NGINX 开源配置中对nginx‑plus 的引用已根据需要进行调整。
  • NGINX App Protect 包含在图像中( gcr.io/nginx-demos/nap-ingress:edge ),但已被禁用(省略了-enable-app-protect标志)。

配置图

种类: ConfigMap
apiVersion:v1
元数据:
名称:nginx-config
命名空间:nginx-ingress
数据:
worker-connections: “10000”
worker-rlimit-nofile: “10240”
保持活动: “100”
保持活动请求: “100000000”

社区 NGINX Ingress Controller 的配置

Kubernetes 配置

apiVersion:apps/v1
种类: DaemonSet
元数据:
标签:
helm.sh/chart:ingress-nginx-2.11.1
app.kubernetes.io/name:ingress-nginx
app.kubernetes.io/instance:ingress-nginx
app.kubernetes.io/version: 0.34.1
    app.kubernetes.io/管理者: Helm
app.kubernetes.io/component:控制器
名称:ingress-nginx-controller
命名空间:ingress-nginx
规范:
选择器:
matchLabels:
app.kubernetes.io/name:ingress-nginx
app.kubernetes.io/instance:ingress-nginx
app.kubernetes.io/component:控制器
模板:
元数据:
标签:
app.kubernetes.io/name:ingress-nginx
app.kubernetes.io/instance:ingress-nginx
app.kubernetes.io/component:控制器
规范:
nodeSelector:
kubernetes.io/hostname:gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork:true
容器:
- 名称:控制器
图像: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
生命周期:
预停止:
执行:
命令:
- /wait-shutdown
参数:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
功能:
删除:
- 全部
添加:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation:true
环境:
- 名称: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- 名称: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
httpGet:
path: /healthz
port: 10254
方案: HTTP
periodSeconds: 1
端口:
- 名称:http
容器端口: 80
协议: TCP
- 名称:https
容器端口: 443
协议: TCP
- 名称:webhook
容器端口: 8443
协议: TCP
volumeMounts:
-名称:webhook-cert
mountPath:/usr/local/certificates/
readOnly:true
serviceAccountName:ingress-nginx
terminationGracePeriodSeconds: 300
卷:
- 名称:webhook-cert
机密:
机密名称:ingress-nginx-admission

配置图

api版本:v1
种类: ConfigMap
元数据:
名称:ingress-nginx-controller
命名空间:ingress-nginx
数据:
最大工作连接数: “10000”
最大工作者打开文件数: “10204”
上游保持连接: “100”
保持活动请求: “100000000”

后端应用程序的配置

Kubernetes 配置

apiVersion:apps/v1
种类: 部署
元数据:
名称:nginx
规格:
选择器:
匹配标签:
应用程序:nginx
模板:
元数据:
标签:
应用程序:nginx
规格:
节点选择器:
kubernetes.io/主机名:gke-rawdata-cluster-default-pool-3ac53622-t2dz
容器:
-名称:nginx
图像:nginx
端口:
-容器端口: 8080
volumeMounts:
-名称:main-config-volume
mountPath:/etc/nginx
-名称:app-config-volume
mountPath:/etc/nginx/conf.d
readinessProbe:
httpGet:
路径:/healthz
端口: 8080
周期秒: 3
卷:
- 名称:main-config-volume
configMap:
名称:main-conf
- 名称:app-config-volume
configMap:
名称:app-conf
---

配置映射

api版本:v1
种类: ConfigMap
元数据:
名称:main-conf
命名空间:默认
数据:
nginx.conf:|+
用户 nginx;
worker_processes 16;
worker_rlimit_nofile 102400;
worker_cpu_affinity auto 11111111111111111;
error_log /var/log/nginx/error.log 通知;
pid /var/run/nginx.pid;

事件 {
worker_connections 100000;
}

http {

log_format main'$remote_addr - $remote_user [$time_local]“$request”'
'$status $body_bytes_sent“$http_referer”'
'“$http_user_agent”“$http_x_forwarded_for”';

sendfile on;
tcp_nodelay 开启;

access_log 关闭;

include /etc/nginx/conf.d/*.conf;
}

---

apiVersion: v1
kind: ConfigMap
元数据:
名称:app-conf
命名空间:默认
数据:
app.conf:“server {listen 8080;location / {default_type text/plain;expires -1;return 200 '服务器地址:$server_addr:$server_port\n服务器名称:$hostname\n日期:$time_local\nURI:$request_uri\n请求 ID:$request_id\n';}location /healthz {return 200 '我很快乐、很健康 :)';}}”
---

服务

api版本:v1
种类: 服务
元数据:
名称:app-svc
规格:
端口:
- 端口: 80
目标端口: 8080
协议: TCP
名称:http
选择器:
应用程序:nginx
---

“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”