精细化 CPU 编排

koord-scheduler 为了提升 CPU 密集型工作负载的性能提供了精细化 CPU 编排能力。

Introduction

越来越多的系统利用 CPU 和硬件加速器的组合来支持实时计算和高吞吐的并行计算。 许多应用程序都需要高性能环境,包括电信、科学计算、机器学习、金融服务和数据分析。

但是,Kubernetes 集群中的 Pod 在多种资源维度上都是共享的,存在相互干扰的问题。 CPU 资源的共享几乎是不可避免的,例如 SMT 线程(即逻辑处理器)共享同一个物理核,同一个芯片中的物理核共享同一个 L3 缓存。 资源竞争会减慢这些对 CPU 敏感的工作负载的运行质量,从而导致延迟升高。

为了提高对 CPU 敏感的工作负载的性能,koord-scheduler 提供了一种精细化的 CPU 编排机制。 它增强了 Kubernetes 的 CPU 管理,并支持详细的 NUMA 局部性和 CPU 排除。

有关详细信息,请参阅设计:细粒度 CPU 编排。

设置

前置条件

  • Kubernetes >= 1.18
  • Koordinator >= 0.6

安装

请确保 Koordinator 组件已正确安装在你的集群中。 如果没有,请参考安装文档。

配置全局参数

精细化 CPU 编排能力是默认开启的。用户不需要额外的配置即可使用。

对于需要深入定制的用户,可以按需修改 Helm Chart 中的配置文件 koord-scheduler-config 设置精细化 CPU 编排的参数。

1. apiVersion: v1
2. kind: ConfigMap
3. metadata:
4. name: koord-scheduler-config
5. ...
6. data:
7. koord-scheduler-config: |
8. apiVersion: kubescheduler.config.k8s.io/v1
9. kind: KubeSchedulerConfiguration
10. profiles:
11. - schedulerName: koord-scheduler
12. - pluginConfig:
13. - name: NodeNUMAResource
14. args:
15. apiVersion: kubescheduler.config.k8s.io/v1
16. kind: NodeNUMAResourceArgs
17. # The default CPU Binding Policy. The default is FullPCPUs
18. # If the Pod belongs to LSE/LSR Prod Pods, and if no specific CPU binding policy is set,
19. # the CPU will be allocated according to the default core binding policy.
20. defaultCPUBindPolicy: FullPCPUs
21. # the scoring strategy
22. scoringStrategy:
23. # the scoring strategy ('MostAllocated', 'LeastAllocated')
24. # - MostAllocated(default): prefer the node with the least available resources
25. # - LeastAllocated: prefer the node with the most available resources
26. type: MostAllocated
27. # the weights of each resource type
28. resources:
29. - name: cpu
30. weight: 1
31. plugins:
32. # enable the NodeNUMAResource plugin
33. preFilter:
34. enabled:
35. - name: NodeNUMAResource
36. filter:
37. enabled:
38. - name: NodeNUMAResource
39. ...
40. score:
41. enabled:
42. - name: NodeNUMAResource
43. weight: 1
44. ...
45. reserve:
46. enabled:
47. - name: NodeNUMAResource
48. preBind:
49. enabled:
50. - name: NodeNUMAResource

koord-descheduler 是通过 Configmap 加载调度器配置的。因此需要通过重启调度器才能使用最新的配置。

字段 说明 版本
--- --- ---
defaultCPUBindPolicy 默认的 CPU 绑定策略。 默认值为 FullPCPUs。 如果 Pod 属于 LSE/LSR Prod Pod,并且没有设置具体的 CPU 绑定策略,CPU 则会按照默认的 CPU 绑定策略进行分配。 可选值为 FullPCPUs 和 SpreadByPCPUs >= v0.6.0
scoringStrategy 打分策略,可选值为 MostAllocated 和 LeastAllocated >= v0.6.0

按节点配置

用户可以单独的为节点设置不同的 CPU 绑定策略和 NUMA Node 选择策略。

CPU 绑定策略

Label node.koordinator.sh/cpu-bind-policy 约束了调度时如何按照指定的策略分配和绑定CPU。具体的值定义如下:

值 描述 版本
--- --- ---
None or empty 不执行任何策略。 >= v0.6.0
FullPCPUsOnly 要求调度器必须分配完整的物理核。等价于 kubelet CPU manager policy option full-pcpus-only=true. >= v0.6.0
SpreadByPCPUs 要求调度器必须按照物理核维度均匀的分配逻辑核。 >= v1.1.0

如果节点 Label 上没有 node.koordinator.sh/cpu-bind-policy,调度器将会按照 Pod 指定的 CPU 绑定策略或者调度器配置的默认策略分配 CPU。

NUMA Node 选择策略

Label node.koordinator.sh/numa-allocate-strategy 表示调度时应该如何选择 NUMA Node。具体的值定义如下:

值 描述 版本
--- --- ---
MostAllocated MostAllocated 表示选择资源剩余最少的 NUMA Node。 >= v.0.6.0
LeastAllocated LeastAllocated 表示选择资源剩余最多的NUMA Node。 >= v.0.6.0

如果 node.koordinator.sh/numa-allocate-strategy 和 kubelet.koordinator.sh/cpu-manager-policy 都设置了, 优先使用 node.koordinator.sh/numa-allocate-strategy。

使用精细化 CPU 编排

  1. 按照下面的 YAM了 创建 Deployment nginx。

使用精细化 CPU 编排时,Pod 需要在 Label 中指定具体的 QoSClass 并指定具体的绑定策略。

1. apiVersion: apps/v1
2. kind: Deployment
3. metadata:
4. name: nginx-lsr
5. labels:
6. app: nginx-lsr
7. spec:
8. replicas: 3
9. selector:
10. matchLabels:
11. app: nginx-lsr
12. template:
13. metadata:
14. name: nginx-lsr
15. labels:
16. app: nginx-lsr
17. koordinator.sh/qosClass: LSR # set the QoS class as LSR, the binding policy is FullPCPUs by default
18. # in v0.5, binding policy should be specified.
19. # e.g. to set binding policy as FullPCPUs (prefer allocating full physical CPUs of the same core):
20. #annotations:
21. #scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy": "FullPCPUs"}'
22. spec:
23. schedulerName: koord-scheduler # use the koord-scheduler
24. containers:
25. - name: nginx
26. image: nginx
27. resources:
28. limits:
29. cpu: '2'
30. requests:
31. cpu: '2'
32. priorityClassName: koord-prod
  1. 创建 nginx deployment 并检查调度结果。
1. $ kubectl create -f nginx-deployment.yaml
2. deployment/nginx-lsr created
3. $ kubectl get pods -o wide | grep nginx
4. nginx-lsr-59cf487d4b-jwwjv   1/1     Running   0       21s     172.20.101.35    node-0   <none>         <none>
5. nginx-lsr-59cf487d4b-4l7r4   1/1     Running   0       21s     172.20.101.79    node-1   <none>         <none>
6. nginx-lsr-59cf487d4b-nrb7f   1/1     Running   0       21s     172.20.106.119   node-2   <none>         <none>
  1. 检查 Pod 的 CPU 分配结果 scheduling.koordinator.sh/resource-status.
1. $ kubectl get pod nginx-lsr-59cf487d4b-jwwjv -o jsonpath='{.metadata.annotations.scheduling\.koordinator\.sh/resource-status}'
2. {"cpuset":"2,54"}

我们可以看到 Pod nginx-lsr-59cf487d4b-jwwjv 绑定了 2 个逻辑核,对应的逻辑核 ID 分别是 2 和 54,这两个逻辑核属于同一个物理核。

  1. 更改 nginx deployment 的 CPU 绑定策略。
1. apiVersion: apps/v1
2. kind: Deployment
3. metadata:
4. name: nginx-lsr
5. labels:
6. app: nginx-lsr
7. spec:
8. replicas: 3
9. selector:
10. matchLabels:
11. app: nginx-lsr
12. template:
13. metadata:
14. name: nginx-lsr
15. labels:
16. app: nginx-lsr
17. koordinator.sh/qosClass: LSR # set the QoS class as LSR
18. annotations:
19. # set binding policy as SpreadByPCPUs (prefer allocating physical CPUs of different cores)
20. scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy": "SpreadByPCPUs"}'
21. spec:
22. schedulerName: koord-scheduler # use the koord-scheduler
23. containers:
24. - name: nginx
25. image: nginx
26. resources:
27. limits:
28. cpu: '2'
29. requests:
30. cpu: '2'
31. priorityClassName: koord-prod
  1. 更新 nginx deployment 并检查调度结果。
1. $ kubectl apply -f nginx-deployment.yaml
2. deployment/nginx-lsr created
3. $ kubectl get pods -o wide | grep nginx
4. nginx-lsr-7fcbcf89b4-rkrgg   1/1     Running   0       49s     172.20.101.35    node-0   <none>         <none>
5. nginx-lsr-7fcbcf89b4-ndbks   1/1     Running   0       49s     172.20.101.79    node-1   <none>         <none>
6. nginx-lsr-7fcbcf89b4-9v8b8   1/1     Running   0       49s     172.20.106.119   node-2   <none>         <none>
  1. 检查 Pod 最新的 CPU 分配结果 scheduling.koordinator.sh/resource-status。
1. $ kubectl get pod nginx-lsr-7fcbcf89b4-rkrgg -o jsonpath='{.metadata.annotations.scheduling\.koordinator\.sh/resource-status}'
2. {"cpuset":"2-3"}

现在我们可以看到 Pod nginx-lsr-59cf487d4b-jwwjv 绑定了两个逻辑核,对应的 ID 分别是 2,3, 属于两个不同的物理核。

  1. (可选) 高级配置.
1. labels:
2. # koordinator QoS class of the pod. (use 'LSR' or 'LSE' for binding CPUs)
3. koordinator.sh/qosClass: LSR
4. annotations:
5. # `resource-spec` indicates the specification of resource scheduling, here we need to set `preferredCPUBindPolicy`.
6. # `preferredCPUBindPolicy` indicating the CPU binding policy of the pod ('None', 'FullPCPUs', 'SpreadByPCPUs')
7. # - None: perform no exclusive policy
8. # - FullPCPUs(default): a bin-packing binding policy, prefer allocating full physical cores (SMT siblings)
9. # - SpreadByPCPUs: a spread binding policy, prefer allocating logical cores (SMT threads) evenly across physical cores (SMT siblings)
10. scheduling.koordinator.sh/resource-spec: '{"preferredCPUBindPolicy": "FullPCPUs"}'