Kubernetes调度器性能调优
1、概述
在 Kubernetes 中,调度是指将 Pod 放置到合适的 Node 上,然后对应 Node 上的 Kubelet 才能够运行这些 pod。调度器通过 kubernetes 的监测(Watch)机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。 调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。 调度器会依据下文的调度原则来做出调度选择。如果你想要理解 Pod 为什么会被调度到特定的 Node 上,或者你想要尝试实现 一个自定义的调度器,这篇文章将帮助你了解调度。
2、kube-scheduler
kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群控制面的一部分。 如果你真的希望或者有这方面的需求,kube-scheduler 在设计上是允许你自己写一个调度组件并替换原有的 kube-scheduler。
对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的 Node 去运行这个 Pod。然而,Pod 内的每一个容器对资源都有不同的需求,而且 Pod 本身也有不同的资源需求。因此,Pod 在被调度到 Node 上之前, 根据这些特定的资源调度需求,需要对集群中的 Node 进行一次过滤。
在一个集群中,满足一个 Pod 调度请求的所有 Node 称之为可调度节点。 如果没有任何一个 Node 能满足 Pod 的资源请求,那么这个 Pod 将一直停留在未调度状态直到调度器能够找到合适的 Node。
调度器先在集群中找到一个 Pod 的所有可调度节点,然后根据一系列函数对这些可调度节点打分, 选出其中得分最高的 Node 来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做 绑定。
在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局域性、负载间的干扰等等。
3、kube-scheduler 调度流程
kube-scheduler 给一个 pod 做调度选择包含两个步骤:
- 过滤
- 打分
过滤阶段会将所有满足 Pod 调度需求的 Node 选出来。 例如,PodFitsResources 过滤函数会检查候选 Node 的可用资源能否满足 Pod 的资源请求。 在过滤之后,得出一个 Node 列表,里面包含了所有可调度节点;通常情况下, 这个 Node 列表包含不止一个 Node。如果这个列表是空的,代表这个 Pod 不可调度。
在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node。 根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。
最后,kube-scheduler 会将 Pod 调度到得分最高的 Node 上。 如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。
支持以下两种方式配置调度器的过滤和打分行为:
- 调度策略 允许你配置过滤的断言(Predicates) 和打分的 优先级(Priorities) 。
- 调度配置 允许你配置实现不同调度阶段的插件, 包括:
QueueSort
,Filter
,Score
,Bind
,Reserve
,Permit
等等。 你也可以配置 kube-scheduler 运行不同的配置文件。
3.1 过滤阶段
官方文档:https://kubernetes.io/docs/reference/scheduling/policies/
在调度时的过滤阶段到底时通过什么规则来对Node进行过滤的呢?就是通过以下规则!
-
PodFitsHostPorts:检查Node上是否不存在当前被调度Pod的端口(如果被调度Pod用的端口已被占用,则此Node被Pass)。
-
PodFitsHost:检查Pod是否通过主机名指定了特性的Node (是否在Pod中定义了nodeName)。
-
PodFitsResources:检查Node是否有空闲资源(如CPU和内存)以满足Pod的需求。
-
PodMatchNodeSelector:检查Pod是否通过节点选择器选择了特定的Node (是否在Pod中定义了nodeSelector)。
-
NoVolumeZoneConflict:检查Pod请求的卷在Node上是否可用 (不可用的Node被Pass)。
-
NoDiskConflict:根据Pod请求的卷和已挂载的卷,检查Pod是否合适于某个Node (例如Pod要挂载/data到容器中,Node上/data/已经被其它Pod挂载,那么此Pod则不适合此Node)
-
MaxCSIVolumeCount::决定应该附加多少CSI卷,以及是否超过了配置的限制。
-
CheckNodeMemoryPressure:对于内存有压力的Node,则不会被调度Pod。
-
CheckNodePIDPressure:对于进程ID不足的Node,则不会调度Pod。
-
CheckNodeDiskPressure:对于磁盘存储已满或者接近满的Node,则不会调度Pod。
-
CheckNodeCondition:Node报告给API Server说自己文件系统不足,网络有写问题或者kubelet还没有准备好运行Pods等问题,则不会调度Pod。
-
PodToleratesNodeTaints:检查Pod的容忍度是否能承受被打上污点的Node。
-
CheckVolumeBinding:根据一个Pod并发流量来评估它是否合适(这适用于结合型和非结合型PVCs)。
3 .2 打分阶段
官方文档:https://kubernetes.io/docs/reference/scheduling/policies/ 当过滤阶段执行后满足过滤条件的Node,将进行打分阶段。
-
SelectorSpreadPriority:优先减少节点上属于同一个 Service 或 Replication Controller 的 Pod 数量
-
InterPodAffinityPriority:优先将 Pod 调度到相同的拓扑上(如同一个节点、Rack、Zone 等)
-
LeastRequestedPriority:节点上放置的Pod越多,这些Pod使用的资源越多,这个Node给出的打分就越低,所以优先调度到Pod少及资源使用少的节点上。
-
MostRequestedPriority:尽量调度到已经使用过的 Node 上,将把计划的Pods放到运行整个工作负载所需的最小节点数量上。
-
RequestedToCapacityRatioPriority:使用默认资源评分函数形状创建基于requestedToCapacity的ResourceAllocationPriority。
-
BalancedResourceAllocation:优先平衡各节点的资源使用。
-
NodePreferAvoidPodsPriority:根据节点注释对节点进行优先级排序,以使用它来提示两个不同的 Pod 不应在同一节点上运行。scheduler.alpha.kubernetes.io/preferAvoidPods。
-
NodeAffinityPriority:优先调度到匹配 NodeAffinity (Node亲和性调度)的节点上。
-
TaintTolerationPriority:优先调度到匹配 TaintToleration (污点) 的节点上
-
ImageLocalityPriority:尽量将使用大镜像的容器调度到已经下拉了该镜像的节点上。
-
ServiceSpreadingPriority:尽量将同一个 service 的 Pod 分布到不同节点上,服务对单个节点故障更具弹性。
-
EqualPriority:将所有节点的权重设置为 1。
-
EvenPodsSpreadPriority:实现首选pod拓扑扩展约束
4、调度器性能调优
在大规模集群中,你可以调节调度器的表现来平衡调度的延迟(新 Pod 快速就位) 和精度(调度器很少做出糟糕的放置决策)。
你可以通过设置 kube-scheduler 的 percentageOfNodesToScore
来配置这个调优设置。 这个 KubeSchedulerConfiguration 设置决定了调度集群中节点的阈值。
4.1 设置阈值
percentageOfNodesToScore
选项接受从 0 到 100 之间的整数值。 0 值比较特殊,表示 kube-scheduler 应该使用其编译后的默认值。 如果你设置 percentageOfNodesToScore
的值超过了 100, kube-scheduler 的表现等价于设置值为 100。
要修改这个值,先编辑 kube-scheduler 的配置文件 然后重启调度器。 大多数情况下,这个配置文件是 /etc/kubernetes/manifests/kube-scheduler.yaml。
重启调度器后,可以执行
kubectl get pods -n kube-system | grep kube-scheduler
来检查该 kube-scheduler 组件是否健康。
4.2 节点打分阈值
要提升调度性能,kube-scheduler 可以在找到足够的可调度节点之后停止查找。 在大规模集群中,比起考虑每个节点的简单方法相比可以节省时间。
你可以使用整个集群节点总数的百分比作为阈值来指定需要多少节点就足够。 kube-scheduler 会将它转换为节点数的整数值。在调度期间,如果 kube-scheduler 已确认的可调度节点数足以超过了配置的百分比数量, kube-scheduler 将停止继续查找可调度节点并继续进行打分阶段。
下文 4.5 调度器做调度选择的时候如何覆盖所有的 Node详细介绍了这个过程。
4.3 默认阈值
如果你不指定阈值,Kubernetes 使用线性公式计算出一个比例,在 100-节点集群 下取 50%,在 5000-节点的集群下取 10%。这个自动设置的参数的最低值是 5%。
这意味着,调度器至少会对集群中 5% 的节点进行打分,除非用户将该参数设置的低于 5。
如果你想让调度器对集群内所有节点进行打分,则将 percentageOfNodesToScore
设置为 100。
下面就是一个将 percentageOfNodesToScore
参数设置为 50% 的例子。
apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration algorithmSource: provider: DefaultProvider ... percentageOfNodesToScore: 50
4.4 调节 percentageOfNodesToScore 参数
percentageOfNodesToScore
的值必须在 1 到 100 之间,而且其默认值是通过集群的规模计算得来的。 另外,还有一个 50 个 Node 的最小值是硬编码在程序中。
注意: 当集群中的可调度节点少于 50 个时,调度器仍然会去检查所有的 Node,因为可调度节点太少,不足以停止调度器最初的过滤选择。同理,在小规模集群中,如果你将 percentageOfNodesToScore 设置为一个较低的值,则没有或者只有很小的效果。如果集群只有几百个节点或者更少,请保持这个配置的默认值。改变基本不会对调度器的性能有明显的提升。
值得注意的是,该参数设置后可能会导致只有集群中少数节点被选为可调度节点, 很多节点都没有进入到打分阶段。这样就会造成一种后果, 一个本来可以在打分阶段得分很高的节点甚至都不能进入打分阶段。
由于这个原因,这个参数不应该被设置成一个很低的值。 通常的做法是不会将这个参数的值设置的低于 10。 很低的参数值一般在调度器的吞吐量很高且对节点的打分不重要的情况下才使用。 换句话说,只有当你更倾向于在可调度节点中任意选择一个节点来运行这个 Pod 时, 才使用很低的参数设置。
4.5 调度器做调度选择的时候如何覆盖所有的 Node
如果你想要理解这一个特性的内部细节,那么请仔细阅读这一章节。
在将 Pod 调度到节点上时,为了让集群中所有节点都有公平的机会去运行这些 Pod, 调度器将会以轮询的方式覆盖全部的 Node。 你可以将 Node 列表想象成一个数组。调度器从数组的头部开始筛选可调度节点, 依次向后直到可调度节点的数量达到 percentageOfNodesToScore
参数的要求。 在对下一个 Pod 进行调度的时候,前一个 Pod 调度筛选停止的 Node 列表的位置, 将会来作为这次调度筛选 Node 开始的位置。
如果集群中的 Node 在多个区域,那么调度器将从不同的区域中轮询 Node, 来确保不同区域的 Node 接受可调度性检查。如下例,考虑两个区域中的六个节点:
Zone 1: Node 1, Node 2, Node 3, Node 4 Zone 2: Node 5, Node 6
调度器将会按照如下的顺序去评估 Node 的可调度性:
Node 1, Node 5, Node 2, Node 6, Node 3, Node 4
在评估完所有 Node 后,将会返回到 Node 1,从头开始。
5、总结
kube-scheduler 给一个 pod 做调度选择包含两个步骤:过滤-》打分,过滤阶段会将所有满足 Pod 调度需求的 Node 选出来,在过滤之后,得出一个 Node 列表,里面包含了所有可调度节点;打分阶段建立在过滤阶段之上,为每个符合调度的Node进行打分,分值越高则被调度到该Node的机率越大。最后,kube-scheduler 会将 Pod 调度到得分最高的 Node 上, 如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。
在大规模集群中,可以通过设置kube-scheduler 的 percentageOfNodesToScore参数项来调节调度器的表现来平衡调度的延迟(新 Pod 快速就位)和精度(调度器很少做出糟糕的放置决策)以进行调度器调优,对于小规模的集群不建议进行调度器调优,另外需要注意对于当集群中的可调度节点少于 50 个时,即使设置了kube-scheduler 的 percentageOfNodesToScore参数项仍然会去检查所有的 Node,因为可调度节点太少,不足以停止调度器最初的过滤选择。
参考:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/kube-scheduler/
参考:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/scheduler-perf-tuning/
参考:https://blog.csdn.net/Dou_Hua_Hua/article/details/108076641