编者:本文是由10篇文章组成的系列文章之一:
- 借助生产级Kubernetes降低复杂性
- 如何通过高级流量管理提升Kubernetes的韧性
- 如何提高Kubernetes中的可观测性
- 使用流量管理工具保护Kubernetes的六种方法
- Ingress Controller选择指南(第1部分):明确需求
- Ingress Controller选择指南(第2部分):风险与面向未来的规划
- Ingress Controller 选择指南,第3部分:开源、默认与商业方案对比
- Ingress Controller 选择指南,第4部分:NGINX Ingress Controller 选项
- 如何选择服务网格
- 在动态Kubernetes云环境中对NGINX Ingress Controller进行性能测试(本文)
还可以免费下载包含完整系列博客的电子书《推动Kubernetes从测试走向生产》。
随着越来越多的企业在生产环境中运行容器化应用,Kubernetes作为容器编排标准工具的地位不断巩固。与此同时,COVID‑19疫情推动了居家办公计划,加速了互联网流量增长,使云计算需求提前了数年。由于客户正遭遇严重的网络中断和过载问题,各公司正在迅速升级基础设施。
要在基于云的微服务环境中达到所需的性能水平,需要采用快速、完全动态的软件,充分利用新一代超大规模数据中心的可扩展性和性能。许多使用Kubernetes管理容器的组织依靠基于NGINX的Ingress控制器向用户交付应用。
本文将介绍我们在真实多云环境中对三款NGINX Ingress控制器进行性能测试的结果,测试衡量了客户端通过互联网建立连接时的延迟:
- 由Kubernetes社区维护、基于NGINX Open Source的NGINX Ingress Controller。与之前的博客一样,本文将其称为社区版Ingress控制器。我们使用从Google Container Registry拉取的镜像测试了0.34.1版。
- 基于NGINX Open Source的NGINX Ingress Controller,版本为1.8.0,由NGINX维护。
- 基于NGINX Plus的NGINX Ingress Controller,版本为1.8.0,由NGINX维护。
测试方案和收集的指标
我们使用负载生成程序wrk2模拟客户端,在规定时间段内通过HTTPS持续发送请求。受测的Ingress控制器包括社区Ingress控制器、基于NGINX Open Source的NGINX Ingress Controller或基于NGINX Plus的NGINX Ingress Controller。这些控制器将请求转发到部署在Kubernetes Pod中的后端应用,并将应用生成的响应返回给客户端。我们生成稳定的客户端流量,对Ingress控制器进行压力测试,并收集以下性能指标:
- 延迟:从客户端发出请求到收到响应所需的时间。我们以百分位分布形式报告延迟。例如,如果延迟测试有100个样本,则第99百分位的值是100次测试中第二慢的响应延迟。
- 连接超时:由于Ingress控制器未能在规定时间内响应请求,TCP连接被静默断开或丢弃。
- 读取错误:由于Ingress控制器的套接字已关闭,尝试从连接中读取数据失败。
- 连接错误:客户端与Ingress控制器之间未能建立TCP连接。
拓扑
在所有测试中,我们使用了在AWS客户端计算机上运行的wrk2实用工具来生成请求。该AWS客户端连接到Ingress控制器的外部IP地址。此控制器在Google Kubernetes Engine(GKE)环境中的DaemonSet上以Kubernetes GKE-node-1形式部署。Ingress控制器配置为执行SSL终止(引用Kubernetes Secret)和第7层路由,并通过Kubernetes Service公开,其Type为LoadBalancer。后端应用以Kubernetes Deployment形式在GKE-node-2上运行。

有关云计算机类型和软件配置的完整详情,请参阅“附录”。
测试方法
客户端部署
我们在AWS客户端计算机上运行了以下wrk2(版本4.0.0)脚本。该脚本会启动2个wrk线程,共同与部署在GKE中的Ingress控制器建立1,000个连接。在每次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 OK HTTP状态码。
后端应用部署
我们分别对后端应用的静态部署和动态部署进行了测试运行。
在静态部署中,共有5个Pod副本,且未通过Kubernetes API应用任何更改。
对于动态部署,我们使用以下脚本定期将后端nginx部署的Pod副本数从5个扩展到7个,再缩减回5个。这样可以模拟动态Kubernetes环境,并测试Ingress控制器适应端点变化的效果。
while [ 1 -eq 1 ]
do
kubectl scale deployment nginx --replicas=5
sleep 12
kubectl scale deployment nginx --replicas=7
sleep 10
done性能结果
静态部署的延迟结果
如图所示,在后端应用采用静态部署时,三种Ingress控制器的性能表现相近。这是合理的,因为它们都基于NGINX Open Source,而且静态部署不需要Ingress控制器重新配置。

动态部署的延迟结果
该图显示了动态部署中每个Ingress控制器产生的延迟。在此部署中,我们定期将后端应用从5个副本Pod扩展到7个,然后再缩减到5个(有关详细信息,请参阅“后端应用部署”)。
显然,在此环境中,只有基于NGINX Plus的Ingress控制器表现良好,直至第99.99百分位几乎都没有延迟。社区Ingress控制器和基于NGINX Open Source的Ingress控制器在较低百分位均出现明显延迟,但二者的变化模式截然不同。社区Ingress控制器的延迟缓慢但持续上升,直至第99百分位,并稳定在约5,000 ms(5秒)。基于NGINX Open Source的Ingress控制器在第99百分位时,延迟急剧升至约32秒;到第99.99百分位时,又升至60秒。
正如我们在动态部署的超时和错误结果中进一步讨论的那样,社区版和基于NGINX Open Source的Ingress控制器出现延迟,是因为后端应用的端点发生变化后,NGINX配置随之更新并重新加载,进而引发错误和超时。

下面更细致地展示了社区版Ingress控制器和基于NGINX Plus的Ingress控制器在与上图相同测试条件下的结果。基于NGINX Plus的Ingress控制器在第99.99百分位之前几乎不会引入延迟,之后延迟开始上升,到第99.9999百分位时接近254 ms。社区版Ingress控制器的延迟则持续增加,到第99百分位时达到5,000 ms,随后趋于平稳。

动态部署的超时和错误结果
此表更详细地说明了延迟结果的原因。
NGINX Open Source 社区版 NGINX Plus连接错误 33365 0 0连接超时 309 8809 0读取错误 4650 0 0
使用基于NGINX Open Source的Ingress控制器时,后端应用端点每次发生变化都需要更新并重新加载NGINX配置,因而会导致大量连接错误、连接超时和读取错误。在NGINX重新加载的短暂过程中,如果客户端尝试连接的套接字已不再分配给NGINX进程,就会发生连接或套接字错误。如果客户端已与Ingress控制器建立连接,但后端端点已不可用,就会发生连接超时。错误和超时都会严重影响延迟,第99百分位的延迟会飙升至32秒,第99.99百分位则会再次飙升至60秒。
使用社区版Ingress控制器时,后端应用扩缩容引起端点变化,导致了8,809次连接超时。社区版Ingress控制器使用Lua代码,避免在端点发生变化时重新加载配置。结果表明,在NGINX中运行Lua处理程序来检测端点变化,可以缓解基于NGINX Open Source的版本存在的部分性能限制。这些限制源于该版本要求在每次端点发生变化后重新加载配置。尽管如此,连接超时仍会发生,并在较高百分位造成显著延迟。
使用基于NGINX Plus的Ingress控制器时,没有出现错误或超时,动态环境对性能几乎没有影响。这是因为该控制器使用NGINX Plus API,在端点发生变化时动态更新NGINX配置。如前所述,最高延迟为254 ms,并且仅出现在第99.9999百分位。
结论
性能结果表明,要在动态Kubernetes云环境中彻底消除超时和错误,Ingress控制器必须能够根据后端端点的变化动态调整,而无需使用事件处理程序或重新加载配置。根据这些结果,可以认为NGINX Plus API非常适合在动态环境中对NGINX进行动态重新配置。在我们的测试中,只有基于NGINX Plus的Ingress控制器在高度动态的Kubernetes环境中实现了稳定无误的性能,有助于持续提供令用户满意的体验。
附录
云计算机规格
机器 云提供商 机器类型Client AWS m5a.4xlargeGKE-node-1 GCP e2-standard-32GKE-node-2 GCP e2-standard-32
NGINX Open Source和NGINX Plus Ingress Controller配置
Kubernetes配置
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
#annotations:
#prometheus.io/scrape: "true"
#prometheus.io/port: "9113"
spec:
serviceAccountName: nginx-ingress
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
containers:
- image: gcr.io/nginx-demos/nap-ingress:edge
imagePullPolicy: Always
name: nginx-plus-ingress
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
- name: readiness-port
containerPort: 8081
#- name: prometheus
#containerPort: 9113
readinessProbe:
httpGet:
path: /nginx-ready
port: readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: 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 Open Source的配置中,已根据需要调整对“
nginx‑plus”的引用。 - 该镜像中包含NGINX App Protect(
gcr.io/nginx-demos/nap-ingress:edge),但已被禁用(省略了-enable-app-protect标志)。
ConfigMap
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
namespace: nginx-ingress
data:
worker-connections: "10000"
worker-rlimit-nofile: "10240"
keepalive: "100"
keepalive-requests: "100000000"Community NGINX Ingress Controller 配置
Kubernetes配置
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
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/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
containers:
- name: controller
image: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /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:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 1
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
- name: webhook
containerPort: 8443
protocol: TCP
volumeMounts:
- name: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admissionConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
max-worker-connections: "10000"
max-worker-open-files: "10204"
upstream-keepalive-connections: "100"
keep-alive-requests: "100000000"后端应用配置
Kubernetes 配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-t2dz
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8080
volumeMounts:
- name: main-config-volume
mountPath: /etc/nginx
- name: app-config-volume
mountPath: /etc/nginx/conf.d
readinessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 3
volumes:
- name: main-config-volume
configMap:
name: main-conf
- name: app-config-volume
configMap:
name: app-conf
---ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: main-conf
namespace: default
data:
nginx.conf: |+
user nginx;
worker_processes 16;
worker_rlimit_nofile 102400;
worker_cpu_affinity auto 1111111111111111;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
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 on;
access_log off;
include /etc/nginx/conf.d/*.conf;
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-conf
namespace: default
data:
app.conf: "server {listen 8080;location / {default_type text/plain;expires -1;return 200 'Server address: $server_addr:$server_portnServer name:$hostnamenDate: $time_localnURI: $request_urinRequest ID: $request_idn';}location /healthz {return 200 'I am happy and healthy :)';}}"
---服务
apiVersion: v1
kind: Service
metadata:
name: app-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: nginx
---关于作者
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

