找回密码
 会员注册
查看: 18|回复: 0

QA玩转K8S系列(二):从服务高可用分析滚动更新中可能踩的坑及6个case分析

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64101
发表于 2024-10-11 21:28:00 | 显示全部楼层 |阅读模式
Kubernetes(简称k8s)是一种流行的容器编排工具,其中Deployment控制器提供了滚动更新(Rolling Update)的功能, 滚动更新是一种逐步替换pod的方式,可以无缝地将应用程序从旧版本切换到新版本。在k8s的滚动更新中,maxUnavailable和maxSurge是两个重要的参数,它们对滚动更新过程中的服务的稳定性和更新速度具有重要影响。本文将通过实例来探讨maxUnavailable和maxSurge参数的功能和影响,以及如何正确设置它们以实现理想的滚动更新策略。滚动更新原理Deployment是工作在ReplicaSet控制器之上的控制器,ReplicaSet作为副本控制器,它来确保pod的副本数量维持在预设的个数。Deployment就是通过控制不同版本的ReplicaSet来实现pod的滚动更新的。具体地,当Deployment需要更新新版本的应用时,它会创建一个新的ReplicaSet,然后,逐步在这个新的ReplicaSet中创建pod,同时减少旧版本ReplicaSet 中的 pod的数量,直到所有旧版本pod都被替换为止。在滚动更新过程中,用户可以通过RollingUpdateStrategy策略来配置滚动更新过程中的参数属性,以确保滚动更新的平滑进行,其中有两个比较重要的参数:maxSurge和maxUnavailable。参数的解释maxUnavailable:在更新期间最大不可用 pod 数。值可以是绝对数(例如:5)也可以是预期副本数的百分比(例如:10%)。如果是百分比的话,计算最大不可用pod数时需要向下取整。默认为 25%。示例:当设置为 30% 时,当滚动更新开始时,旧的 ReplicaSet 可以立即缩减到所需 pod 的 70%。一旦新的 pod 准备就绪,旧的 ReplicaSet 可以进一步缩减,然后扩大新的 ReplicaSet,确保在更新期间始终可用的 pod 总数至少是所需 pod 的 70%。maxUnavailable翻译过来是“最大不可用数”,参数定义了滚动更新期间允许不可用的pod的最大数量。在滚动更新期间,当新版本的pod正在部署时,旧版本的pod可能会不可用。通过设置maxUnavailable参数,可以控制不可用的pod数量,从而保证集群中应用程序的可用性。maxSurge:可以超出预期副本数进行调度的最大pod数。值可以是绝对数(例如:5)或是预期副本数的百分比(例如:10%)。如果是百分比的话,计算可超出预期副本数pod数时需要向下取整。默认为 25%。示例:当设置为 30% 时,新的 ReplicaSet可以在滚动更新开始时立即扩展,使新旧 pod 的总数不超过所需 pod 的 130%。一旦旧 pod 被杀死,新的 ReplicaSet 可以进一步扩展,确保在更新期间任何时候运行的 pod 的总数最多为所需 pod 的 130%。maxSurge 参数定义了滚动更新期间可以同时部署的新版本pod数量与当前副本数的最大差异。这意味着可以控制一下滚动更新过程中新版本pod的部署速度。maxSurge的作用在于保证滚动更新过程中不会突然部署过多的新版本pod,从而避免资源的浪费和集群的压力过大。这两个参数使得k8s在更新过程中会保证:①.Ready pod数不低于 desired pods number - maxUnavailable;②.所有pod数不多于 desired pods number + maxSurge实践效果那这两个参数的具体作用效果是怎样的呢?我们通过实际的例子看下。Case1首先我们创建一个Deployment,副本数设置为20。滚动更新的maxUnavailable和maxSurge都采用k8s的默认值25%, 通过kubectl get rs -w来watch ReplicaSet的变化,可以观测到滚动更新的过程。可以看到Deployment滚动更新实际是依靠新旧ReplicaSet交接棒完成的,更新过程分成两步:Scale up和Scale down。Scale up负责将新rs的replicas朝着deployment.Spec.Replicas指定的数据递加。Scale down负责将旧的replicas朝着0的目标递减。一次完整的滚动更新需要经过很多轮Scale up和Scale down 的过程。为了更直观的说明整个更新过程,我们将更新过程中新旧两个ReplicaSet每一步更新的期望pod数(DESIRED)当前pod数(CURRENT)和就绪pod数(READY)分别进行了绘制,下图给出了滚动更新过程中pod数量的变化情况。在创建过程中,最大超出副本数是20*25%=5个,从CURRENT图中,可以看到最大的pod数量是25。最大不可用数量是20*25%=5,在READY中可以看到,更新过程中,ready的pod数最小为20-5=15。可以看到maxUnavailable参数决定了滚动更新时系统最少的可用pod数,这个值会影响到我们系统的稳定性,如果在滚动更新过程中pod数量降低,就会增加现有pod的负载,如果maxUnavailable过大,可以提供服务的pod数量过少,一旦性能击穿,就有可能造成服务可用性的降低。Case2既然maxUnavailable决定了系统的稳定性,那为了保证系统稳定,这个值是不是越低越好呢?我们调整下maxUnavailable的参数,看看maxUnavailable变化会产生什么样的情况。这次我们设置maxUnavailable =75%,maxSurge保持25%不变。可以看到当maxUnavailable提高后,整个滚动更新调整的步数降低了,也就是更新的速度更快了。这是因为旧版本应用replicasetA第一时间删除了更多的pod,给新版本应用replicasetB腾出了更多的"坑位",让新版本pod更快的创建出来。那同样地,如果我们降低maxUnavailable,虽然提高了系统滚动过程中可用的pod数量,但是会降低我们的更新速度。而在一些新老版本无法兼容的情况下,新老版本并存时会存在一定的失败率,那么更新时间久,就会导致用户体验的降低。Case3滚动更新速度还跟maxSurge参数有关,在保持maxUnavailable =25%的情况下,我们调整maxSurge增到到75%,也就是就是更新过程中最多有同时有35个pod在运行。可以看到当我们提高maxSurge之后,第一时间并行申请创建的新版本服务(replicasetB)的pod数量增加了,也就能更快地获得更多ready的pod,进而促进旧版本pod的释放,加快了整体上pod替换的速度。但是,这种方式也有它的“副作用”,我们看到更新过程中同时运行的pod数量增加到了35个,那也就是说我们在集群当中需要更多的资源来创建pod。当集群资源有限的情况下,虽然我们在第一时间申请创建了更多的新版本的pod,但是并不意味这这些pod能快速进入ready状态,此时即便调大maxSurge,也不会增加更新的速度。Case4我们再看一种情况,这次我们将maxUnavailable 和maxSurge都调大到75%,maxUnavailable增大旧版pod释放的速度,maxSurge增大新版pod创建的速度,那两者同时增大会不会带来pod快速、稳定更新的这种双赢的结果呢?怎么样?结果出乎在意料之外又在情理之中。从图中我们看到,同时调大两个参数,并没有实现新pod快速替换旧pod的预期。因为replicasetB在动态调整的过程中,desired的值最大也就是咱们设置的新版本的副本数了。我们设置了maxSurge=75%能容纳35个pod,而maxUnavailable快速释放出15个pod旧,那么给新pod的坑位就有30个之多,但是新pod最多也就能创建20个,所以maxSurge起到加快新pod创建速度的作用就会被限制。我们可以简单的分析出,当maxUnavailable + maxSurge > 期望的副本数 时,maxSurge 加快发布速度的作用就被限制了,实际发布效果上来看,此时的效果和 maxSurge = 副本数- maxUnavailable 的实际效果会比较类似。还有需要注意的,maxSurgeS和maxUnavailable不能同时为0,其实很好理解,这种情况下相当于既不给新pod申请额外的坑位,又不给新pod腾挪坑位,导致滚动更新无法实现。通过上面4个实例,我们总结出:maxUnavailable 控制更新过程ready的pod数大于 预期副本数 - maxUnavailable,决定第一时间旧pod释放多少,调大maxUnavailable,可以给新pod“腾出”更多的坑位,来加快新旧版本更新,是一种先下后上的方案。需要关注的是,要实现评估当达到最小pod数时系统是否能扛得住系统的压力,防止性能击穿带来的服务不可用。maxSurge 控制更新过程中pod的整体数量的最大值,包括创建中和创建成功各种状态的pod。调大maxSurge,可以给新pod“申请”更多的坑位,来加快新旧版本的更新,是一种先上后下的方案。这种方案看似快速稳定,但是会给集群带来额外的压力,同时效果也受集群资源限制,如果在集群整体资源多项目共用或者集群资源情况不可见的情况下,还是建议降低maxS,考虑通过调整maxUnavailable来实现快速更新。我们实验了新旧版本副本数相同时的更新情况,如果新版本和旧版本的副本数不同,也就是在更新过程中还包含了扩缩容,这种情况下有哪些值得注意的情况呢?Case5先创建一个5副本的应用,然后我们把它滚动更新到20副本的新版本中,maxUnavailable和maxSurge都采用默认值25%。此时的最大不可用pod数需要用新版本应用的副本数来计算,结果为 20*25%=5个,那在更新开始时,是不是就会对replicasetA进行缩容,从5个旧pod中删除全部5个旧pod,导致服务不可用呢?从实际结果上来看,并不是这个样子,而是先将旧版本应用replicasetA进行扩容,扩容的预期 = 最终的副本数 = 20 ,那么新版本replicasetB的副本数是 20 * 125% - 20 = 5个。从Deployment Controller核心逻辑 syncDeployment函数中也可以看到,deployment会先判断是否需要scale再去进行RollingUpdate,也就是扩缩容的优先级要高于更新操作。当我们理解maxUnavailable参数时,不妨直接把它转化为最小可用副本数进行设置,集群最小可用副本数 = 最终副本数 - 最大不可用pod数,以此来判断集群是否会因为pod过少而出现服务中断。在实际的代码实现中,也是先计算minAvailable在决定缩容多少pod的。Case6反过来有另外一个例子,我们希望调大单pod的资源配额降低pod数量,来达到降低限流比的目的。那是否可以在一次滚动更新中实现呢?我们先建一个20个副本的应用,然后把它滚动更新到5副本的新版本中,maxSurge和maxUnavailable都采用默认值25%,此时集群最小可用副本数 = 5 - (5*25%) = 4。在滚动更新时就会直接从replicasetA中删除16个pod,此时大量释放旧pod可能会给服务稳定性带来较大的影响。所以此时在一次滚动更新中同时进行资源配额增加和副本数降低两种操作时,一定要注意更新过程中最大不可用pod数的影响。也可以先通过滚动更新修改资源配额,然后再通过缩容来降低副本数。总结正确的设置maxunavailable和maxSurge参数可以实现平滑的滚动更新,确保应用程序的可用性和可靠性。下面是一些最佳实践:a. 在滚动更新之前,建议先进行适当的测试和评估,以更好地理解应用程序的性能和需求,从而合理设置maxunavailable和maxSurge参数。b. 在应用程序的高峰期,可以适当降低maxUnavailable的值,以减少滚动更新过程中的不可用性。c. 为了避免资源的浪费,建议将maxSurge设置为较小的值,以确保滚动更新过程中不会产生过多的新版本Pod,从而减少对集群资源的压力。d. 定期监控滚动更新过程中的性能和结果,及时调整maxUnavailable和maxSurge参数,以达到最佳的滚动更新策略。滚动更新是k8s中的一个重要功能,而maxUnavailable和maxSurge是影响滚动更新过程的关键参数。通过本文的介绍和实践,希望读者能充分理解和正确设置这两个参数,通过合理设置这两个参数,实现平滑的滚动更新过程,避免不必要的不可用性和资源浪费。在实践中,需要根据具体的应用程序需求和资源环境来优化这些参数,持续的监控和调整,将有助于优化应用程序的性能和资源利用率,提高整体的系统可靠性和稳定性。分享给第一个想到的人
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 12:22 , Processed in 0.395265 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表