3大策略+1款工具,在K8s上搞定应用零宕机

打印 上一主题 下一主题

主题 845|帖子 845|积分 2535

原文链接:
https://jaadds.medium.com/building-resilient-applications-on-kubernetes-9e9e4edb4d33
翻译:cloudpilot.ai
Kubernetes 提供的某些特性可以帮助企业充分利用云原生应用的优势,例如无需关闭整个集群即可更改基础设施、自动修复应用程序以及根据流量举举措态扩展。
然而,正是这种动态特性引发了一个题目:Kubernetes 是否足够稳定,能够运行那些任务关键型、高吞吐量的应用程序?
例如,Pod 随时大概出现故障,这意味着流量和用户体验大概会受到影响。虽然在设置不当的情况下确实会发生这种情况,但通过精确的探针、Pod 分配策略以及扩展选项,任何应用程序都可以在 Kubernetes 上稳健运行。
本文中,我们将讨论如何减少宕机时间并介绍一款开源工具帮助您进步在 Kubernetes 上运行的应用程序的弹性。
设置健康探针(Probe)

健康探针用于告知用户 Kubernetes 控制平面应用程序是否健康、是否准备好接收流量或仍处于启动状态。精确设置的健康探针可以提拔应用程序的响应能力,而设置不当则大概导致不必要的宕机。
健康探针常常是容易设置错误的部门,因此理解差别类型健康探针的作用非常紧张。
差别探针的作用


  • 停当探针(Readiness Probe): 在将流量路由到 Pod 之前举行检查。假如停当探针检测失败,Pod 将不再接收流量。
  • 存活探针(Liveness Probe): 用于判断 Pod 内的容器是否正常运行。假如存活探针检测失败,容器将被重新启动。
停当探针

当 Pod 完全启动并准备好接收流量时,停当探针才会通过检测。假如需要通过启动缓存或初始化数据库连接池来预热应用程序,请在停当探针检测之前完成这些操作。
只有当您的 Pod 通过 Kubernetes 服务对外提供流量时,才需要设置停当探针。假如您的 Pod 只是执行任务,例如运行批处置惩罚作业或从队列中消耗数据,则不需要设置停当探针。

停当探针检测失败的情况


  • 容器被过多的流量压垮,需要先完成当前请求才气继承新的流量。
  • 应用程序接收到 SIGTERM 信号(我们将在下一节中进一步讨论这一点)。
选择探针时的注意事项

检查 endpoint 和服务的 endpoint 保持一致是一个不错的选择;当请求处置惩罚的 endpoint 被完全占用时,发送给停当探针的请求也会失败,从而减少流量进入 Pod。
为了更好地评估应用程序的停当状态,可以使用多种指标,例如数据库连接池的利用率、正在使用的线程数以及并发请求数。当这些指标显示应用程序已经达到负载极限时,您可以使停当探针检测失败,以防止性能的进一步恶化。
根据服务的类型,您可以选择使用命令、HTTP 探针、TCP 探针或 gRPC 探针。
需要注意的是,只管避免使用 TCP 探针,由于它只检查服务是否可以通过端口访问,不会深入到应用程序层面,因此不能精确反映应用程序是否能够继承新请求。
一般情况下,Web 应用程序和暴露 REST 或 GraphQL 接口的微服务/应用程序应使用 HTTP 探针。而对于使用 gRPC 接口的服务,则应使用 gRPC 探针。
几点建议


  • 在应用程序代码中记录停当探针的执行时间。假如探针检测失败过于频仍,有助于调整阈值。
  • 避免在停当探针中调用后端服务,由于这大概导致探针超时并检测失败。探针的目标是判断应用程序是否准备好接收流量。假如后端服务出现故障,应用程序应该通过适当的错误处置惩罚机制来应对。
  • 假如应用程序完全依赖后端服务,且需要在通过停当探针前获取后端服务状态,请异步获取这些状态信息。
存活探针(Liveness Probe)

存活探针用于判断应用程序是否正在运行或存活。应用程序未准备好并不意味着它没有存活(它大概正在处置惩罚举行中的请求,但暂时不继承新的请求)。然而,假如应用程序不存活,那么它永远无法准备好接收流量。

存活探针检测失败的情况


  • 容器遇到了无法通过非重启方式办理的题目。
  • 应用程序进入了冻结状态,无法通过等候或冷却恢复。例如,由于数据库服务器响应缓慢,数据库连接池被停滞。
  • 与关键后端服务(如数据库或队列)的连接失败,且无法在不重启的情况下重新建立连接。
选择探针时的注意事项

在我看来,只有当应用程序可以通过重启恢复时,才应该使用存活探针。一个理想的存活探针应返回非常简单(或硬编码)的响应,除非应用程序出现严重题目,否则不会导致探针失败。
假如您的应用程序需要通过重启来重新建立与后端服务(如数据库或队列)的连接,那么在通过存活探针前检查这些连接大概是个好做法。
不过需要注意的是,使用当代框架(如 Express、Spring Boot)开发的云原生应用通常不需要重启来重新建立连接——处置惩罚后端连接池的库通常能够自动完成此过程。假如您发现不需要存活探针,最好避免使用它。
注意事项


  • 存活探针和停当探针不应该使用雷同的设置。这两个探针的要求差别,您在停当探针中采取的操作大概会导致存活探针过早报错。
  • 假如必须为两个探针使用雷同的端点,请确保它们有差别的设置(如 initialDelay、timeout、period 等)。
  • Kubernetes 在执行探针时不包管顺序,因此确保停当探针先于存活探针失败。假如停当探针答应三次失败,存活探针应容忍五次失败。
优雅处置惩罚 Pod 终止

Pod 终止时是请求失败的另一个常见缘故原由。除非能优雅地处置惩罚 Pod 的终止,否则发送到终止 Pod 的请求大概会失败。
当 Pod 内的容器没有优雅地关闭(即突然制止或瓦解)时,正在处置惩罚中的请求会受到影响。别的,与 pod 关联的 Kubernetes 服务对象会一直发送流量,直到应用程序退出,从而导致某些请求收到“连接拒绝”的报错。
防止出现此类突发故障的方式是精确处置惩罚 SIGTERM 信号。为了理解其工作原理,让我们详细看看 Pod 的终止生命周期。


  • 当 Pod 被标记为删除时,kubelet 会调用 Pod 中界说的全部容器的 preStop 处置惩罚程序。同时,优雅终止周期(由 terminationGracePeriodSeconds 界说)的倒计时也开始了。
  • 在 prestop 钩子执行完毕后,kubelet 会向 Pod 内全部容器发出 SIGTERM 信号。SIGTERM 是请求终止容器内运行的应用程序的信号。然而,应用程序在收到 SIGTERM 后仍然可以继续运行。
  • 在优雅终止周期结束后,kubelet 会向全部容器发出 SIGKILL 信号。假如已经有正在运行的应用程序,该命令将使其制止运行。
  • Kubernetes 控制平面随后会移除 Pod,此后,任何客户端都无法看到 Pod。
通常情况下,当 Pod 开始终止时,SIGTERM 是容器收到的第一个信号。假如指定了 prestop 处置惩罚器,它将优先被调用。在某些情况下,指定 prestop 处置惩罚器有助于实现 Pod 的优雅终止。
接下来,我们将起首探讨如那里置惩罚 SIGTERM 信号,然后再了解 preStop 处置惩罚器如何实现雷同的效果。
处置惩罚 SIGTERM 信号

制止接收流量的最可靠方法是在 pod 开始终止时,通过捕捉 SIGTERM 信号使停当探测器失败。
PS:妥善处置惩罚 SIGTERM 信号被认为是进步应用程序弹性的最紧张环节。这是由于应用程序通过此信号得知本身即将被终止。Kubernetes 的动态特性要求它出于各种缘故原由(如推出新应用版本、缩减副本、集群维护任务等)频仍终止 pod。假如不处置惩罚 SIGTERM,您将为 Kubernetes 的动态特性破坏应用程序的稳定性埋下隐患。
在收到 SIGTERM 后,应用程序需要捕获该信号并举行优雅关闭,详细步调如下:

  • 通过明确标记条件或关闭继承请求的线程池,使准备停当探针失败。
  • 让正在举行的请求完成。
  • 关闭任何双向连接,如 WebSocket 或 gRPC。
  • 关闭数据库连接和队列连接。
当停当探针失败时,连接到 Pod 的服务对象将不再将请求发送到该 Pod 的 IP,如许可以立即减少流量。
要接收 SIGTERM 信号,容器内的应用程序应该以进程 ID 1 运行。假如您使用脚原来启动应用程序,那么该脚本将是 PID 为 1 的进程。在这种情况下,您需要保存应用程序的 PID,并使用该 PID 将 SIGTERM 信号转发回应用程序。
调整滚动更新

在推出新版本应用程序时,也大概会出现宕机。当摆设被扩展到最大副本数并举行滚动更新时,有资格处置惩罚流量的 Pod 大概会出现故障,导致某些请求失败。
通过将 maxUnavailable 设置为 0,您可以确保在关闭任何现有 Pod 之前,先调治新 Pod。
  1. strategy:
  2.     rollingUpdate:
  3.       maxSurge: 1
  4.       maxUnavailable: 0
  5.     type: RollingUpdate
复制代码
请注意,一旦新 Pod 开始运行,旧 Pod 就会被关闭。控制平面并不会检查新 Pod 是否已准备好接收流量。假如您需要在新 Pod 准备好接收流量之前延迟终止旧 Pod,可以使用停当门(readiness gates)。
现在,这一功能仅由 AWS 负载平衡器原生支持。有关如何在 AWS 负载平衡器中使用停当门的更多信息,请查看AWS官方文档[1]
界说 Pod 停止预算

在举行集群维护任务时,例如更新 Kubernetes 版本或升级集群节点,Pod 大概会被终止或重新调治到差别的节点。Pod 停止预算(Pod Disruption Budget, PDB)通过答应保留最小数量的 Pod,来保护摆设不受这些停止的影响。以下设置表示在发生停止时,至少有 50% 的 Pod 处于可用状态:
  1. apiVersion: policy/v1
  2. kind: PodDisruptionBudget
  3. metadata:
  4.   name: pdb-front–end
  5. spec:
  6.   minAvailable: 50%
  7.   selector:
  8.         matchLabels:
  9.           app: front–end
复制代码
假如您要保护的是无状态应用程序,最好使用百分比来表示 minAvailable 和 maxUnavailable 属性。由于随着时间的推移,运行应用程序所需的副本数量大概会增加,从而使 PDB 界说很快过时。
假如应用程序是有状态的,并且需要维护最低数量的 Pod 以达到基本要求,那么最好将属性指定为详细数字。
需要注意的是,并非全部停止都思量 PDB。例如,使用 kubectl 删除摆设并不会触发 PDB。通常,与节点相关的更改,如 kubectl delete 、kubectl drain 和 kubectl cordon 才会评估 PDB。这篇博客[2]详细讨论了经过 PDB 的自愿停止。您还可以参考 K8s 官方文档[3],以获取有关界说 PDB 的更多建议。
别的,开源K8s自动扩缩容工具 Karpenter 可以进一步赋能 PDB,它能够设置停止预算并且精确管理节点更新的速度。
例如,Karpenter 可以设置单个节点每15分钟更新一次,从而大大降低对运行服务的潜在影响。这种循序渐进、可控的方法可确保服务保持稳定,并最大限度地减少升级期间的宕机时间。
在节点之间分配 Pod

假如发生节点级故障,将 Pod 分布在差别节点大将有助于减少故障的影响。您可以通过使用 Pod 亲和性(Pod Affinity)或拓扑分布约束(Topology Spread Constraints)来实现这一点。
Pod 反亲和性(Pod Anti-affinity)

通过使用反亲和性规则,您可以指示 Kubernetes 调治器不要将两个 Pod 调治到同一个节点上。
  1. affinity:
  2.   podAntiAffinity:
  3.      requiredDuringSchedulingIgnoredDuringExecution:
  4.        - labelSelector:
  5.            matchExpressions:
  6.              - key: app
  7.                operator: In
  8.                values:
  9.                - transaction-service
  10.          topologyKey: kubernetes.io/hostname  
复制代码
根据这一规则,标签为 app 且值为 transaction-service 的两个 Pod 将不会运行在具有雷同 kubernetes.io/hostname 值的节点上。
根据您对这一行为的严格程度,可以选择 requiredDuringSchedulingIgnoredDuringExecution 或 preferredDuringSchedulingIgnoredDuringExecution 策略。当使用前者时,kube-scheduler 只有在满足条件的情况下才气调治 Pod。
假如找不到没有 transaction-service 应用的节点,它将等候新的节点可用。使用后者时,调治器会只管避免将 transaction-service Pod 放置在同一节点上,但假如没有合适的节点可用,它将违背条件举行调治。
拓扑分布约束(Topology Spread Constraints)

假设您在一个托管的 Kubernetes 集群上运行,例如 EKS 或 AKS,并且需要将 Pod 均匀分布在差别的可用区(Availability Zones)中。您可以使用 topologySpreadConstraints 来实现这一点。
可用区是云基础设施提供商用来隔离故障的一种方法。通过在可用区之间分散 Pod,可以使应用程序具有更高的可用性。
即使有适当的反亲和性规则将 Pod 放置在差别的节点上,您的应用程序也大概会被调治成如下情况:

通过界说如下的 topologySpreadConstraint,您可以实现均匀分布:
  1. topologySpreadConstraints:
  2.  - labelSelector:
  3.      matchLabels:
  4.        app: transaction-service
  5.    maxSkew: 1
  6.    topologyKey: topology.kubernetes.io/zone
  7.    matchLabelKeys:
  8.      - pod-template-hash
  9.    whenUnsatisfiable: DoNotSchedule
复制代码

您大概会想知道,为什么在这种情况下不能使用反亲和性,只需将 Topology key 更改为 topology.kubernetes.io/zone。假如不仔细设置策略和参数,则会限定应用程序的扩展性。例如,假如您只是简单地通过更改 Topology key 来更改我们看到的反亲和性策略,那么您的应用程序将无法扩展到凌驾三个副本。
因此,当您的目标是将一个 Pod 与另一个 Pod 放在一起,或避免将 Pod 放在一起时,最好使用亲和;而使用拓扑分布约束则可以在节点、可用区和地区之间分配 Pod。
借助 Karpenter 提拔应用弹性

Karpenter是一款开源的 Kubernetes 集群自动扩缩容工具。它可以通过观察不可调治的 Pod 的聚合资源请求并做出启动和终止节点的决策,以最大限度地减少调治延迟,从而在几秒钟内(而不是几分钟)提供合适的盘算资源来满足您的应用程序的需求。
它根据 pod 的调治需求自动设置和取消设置节点,从而实现高效扩展和成本优化。它的主要功能包括:

  • 监控 Kubernetes 调治器因资源限定而无法调治的 pod。
  • 评估无法调治 pod 的调治要求(资源请求、节点选择器、亲和力、容忍度等)。
  • 提供满足这些 pod 要求的新节点。
  • 在不再需要节点时将其移除。
2个月前,Karpenter 发布了 1.0(GA) 版本,在这一版本中 Karpenter 包含以下2个重大更新可以帮助您提拔应用弹性和稳定性:
1、按缘故原由设置停止预算(Disruption Budget)

Karpenter 的停止控制在节省成本和可用性方面已经是一项了不起的功能。Karpenter 会自动发现可停止的节点,并在需要时启动替代节点。
1.0 版引入了停止预算,可按缘故原由(如未充分利用、空闲或漂移)设置停止预算。该功能在对服务可用性要求极高的生产情况中非常关键。
假如您在“双十一”期间运行一个线上购物平台。在流量高峰期,大概会合并利用率低的节点以节省成本,但这大概会无意间停止正在举行的交易,从而导致糟糕的客户体验。
假如没有 Karpenter 合理的停止预算,则无法轻松避免此类情况。现在,您可以界说策略,在关键时期限定节点合并,而在非高峰期(如闪购或节假日促销)答应节点合并,确保您的服务不停止并降低成本。
2、新的优雅终止期限(Termination Grace Period)

安全性和合规性对于维护稳定、安全的 Kubernetes 情况至关紧张。但是管理节点的生命周期以确保它们保持合规性具有挑战。
例如,假如您需要遵守严格的安全和合规规定,您大概希望确保节点的运行时间不凌驾预定的期限,以避免出现潜在的漏洞。
在 Karpenter 1.0 之前,管理节点生命周期以满足这些规定需要手动操作或自界说自动化。terminationGracePeriod 通过强制执行节点的最长生命周期,自动终止和替换凌驾预定寿命的节点,从而实现了这一过程的自动化。
这可以防止使用过时或大概不合规的节点,确保基础设施在无需人工监督的情况下保持安全和合规,并降低软件或设置过时的风险。
Karpenter 的这些重大更新意味着 K8s 集群的自动扩展正在向智能化迈进。
随着越来越多的知名企业(如Slack、阿迪达斯奥迪等)采用 Karpenter 来帮助他们优化成本,我们可以预见:在未来,集群管理的复杂性将越来越自动化,从而使 DevOps 团队能够专注于更高层次的战略举措。
假如您正在寻求以更智能、自动化的方式优化 Kubernetes 基础设施,Karpenter + 云妙算将您的绝佳选择。
云妙算通过智能节点选择、Spot自动化及智能 AI 预测功能,让用户可以采用最多样化的实例类型,最大化利用 Spot 实例并且提前预测 Spot 实例停止时刻,降低应用50%以上的云成本,同时包管应用稳定性。
结  论

只管实现 Kubernetes 的弹性和零宕机摆设好像是不大概的,但通过经心规划和适当设置,您可以利用该平台的动态特性来运行关键任务应用程序。
通过采用本文介绍的策略——例如设置适当的健康探针、优雅地处置惩罚 Pod 终止和界说 Pod 停止预算——您可以显著减少宕机时间并进步应用程序的整体稳定性。别的,设置集群自动扩展工具 Karpenter 和 Pod 自动扩展可以更有效地应对流量激增。
随着 Kubernetes 的不断发展,了解最新的功能和最佳实践将进一步加强您构建和维护弹性应用程序的能力。

<strong>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

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