节点自动伸缩
云容器引擎的自动伸缩能力是通过节点自动伸缩组件实现的,可以按需弹出实例。
工作原理
在Kubernetes中,节点自动伸缩的工作原理与传统意义上基于使用率阈值的模型有所差别,这也是很多开发者在从传统的IDC或者其他编排系统迁移到Kubernetes后最难理解的地方。
传统的弹性伸缩模型是基于使用率的,例如:一个集群中有3个节点,当集群中的节点CPU、内存使用率超过特定的阈值时,此时弹出新的节点。但当深入思考时会发现以下几个问题:
- 阈值是如何选择与判断的?
在一个集群中,部分热点节点的利用率会较高,而另外一个节点的利用率会很低。如果选择平均利用率的话可能会造成弹性伸缩的不及时。如果使用最低的节点的利用率,那么也会造成弹出资源的浪费。
- 弹出实例后是如何缓解压力的?
在Kubernetes中,应用是以Pod为最小单元,部署在集群的不同节点上的。当一个Pod资源利用率较高的时候,即便此时所在的节点或者集群的总量触发了弹性扩容,但是该应用的Pod数目,以及Pod对应的Limit没有任何变换,那么负载的压力是无法转移到新扩容出的节点上的。
- 如何判断以及执行实例的缩容?
如果基于资源利用率的方式判断节点是否缩容,那么很有可能出现,Request很大,但是Usage很小的Pod被驱逐,当集群中这种类型的Pod较多时,会导致集群的调度资源被占满,部分Pod无法调度。
Kubernetes节点伸缩是怎么解决以上问题的呢?Kubernetes是通过调度与资源解耦的两层弹性模型来解决的。
基于资源的使用率来触发应用副本的变化,也就是调度单元的变化。而当集群的调度水位达到100%的时候会触发资源层的弹性扩容,当资源弹出后,无法调度的单元会自动调度到新弹出的节点上,从而降低整个应用的负载状况。以下介绍Kubernetes弹性伸缩的技术细节:
- 如何判断节点的弹出?
cluster-autoscaler是通过对处在Pending的Pod进行监听而触发的。当Pod处在Pending的原因是调度资源不足的时候,会触发cluster-autoscaler的模拟调度,模拟调度器会计算在配置的伸缩组中哪个伸缩组弹出节点后可以调度这些Pending的Pod。如果有伸缩组可以满足,那么就弹出相应的节点。
模拟调度就是将一个伸缩组当成一个抽象的Node,伸缩组中配置的机型规格对应会成为Node的CPU/内存/GPU的容量,然后设置伸缩组上面的Label、Taint,也就是Node的Label与Taint。模拟调度器会在调度模拟的时候,将该抽象的Node纳入调度参考。如果Pending的Pod可以调度到抽象的Node,那么就会计算所需的Node的数目,驱动伸缩组弹出节点。
- 如何判断节点的缩容?
首先只有弹性伸缩弹出的节点会被缩容,静态的节点是无法被cluster-autoscaler接管的。缩容的判断是通过每个节点单独判断的。当任意一个节点的调度利用率低于所设置的调度阈值时,会触发节点的缩容判断。此时cluster-autoscaler会尝试模拟驱逐节点上面的负载,判断当前节点是否可以排水彻底。有些特殊的Pod(kube-system命名空间的非DaemonSet Pod、PDB控制的Pod等),则会跳过该节点而选择其他的候选节点。当节点发生驱逐时,会先进行排水,将节点上的Pod驱逐到其他的节点,然后再下线该节点。
- 多个分组之间如何选择?
不同分组之间,实际上相当于不同的虚拟的Node之间的选择,和调度策略一样,这里也存在一个打分的机制。首先符合调度策略的Node会先过滤出来,在符合调度策略的Node中,会根据affinity等亲和性的策略进行选择。如果上述的策略都不存在,默认情况下cluster-autoscaler会通过least-waste的策略来进行抉择。least-waste的策略的核心就是模拟弹出节点后,剩余的资源最少。此外,有一个特别的场景,当有一个GPU的伸缩组和CPU的伸缩组同时可以弹出生效时,默认CPU会优先于GPU弹出。
- 如何提高弹性伸缩的成功率?
弹性伸缩的成功率主要取决如下两个因素:
1、调度策略是否满足
首先在配置好伸缩组后,开发者需要先确认下该伸缩组可以承载的Pod的调度策略范围。如果无法直接判断,最简单的方式是通过nodeSelector直接选择伸缩组的Label进行预弹模拟。
2、资源配置是否充分
当模拟调度通过后,会选择伸缩组进行弹出,但是伸缩组中配置的ECS规格是否有库存会直接决定是否可以成功弹出实例。因此配置多个节点池选择不同的规格可以大大提高弹出成功率。
注意事项
单一规格的ECS库存容量波动较大,建议在伸缩组中配置库存充裕的实例规格,提高节点伸缩成功率。
由于节点标签和污点配置映射到伸缩组Tag后自动伸缩才可识别,并且伸缩组Tag有数目上限限制,开启自动伸缩的节点池配置的ECS标签、污点和节点标签的总数需要控制在一定数量之内。
自动伸缩的节点池避免手动干预,经过弹性伸缩的ECS在移出节点池后,但在伸缩组中还存在,缩容时也会缩该节点
操作步骤
在插件市场选择cube-vertical-pod-autoscaler并安装,安装插件步骤请参考安装插件。
1、安装cube-cluster-autoscaler插件
2、创建节点池,开启自动伸缩
执行上述步骤后,当集群若有因资源不足导致pod pending的情况,插件会自动购买所选规格的节点并加入到集群中。在该节点池详情的伸缩活动页中可查看伸缩记录。插件的配置可以在伸缩组页面自定义修改,如下图所示:
常见问题
为什么节点自动伸缩组件无法弹出节点?
请检查是否存在如下几种场景:
1、配置伸缩组的实例规格无法满足Pod的资源申请(Request)。或者实例规则是已售罄的规格,实际运行时请考虑以下资源预留。
- 在创建实例的过程中因为虚拟化、OS占用掉一部分。
- 需要占用一定的节点资源来运行相关组件(例如kubelet、kube-proxy、calico、Container Runtime等)。
- 默认节点会安装系统组件,Pod的申请资源要小于实例的规格。
2、开启自动伸缩的节点池中出现如下异常情况。
- 实例未加入到集群且超时。
- 节点未ready且超时。
为保证后续扩缩准确性,弹性插件以阻塞方式处理异常情况,在处理完异常情况节点前,不进行扩缩容。
为什么节点自动伸缩组件无法缩容节点?
请检查是否存在如下几种场景:
- 节点Pod的资源申请(Request)阈值高于设置的缩容阈值。
- 节点上运行kube-system命名空间的Pod。
- 节点上的Pod包含强制的调度策略,导致其他节点无法运行此Pod。
- 节点上的Pod拥有PodDisruptionBudget,且到达了PodDisruptionBudget的最小值。
多个伸缩组在弹性伸缩的时候是如何被选择的?
在Pod处在无法调度时,会触发弹性伸缩组件的模拟调度逻辑,会根据伸缩组配置的标签和污点以及实例规格等信息进行判断。当配置的伸缩组可以模拟调度Pod的时候,就会被选择进行节点弹出。当同时有多个伸缩组满足模拟调度条件的时候,根据配置的策略选择。默认采用的是最少浪费原则,即根据模拟弹出后节点上剩余的资源最小为原则进行抉择。