在最近的K8s服务上线过程中,我发现了一些问题,更具体的说,我在使用阿里云k8s的过程中注意到:会出现slb短时RT增加,Pod部署初期就达到了扩容上限,并且开始大量的扩容,这无疑占用了大量的k8s资源。实际上,大部分情况下,pod只需要2个,但结果却扩容到了几十个,这是一个问题。
为了解决这问题,我查阅了一些相关文档并整理出了一些解决方法。其中,更新应用时,如何实现 K8s 零中断滚动更新?一文详析在 K8s 中更新应用时为何会发生服务中断以及如何避免?https://mp.weixin.qq.com/s/ceqNDxOs-m-iXj980kNQXw这篇文档给了我许多启发。
首先,对于pod来说,新建和删除是自然的,新建pod的时候需要保证服务能够完全启动并可以正常响应请求,然后就可以将pod所在的node挂到service上了。这里,我们只需要配置了就绪检查就能保证pod的可用。
然后就是删除pod的操作,这里面可能会出现pod收到SIGTERM信号停止工作后,没能从EndPoints中移除。这就会导致service把正常的请求跳转到已经停止工作的pod上。对于这种情况,我建议我们可以为pod设置一个preStop Hook,让 Pod 在收到 SIGTERM 时,先sleep一段时间,而不是立刻停止工作。这样就能保证从SLB转发过来的流量还可以继续被pod处理。
当然还有一种时就是pod已经是termintaing状态,但是 iptables/ipvs条目清理和service移除后端node这两个操作之间存在时间差,虽然他们是同时进行的,但是对于大量请求,总会有部分请求在这细小的时间差之间成为漏网之鱼。对于这种情况,咱们可以分开说,首先是Cluster 模式下 kube-proxy会将所有业务 Pod 写入 Node 的 iptables/ipvs 中,如果当前 Node 没有业务 pod,则该请求会被转发给其他 Node,因此不会存在服务中断(总之目前来看使用cluster模式可以忽略这种中断可能性),还有一种就是Local 模式下的kube-proxy,这种模式会导致请求中断,中断原因主要与cluster和local这两种外部流量策略有关,详细的可以参考阿里的这个文档:
使用Service对外暴露应用_容器服务 Kubernetes 版 ACK-阿里云帮助中心 (aliyun.com)
最后,我们还遇到了在上线的时候,服务启动一开始,发现pod所使用的资源很高。这就导致了pod数量的扩容,对于这类问题,我们其实也可以解决。主要的方法就是在服务上线的时候,设置监控不去读取新启动的pod的资源使用情况。 我从两个维度进行了尝试,包括集群维度和工作负载维度。
集群维度
在集群维度,我们可以通过升级ACK提供的最新版metrics-server,并在其启动参数上开启开关防止多弹,这是全局开关,设置后对集群内所有相关负载生效。
这是全局开关,设置后对集群内所有相关负载生效。
##在metrics-server的启动参数中加入以下选项。
--enable-hpa-rolling-update-skipped=true
工作负载维度:
而在工作负载维度上,我们有两种方法:
方法一:通过在指定工作负载的模板中添加以下Annotation,可以在滚动发布时临时暂停HPA的判断生效。
##工作负载的spec.template.metadata.annotations加入Annotation,滚动发布时临时暂停HPA的判断生效。
HPARollingUpdateSkipped: "true"
方法二:通过在指定工作负载的模板中添加以下Annotation,可以在应用发布开始阶段跳过设定的预热时间段。
##工作负载的spec.template.metadata.annotations加入Annotation,在应用发布开始阶段跳过设定的预热的时间段。
HPAScaleUpDelay: 3m # 3m仅为示例,具体时间段请按需设置
详细的可以参考阿里云的文档:容器服务ACK弹性伸缩的常见问题及解决办法_容器服务 Kubernetes 版 ACK-阿里云帮助中心