实例在新建时,调度器可以根据当时集群状态选择最优节点进行调度,但集群内资源使用状况是动态变化的,集群在一段时间内就会出现不均衡的状态,需要 Descheduler 将节点上已经运行的 pods 迁移到其他节点,使集群内资源分布达到一个比较均衡的状态。有以下几个原因我们希望将节点上运行的实例迁移到其他节点:
descheduler 会根据相关的策略挑选出节点需要迁移的实例然后删除实例,新实例会重新通过 kube-scheduler 进行调度到合适的节点上。descheduler 迁移实例的策略需要与 kube-scheduler 的策略共同使用,二者是相辅相成的。
使用 descheduler 的目的主要有两点,一是为了提升集群的稳定性,二是为了提高集群的资源利用率。
针对以上几个问题,descheduler 提供了一系列策略让使用者可以根据一些规则和配置来重新平衡集群状态,达到一个理想的效果,其核心原理是根据策略配置找到可以被迁移的 pod 并驱逐它们,其本身并不会进行调度被驱逐的 Pod,而是依靠默认的调度器来实现,当前支持的策略有十种:
策略 | 描述 |
---|---|
RemoveDuplicates | 将节点上同类型的Pod进行迁移,确保只有一个Pod与同一节点上运行的ReplicaSet、Replication Controller、StatefulSet或者Job关联。 |
LowNodeUtilization | 将 requests 比率较高节点上的Pod进行迁移。 |
HighNodeUtilization | 将 requests 比率较低节点上的Pod进行迁移。 |
RemovePodsViolatingInterPodAntiAffinity | 将不满足反亲和性的Pod进行迁移。 |
RemovePodsViolatingNodeAffinity | 将不满足节点节点亲和性策略的Pod进行迁移。 |
RemovePodsViolatingNodeTaints | 将不满足节点污点策略的Pod进行迁移。 |
RemovePodsViolatingTopologySpreadConstraint | 将不满足拓扑分布约束的Pod进行迁移。 |
RemovePodsHavingTooManyRestarts | 将重启次数过多的Pod进行迁移。 |
PodLifeTime | 将运行时间较长的Pod进行迁移。 |
RemoveFailedPods | 将运行失败的Pod进行迁移。 |
高利用率节点的迁移策略示例:
其他策略示例:
以上就是针对社区 Descheduler 组件的介绍。
在了解了 descheduler 的策略之后会发现,社区 descheduler 的功能远远不能满足公司内部生产环境的需要,首先以 Remove 开头的几个策略其实在大部分场景是不需要的,如果集群内的资源足够通过配置反亲和性策略是可以将同一 workload 下所有 Pod 打散的。LowNodeUtilization 和 HighNodeUtilization 策略可以根据实际的场景进行扩展,社区这两种策略是根据节点的分配率 request 比率来进行决策的,只基于分配率进行 Pod 迁移整体而言效果相对一般,无法达到生产环境的要求,在大部分生产环境下需要使用 node 真实使用率来进行 Pod 迁移。
针对 LowNodeUtilization 策略的扩展,除了将社区 request 分配率指标改为基于节点真实的 cpu 使用率外,在分析了生产环境的数据之后还添加了一些额外的功能。策略在实际使用过程中进行了多次演进,从最初的使用社区的周期性巡检机制进行触发,但该策略有一定的滞后性,真实场景如果节点利用率过高则要尽快进行处理,针对节点高利用率场景为了提高时效性,策略在扩展时直接对接了内部的监控系统,通过告警回调触发节点上面 Pod 的迁移,最后针对部分无法在高峰期进行 Pod 迁移的场景,也设计了节点利用率预测算法,在高峰期前提前预测高利用率节点执行 Pod 迁移操作。
该策略直接了当,通过监控发现高利用率节点后直接触发节点上 Pod 的迁移,通过告警配置对策略调整也比较灵活,配置完之后直接生效也不需要升级 descheduler 组件。如果高利用率节点比较多可以将报警策略配置在标准阈值之下以激进的方式进行处理,在生产环境中,策略不管是激进还是保守都可能无法达到最好的效果,最终需要将策略调整到一个稳健的状态。
生产环境中业务会有变更的时间窗口,部分高优服务 Pod 迁移的操作也需要和业务的变更时间窗口保持一致,避免因 Pod 迁移影响了业务的稳定性,针对这部分无法在高峰期进行 Pod 迁移的场景,需要在高峰期来临前提前预测到高利用率节点并进行处理,当前预测算法的实现主要是参考了社区 VPA 组件的推荐算法,基于时间序列的一个预估算法,算法的核心是获取节点上所有实例最近8天的利用率数据,再按天计算得到每天的P95数据,最后对每天的P95数据使用不同的权重系数加权计算得到最终的预测结果。
预测数据需要做大量的分析验证,发现对准确性有影响的因素,在实际验证过程中发现影响预测结果的主要有两点,一是服务下不同 Pod cpu 分层问题,服务下所有 Pod 在同一个时间点的 cpu 利用率不一致,出现这种情况可能的原因有:
二是节点上实例数量的变化,如果预测了节点的利用率结果之后,节点上面有实例的新建与销毁操作也会影响预测的准确性。当前节点利用率预测功能也在不断优化中,此处不再对细节进行详细的说明。
实际场景中,某些 request 分配率比较高但实际使用率比较低的节点上有很多实例运行的时间比较久,业务长期不会进行变更导致实例不会通过销毁重建的方式自动进行重调度,针对此场景可以尝试通过迁移 Pod 来提高节点的利用率。
针对 HighNodeUtilization 策略的扩展比 LowNodeUtilization 策略简单一些,主要是将 request 比率替换为了实际的使用率,再就是会在下文讲到两个策略都需要用的一些约束机制与效果分析机制。
由于生产环境中场景复杂,Pod 迁移对业务来说也是一个有损的操作,在迁移过程中必须要做好必要的防范措施,需要配置一些约束策略来保障业务的稳定性。
尽管 k8s 可以通过配置 PDB(PodDisruptionBudget)来避免对象的副本被同时驱逐,不过我们认为 PDB 不够精细化,在跨集群场景中也无法更好的运用,此处会通过一个全局的约束限制模块让服务的 Pod 在重调度过程中对服务影响尽可能的小以及尽可能的均衡负载有问题的节点,保证业务不中断或业务SLA不降级。
当前在 Pod 迁移过程中有多种策略来进行约束 ,在宏观策略上,主要是针对全局的约束策略,在不同的集群与资源池中,Pod 迁移时的速率以及一个周期内迁移 Pod 总数量会有限制,Pod 迁移时间窗口、迁移时是否跨集群等策略也有一定的限制,每个集群与资源池也会配置黑白名单。在微观策略上,主要是针对节点和服务的约束策略,节点与服务 Pod 迁移速率与一个周期内迁移 Pod 总数量也有限制,在迁移时挑选服务下 Pod 也会针对 Pod 状态以及服务等级做一些限制。
二次调度在功能完善之后,还需要一套效果评估机制,如果二次调度服务运行一段时间后,迁移了很多 Pod,但因为其他因素的影响,高利用率节点数量没有降低的话需要怎么分析呢?
经过对线上数据的分析,最终使用了三个指标对二次调度进行效果评估。第一个是高利用率节点的发现率,指的是二次调度能发现的高利用率节点数量,与通过公司的标准采集到的高利用率节点数量的一个比例,理论上二者的结果应该是接近的,实际上在执行的过程发现报警策略使用的算法无法与公司统计标准使用的算法保持一致、配置的报警敏感度低以及Pod 迁移窗口限制等原因导致发现率是偏低的。
第二个是高利用率节点Pod驱逐率,驱逐率指的是发现高利用率节点之后,能够在节点上挑选出合适的 Pod 并进行驱逐。有些高利用率节点因为服务相关约束、实例挑选算法不合理或者全局策略约束等原因无法驱逐节点上面的 Pod。
第三个是高利用率节点Pod驱逐有效率。如果驱逐了高利用率节点上面的Pod节点利用率没有降低到一定阈值,那也是不符合预期的。主要有三个原因会导致驱逐有效率低,第一个是因为各种约束导致驱逐的 Pod 不是最优的,节点利用率下降不明显,第二个就是 Pod 被驱逐后,节点上很快会有新 Pod 被调度上来了,第三个是节点上部分 Pod 利用率变高了。
最后就是对全集群高利用率节点数量的分析,针对全集群高利用率节点的数据,也会按资源池与集群维度进行拆分建立以上三个指标数据,理论上高利用率节点发现率、驱逐率和有效率三个指标如果都比较符合预期全集群高利用率节点的比例是会控制在一定范围内的。
以上就是二次调度效果分析的一个流程。
当前主要是通过二次调度组件自身的一些数据进行了分析,全集群的高利用率节点还与业务流量、集群容量、调度器策略等因素相关,部分业务有运营活动时集群整体的资源利用率都会提升,集群资源紧张时调度器针对得分较低的节点也会将实例调度上去,这些都会对高利用率节点数量造成影响。
本文主要介绍了当前社区 descheduler 组件的基本功能以及在生产环境中 descheduler 的一些缺陷和一些扩展策略,在使用 descheduler 迁移 Pod 时还需要对业务的稳定性做一些保障措施,组件功能在完善之后还需要对服务自身进行的一些效果评估机制。descheduler 的基本功能是基于一些策略对 Pod 进行迁移,在生产环境中有很多场景需要对 node 上的 Pod 做一些迁移操作,比如需要重启节点的内核升级操作、不同机房进行的机器搬迁操作,还有针对 k8s 组件进行升级的场景中(运行时的切换等) 都可以用 descheduler 添加一些策略进行支持。未来会结合生产环境继续在 descheduler 里面添加其他的策略,比如基于节点干扰率场景的 Pod 迁移,基于服务特征识的反亲和策略进行 Pod 迁移。