K8S实现零宕机实践

越来越多的大厂都在上云、上容器、上K8S编排,K8S和容器云确实帮助我们解决了很多问题。但是,带来方便的同时,也让我们的架构变得更复杂了,更难于依靠"老经验"来解决问题了。虽然我们不用再费力考虑一层的问题,怎么实现冗余网络和负载平衡、反向代理、监控问题、日志和升级问题,但是你要更加注重配置管理的统一管理、弄好统一策略、安全和合规隐私管理。当然最重要的就是要保证业务的零宕机,本文我们就来说这方面的问题。

概述

万能的K8S并不是万能良药,不能保证万无一失,要保证生产集群高可用性(零宕机)需要精心配置才能起到效果。

最少两个实例

添加健康检查(探针)

应用程序必须处理Sigterm

配置自动缩放器

给予足够的资源

使用pod Anti-affinity

添加PDB

...

容器镜像存储

熟悉容器的同学都知道,使用容器最关键的是镜像,没有镜像一切容器都无从说起。非常简单,可以用docker命令随手一键拉取和运行容器镜像。但是对一个生产集群来说,则就要讲究的多了,不能随便Pull,随便Run镜像了。

如果你使用时一个公有的镜像注册表,则通常你需要的镜像无法得到保证:

一个镜像在注册表可能会随时变化,可能会删除导致无法Pull,这是K8S会抛出magePullBackOff错误警告。

正在使用的镜像标签被删除,同样也会抛出ImagePullBackOff错误告警。

镜像标签没有改变,但镜像内容已经发生了变化(非不可变镜像,因此镜像哈希值不同)。可能会导致集群的不同节点上的镜像之间的行为不相同(取决于标签何时更改并在集群节点上拉取)

它不符合要求控制这些镜像的安全合规要求和版本安全策略。

所以通常需要自建和维护一个镜像注册表,在生产集群中配备该私有镜像地址,可以考虑使用一些工具(比如dregsy)定期进行同步第三方的重要基础镜像。

当部署应用程序时,首先检查私有注册表中是否存在该镜像并使用它。然后会复制镜像到注册表,然后在私有注册表中使用它。这对来说是100%透明的,但可以在部署日志中看到有关它的日志,可以确保不可用的外部容器注册表不会影响的工作负载,或者在扩展时遇到一些配额问题。

Pod数量

为了保障集群的高可用性则一个集群中需要最少2个副本(Pod)。

一个常见错误是:"没有必要不弄两个实例,K8S执行滚动更新,因此会在关闭当前实例之前启动一个新实例"。确实如此,但它仅适用于部署更新过程。

其他更多的场景下则无法保证:

当丢失运行应用程序的节点时(节点崩溃、硬件故障......),应用程序/pod 必须从头开始执行以下步走:

镜像拉取(如果节点上尚未存在):拉取时间取决于镜像大小;

镜像拉取(如果节点上尚未存在):拉取时间取决于镜像大小;

磁盘附件(如果有):需要几秒钟(通常观察最多1分钟);

应用程序启动:可能会有所不同,具体取决于受影响的资源和应用程序类型(Java 应用程序通常需要更长的时间才能启动);

探针:它们正在等待您的应用程序准备好提供服务,并增加了几秒钟的时间

  • 当集群请求节点耗尽时(例如在EKS升级或节点类型更改期间):

  • 将被替换的节点上的Pod正在接收SIGTERM 信号以正常停止。Pod正在从RUNNING 状态切换到TERMINATING 状态。此时,K8S服务已更新,以TERMINATING 停止向pod发送流量。因此,不会再收到流量并且会出现停机时间。然后创建一个新的Pod(请参阅上面的场景),在此期间,不会收到任何流量。不幸的是,**K8S在杀死一个pod之前不会启动一个新的pod。**在这种情况下,会导致一系列的连锁反应,导致集群挂机。

所以设置两个实例是避免停机的最低要求。

PDB

PodDisruptionBudget (PDB)是一个K8S对象,它指定在部署、维护或任何给定时间不可用的pod数量。这有助于确保应用程序保持可用,即使某些pod被终止或剔除的情况下保持应用程序的正常访问。

举一个例子,一个应用程序有三个pod(实例),要保证始终拥有至少两个Pod在运行,则可以使用一个PDB对象的策略,这将保证始终有至少两个正在运行的pod。

部署策略

K8S部署支持两种策略:

RollingUpdate:默认更新,部署顺利

Recreate:在启动较新版本的应用程序之前强制应用程序完全关闭。

默认情况下,应用RollingUpdate策略。但是可以使用其他选项(例如最大不可用百分比和最大激增)来调整部署的方式。

当的流量负载很大并且想要控制部署速度以最大程度地减少性能影响时,这些选项非常有用。

在这给大家应思考题:上个月导致滴滴服务中断12小时的故障中,采用的是什么部署方式呢?

自动回滚部署

自动回滚对保证业务的高科用非常有用,但是遗憾的是K8S中默认是不支持自动回滚。需要借助并配置第三方的Helm、ArgoCD、Spinnaker等工具才能实现自动回滚。

大多数人想要的很简单:如果应用程序无法正常启动,不要向其发送流量并回滚。

例如,对于Helm,使用Helm实现它的一些选项很有趣:

---等待

---等待工作

---atomic

为了获得运行良好的解决方案,必须设置并正确配置探针。

探针

可能很多人都不会想到这玩意,但是对于集群高科用(零停机)来说,探针是非常非常重要的。

验证应用程序健康状况的两个最重要的探测器是"Liveness"和"Readiness"探测器,一个简单图标解释如下:

Liveness探针可确保应用程序处于活动状态,并判断pod是存活还是死亡。如果活性探测不成功,则

Pod停止接收流量;

Pod重启,尝试恢复健康状态。任何进一步的重新启动都会应用指数退避(指数延迟)

Readiness探针决定是否将流量发送到Pod。

如果流量出现突发(并且活性探针正在响应),但的应用程序开始变慢,则就绪状态可以决定停止向应用程序发送流量,是其恢复到更加健康的状态。

如果就绪探针没有响应,则不会重新启动Pod,仅指示负载均衡器停止向该Pod发送流量。

那么两个探测器该如何选择使用呢?可以取舍,去轻就重呢?

不可以,两个缺一不可,在任何时候就要配好两种探测器,才能保证零宕机。当然,可以使用简单的TCP检查,但它永远不会像自己在应用程序中构建的自定义探针。

初始启动时间延迟

初始启动时间可能需要延迟。它可能发生在不同的情况下:

应用程序使用大量CPU来启动(例如 pringBoot应用程序)。

应用程序需要在启动时执行比以前更多的操作,并且没有分配足够的CPU资源时。

应用程序必须加载数据库中的架构和其中的数据,并且在数据库准备就绪之前它才可用。

还有其他场景......

但这是可能发生片状启动的示例。如果遇到这种情况或想要预见它,应该更新initialDelaySeconds:

优雅的终止期

这个K8S选项并不直接与零停机功能相关,而是更多地涉及忽略应用程序正常关闭的重要性的缺点/板效应。

注意:仅当应用程序能够拦截 SIGTERM时,优雅终止期才能起作用!

如果应用程序未编码为拦截SIGTERM,它只会硬终止应用程序,无论是否存在大于30秒的优雅终止期,这都可能导致数据丢失。

不管理SIGTERM可能会带来几个问题:

糟糕的用户体验:用户遇到错误、空白页面或更糟的情况;

丢失数据:数据尚未提交,用户事务丢失;

不可恢复的数据:刷新磁盘上的数据突然停止,应用程序无法处理它;

还存在其他原因,但会看到让应用程序足够快地关闭是多么重要。

多花一点时间让应用程序正常停止通常是一个好的做法(<5分钟)。K8S默认值为30秒,但可以使用终止GracePeriodSeconds选项进行调整。

Pod Anti-affinity

Pod反关联性可让避免同一节点上存在同一应用程序 (Pod) 的多个实例。当所有实例都位于同一节点上时发生节点崩溃时,可以想象会发生停机。

为了避免这种情况,可以让K8S避免所有pod都位于同一节点上。存在两个版本:

Soft Anti-AffinityPreferredDuringSchedulingIgnoredDuringExecution:它会尽力避免它,但如果它不能(缺乏资源),它将在同一节点上添加两个实例。这个版本具有成本效益,并且在95%的情况下都能发挥作用。

Hard Anti-Affinity requiredDuringSchedulingIgnoredDuringExecution:这是一个硬性要求,不能在同一节点上有两个Pod。但如果要求同一应用程序有50个pod,则需要50个节点。这很快就会变得非常昂贵。

足够的资源

资源是最常见的问题之一。当设置的资源不足时,应用程序可以:

内存不足 (OOM) 并被内核剔除,所以会遇到停机、连接严重关闭等情况......;

未充足的CPU:应用程序可能需要很长时间才能响应,甚至有时在活动检查成功之前无法启动。

运行100%的CPU可能会强制自动缩放程序添加过多的实例。而只需要利用当前的CPU数量即可。除非知道具体原因,否则低于100m通常是有问题了。

自动缩放

自动缩放是避免流量负载下停机的好方法。默认基于CPU(可以使用其他自定义指标)。这是自动部署更多实例(Pod)的简单方法。

**注意自动缩放不是万能药!**必须正确配置应用程序才有效果。

因此,例如,当pod短时间内运行超过60%的CPU时,K8S会触发一个新的pod来处理负载并减少当前正在运行的应用程序的使用率。

以下是K8S上的示例:

结论

K8S确实有神奇的作用,但只有当应用程序尽可能是云原生且配置正确时,它才能发挥神奇作用。

总之,当想将应用程序引入 K8S时,至少应该注意:

最少两个实例

添加健康检查(探针)

应用程序必须处理Sigterm

配置自动缩放器

给予足够的资源

使用pod Anti-affinity

添加PDB

...

如果一切设置正确,K8S保证会比拜佛贴符管用的多,但是零宕机不管需要理论知识更重要是将其结合个性化环境实践。

相关推荐
界面开发小八哥2 分钟前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
草莓base15 分钟前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Allen Bright28 分钟前
maven概述
java·maven
qystca31 分钟前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
编程重生之路31 分钟前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱31 分钟前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea
今天吃饺子36 分钟前
2024年SCI一区最新改进优化算法——四参数自适应生长优化器,MATLAB代码免费获取...
开发语言·算法·matlab
O&REO36 分钟前
单机部署kubernetes环境下Overleaf-基于MicroK8s的Overleaf应用部署指南
云原生·容器·kubernetes
努力进修40 分钟前
“探索Java List的无限可能:从基础到高级应用“
java·开发语言·list
politeboy40 分钟前
k8s启动springboot容器的时候,显示找不到application.yml文件
java·spring boot·kubernetes