作者:乔普、申信
介绍
在云原生环境中,集群提供者常常将不同类型的工作负载部署在同一个集群中,利用不同业务的不同峰值效果,实现资源分时复用,避免资源浪费。然而,不同类型负载之间混合部署常常会导致资源竞争和相互干扰。最为典型的场景便是在线和离线负载的混合部署。当离线较多的占用计算资源时,在线负载的响应时间就会受到影响;当在线长时间较多的占用计算资源时,离线负载的任务完成时间不能得到保证。这种现象属于 Noisy Neighbor 问题。
根据混合部署的程度、资源类型的不同,解决该问题有许多不同的思路。Quota 管理可从整个集群维度限制负载的资源使用量,Koordinator 在这方面提供了多层次弹性 Quota 管理功能 [ 1] 。单机维度上看,CPU、内存、磁盘 IO,网络资源都有可能被不同负载共享。Koordinator 在 CPU、内存上已经提供了一些资源隔离和保障的能力,磁盘 IO 和网络资源方面的相关能力正在建设中。
本文主要介绍当不同类型工作负载混合部署在同一个节点上时,Koordinator 如何帮助负载之间(在线和在线、在线和离线)协同地共享 CPU 资源。
问题描述
CPU 资源 Noisy Neighbor 的本质是不同的负载之间无协同地共享 CPU 资源。
- Kubernetes 默认的资源模型利用 cgroup(cfs quota) 从 CPU 时间使用量上来限制不同负载对于 CPU 资源的访问。这种情况下,一些负载就可能会被操作系统调度器切换所在的 CPU 核。由于不同 CPU 核对不同物理位置的内存访问时间不同,切换大概率会导致更长的内存访问时间,从而影响负载性能。
- 在 NUMA 架构中,SMT 线程(逻辑核)共享物理核的执行单元和 L2 缓存。当同一个物理核中有多种工作负载时,不同工作负载间就会产生资源争抢,导致负载性能下降。
Kubernetes 在单机侧提供了拓扑管理器和 CPU 管理器来尝试解决上述问题。然而,该功能只有在 Pod 已经调度到机器上之后才会尝试生效。这样就有可能导致 Pod 会被调度到 CPU 资源满足但是 CPU 拓扑不满足负载要求的情况。
解决方案
面向应用的 CPU 编排 QoS 语义
针对上述问题和不足,Koordinator 设计了面向应用的 QoS 语义和 CPU 编排协议,如下图所示。
![image.png image.png]()
LS(Latency Sensitive)应用于典型的微服务负载,Koordinator 将其与其它的延迟敏感型负载隔离保障其性能。LSR(Latency Sensitive Reserved)类似于 Kubernetes 的 Guaranteed,在 LS 的基础上增加了应用要求预留绑核的语义。LSE(Latency Sensitive Exclusive)则常见于中间件等对 CPU 特别敏感的应用,Koordinator 除了满足其类似于 LSR 要求绑核的语义外,还确保其所被分配的 CPU 不与任何其它负载共享。
另外,为提高资源利用率,BE 负载可与 LSR 和 LS 共享CPU。为了确保与 BE 共享的延迟敏感型应用不受其干扰,Koordinator 提供了如干扰检测、BE 压制等策略。本文重点不在此,读者可关注后续文章。
丰富的 CPU 编排策略
对于 LSE 类型的应用,当机器是超线程架构时,只能保证负载独占逻辑核。这样当同一个物理核中有其它负载时,应用性能仍会受干扰。为此,Koordinator 支持用户在 Pod Annotation 上配置丰富的 CPU 编排策略来提高性能。
CPU 编排策略分为 CPU 绑定策略和 CPU 独占策略。CPU 绑定策略决定应用所被分配逻辑核在物理核间的分布,可采用物理核间打散或者堆叠。堆叠(FullPCPU)的方式指为应用分配完整的物理内核,可以有效地缓解 Noisy Neighbor 问题。打散(SpreadByPCPU)则主要应用于一些具有多种不同峰谷特性的延迟敏感型应用,可以让应用程序在特定时间充分使用 CPU。CPU 独占策略决定应用所被分配逻辑核的独占级别,可尽量避开已经同独占策略申请的物理核或 NUMANode。
增强的 CPU 调度能力
Koordinator 支持配置 NUMA 的分配策略,决定在调度时如何选择满意的 NUMA 节点。MostAllocated 表示从可用资源最少的 NUMA 节点分配,可以尽可能减少碎片,为后续的负载留下更大的分配空间。但是,这种方式可能会导致依赖 Barrier 的并行代码性能收到影响。DistributeEvenly 表示在 NUMA 节点上平均分配 CPU,可以提高上述并行代码的性能。LeastAllocated 表示从可用资源最多的 NUMA 节点分配。
另外,Koordinator 对 CPU 的分配逻辑是在中心调度器完成的。这样就会有一个全局的视角,避免了 Kubernetes 单机方案可能导致的 CPU 资源量满足但是拓扑不满足的窘境。
最佳实践
由上文可知,Koordinator 精细化 CPU 编排能力能够显著提高多应用混合部署场景下 CPU 敏感型工作负载的性能。为了让读者能够更清楚地使用和直观感受 Koordinator 的精细化 CPU 编排能力,本文将在线应用采用不同方式部署到集群中,观察压测中服务的延迟,来判断 CPU 编排能力的效果。
本文会在同一个机器上部署多个在线应用,压测 10 分钟,以充分模拟生产实践中可能出现的 CPU 核切换场景。对于在线应用和离线应用混合部署的情况,Koordinator 提供了如干扰检测、BE 压制等策略。本文重点不在此,读者可关注后续文章中的实践。
![image image]()
本次实验采用以下指标,评估应用不同部署方式下 Nginx 应用的性能表现:
- 响应时间 RT(Response Time)分位值RT 是在线应用通常关注的性能指标,RT 越低代表在线服务性能越好。RT 指标通过收集 wrk 压测结束后打印的信息获得,在实验中反映了 Nginx 应用响应 wrk 请求所花费的时间。例如 RT-p50 表示 Nginx 响应前 50% wrk 请求最大所花费的时间(中位数),RT-p90 表示 Nginx 响应前 90% wrk 请求最大所花费的时间。
- 每秒请求数 RPS(Request Per Second)RPS 是在线应用每秒服务的请求数量,服务承受的 RPS 越多代表在线服务的性能越好。
实验结果如下:
![image.png image.png]()
- 对比 B 和 A,可以发现采用 LSE QoS 绑核之后,服务响应时间 P99 明显减小,很好地减轻了长尾现象
- 对比 C 和 B,可以发现采用 LSR QoS 绑核且允许逻辑核占用更多物理核资源之后,在服务响应时间更好的情况下可以承受更多的请求
综上,在线服务部署在同一机器的场景下,采用 koordinator 精细化 CPU 编排能够有效抑制 Noisy Neighbor 问题,减少 CPU 核切换带来的性能下降。
环境
首先,要先准备一个 Kubernetes 集群并安装 Koordinator [ 2] 。本文选择一个 Kubernetes 集群的两个节点来做实验,其中一个节点作为测试机,将运行 Nginx 在线服务器;另一节点作为压测机,将运行客户端的 wrk,向 Nginx 请求 Web 服务,制造压测请求。
在线应用
- 使用 ColocationProfile [ 3] 为应用注入精细化 CPU 编排协议
B 组精细化 CPU 编排协议:
apiVersion: config.koordinator.sh/v1alpha1
kind: ClusterColocationProfile
metadata:
name: colocation-profile-example
spec:
selector:
matchLabels:
app: nginx
# 采用 LSE QoS
qosClass: LSE
annotations:
# 采用物理核间堆叠
scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy":"FullPCPUs"}'
priorityClassName: koord-prod
C 组 CPU 精细化编排协议:
apiVersion: config.koordinator.sh/v1alpha1
kind: ClusterColocationProfile
metadata:
name: colocation-profile-example
spec:
selector:
matchLabels:
app: nginx
# 采用 LSR QoS
qosClass: LSR
annotations:
# 采用物理核间打散且独占物理核
scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy":"SpreadByPCPUs", "preferredCPUExclusivePolicy":"PCPULevel"}'
priorityClassName: koord-prod
- 在线服务本文选用 Nginx 在线服务器,Pod YAML 如下:
---
# nginx应用配置
apiVersion: v1
data:
config: |-
user nginx;
worker_processes 4; # Nginx的Worker个数,影响Nginx Server的并发。
events {
worker_connections 1024; # 默认值为1024。
}
http {
server {
listen 8000;
gzip off;
gzip_min_length 32;
gzip_http_version 1.0;
gzip_comp_level 3;
gzip_types *;
}
}
#daemon off;
kind: ConfigMap
metadata:
name: nginx-conf-0
---
# Nginx实例,作为在线类型服务应用。
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx-0
namespace: default
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- "${node_name}"
schedulerName: koord-scheduler
priorityClassName: koord-prod
containers:
- image: 'koordinatorsh/nginx:v1.18-koord-exmaple'
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 8000
hostPort: 8000 # 压测请求访问的端口。
protocol: TCP
resources:
limits:
cpu: '4'
memory: 8Gi
requests:
cpu: '4'
memory: 8Gi
volumeMounts:
- mountPath: /apps/nginx/conf
name: config
hostNetwork: true
restartPolicy: Never
volumes:
- configMap:
items:
- key: config
path: nginx.conf
name: nginx-conf-0
name: config
- 执行以下命令,部署 Nginx 应用
kubectl apply -f nginx-0.yaml
- 执行以下命令,查看 Nginx 应用的 Pod 状态
kubectl get pod -l app=nginx -o wide
可以看到输出如下,表示 Nginx 应用已经在测试机上正常运行
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-0 1/1 Running 0 2m46s 10.0.0.246 cn-beijing.10.0.0.246 <none> <none>
- 在压测机上,执行以下命令,部署压测工具 wrk
wget -O wrk-4.2.0.tar.gz https://github.com/wg/wrk/archive/refs/tags/4.2.0.tar.gz && tar -xvf wrk-4.2.0.tar.gz
cd wrk-4.2.0 && make && chmod +x ./wrk
压测
- 使用压测工具 wrk,向 Nginx 应用发起压测请求。
# node_ip填写测试机的IP地址,用于wrk向测试机发起压测;8000是Nginx暴露到测试机的端口。
taskset -c 32-45 ./wrk -t120 -c400 -d600s --latency http://${node_ip}:8000/
- 等待 wrk 运行结束后,获取 wrk 的压测结果,wrk 输出格式如下所示。重复多次测试,以获得相对稳定的结果。
Running 10m test @ http://192.168.0.186:8000/
120 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.29ms 2.49ms 352.52ms 91.07%
Req/Sec 0.96k 321.04 3.28k 62.00%
Latency Distribution
50% 2.60ms
75% 3.94ms
90% 5.55ms
99% 12.40ms
68800242 requests in 10.00m, 54.46GB read
Requests/sec: 114648.19
Transfer/sec: 92.93MB
总结
在 Kubernetes 集群中,不同业务负载之间可能存在 CPU、内存等资源的争抢,影响业务的性能和稳定性。面对 Noisy Neighbor 现象,用户可以使用 Koordinator 为应用配置更精细的 CPU 编排策略,使得不同应用可以协同的共享 CPU 资源。我们通过实验说明,Koordinator 的精细化 CPU 编排能力能有效抑制 CPU 资源的争抢,改善应用性能。
非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 Koordinator 开源社区。你是否已经有一些希望与我们社区交流的内容呢?可以通过以下渠道参与讨论:
- 加入社区 Slack channel (English)
- 加入社区钉钉群:搜索群号 33383887 (Chinese)
相关链接:
[1] 多层次弹性 Quota 管理功能
https://koordinator.sh/docs/user-manuals/multi-hierarchy-elastic-quota-management/
[2] 安装 Koordinator
https://koordinator.sh/docs/installation/
[3] ColocationProfile
https://koordinator.sh/docs/user-manuals/colocation-profile/
点击此处,立即了解 Koordinator 项目!