今天是「DevOps云学堂」与你共同进步的第 52天
我在本地和托管 Kubernetes 集群方面工作了七年多。我能说的是,容器已经彻底改变了托管格局!它带来了许多需要复杂设置的设施。拥有多个实例,具有滚动重启、零停机、健康检查等功能。以前真是费时费力(实现 VRRP 解决方案、使用 monit 之类的应用程序监控重启、负载均衡 haproxy 之类的)!
因此,现在使用 Kubernetes 可以更轻松地访问一切,但如果您想为应用程序的生命周期构建完美的设置,您仍然必须了解它的工作原理以及根据您的情况应遵循哪种策略。
在本文中,我将解释为什么以及如何使用 Kubernetes 实现零停机应用程序。
如果您已经使用Docker一段时间,那么这看起来很简单。拉取和使用容器镜像非常简单。但是,在生产环境中,如果您不是映像所有者,您通常不想依赖远程且不受控制的映像注册表。为什么?
存在多种解决方案。一种是将容器映像从源注册表同步到您自己的注册表。
这听起来很明显,但如果您正在寻求高可用性,则您的应用程序至少需要2 个 Kubernetes 副本(2 个 Pod)。例子:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2 # tells deployment to run 2 pods matching the template
template:
..
我多次听到的关于 Kubernetes 的一个常见错误是:“我不需要两个实例,因为 Kubernetes 执行滚动更新,因此它将在关闭当前实例之前启动一个新实例”。确实如此,但它仅适用于部署更新!
以下是不适用此规则的其他场景:
这就是为什么设置两个实例是避免停机的最低要求(另请参阅 Pod 反关联性部分)。
PodDisruptionBudget (PDB) 是一个 Kubernetes 对象,它指定在部署、维护或任何给定时间不可用的 Pod 数量。这有助于确保您的应用程序保持可用,即使某些 pod 被终止或驱逐也是如此。
让我们举一个例子,我的应用程序有三个 pod(实例);我总是希望始终拥有至少两个running;我可以应用一个 PDB 对象,这将保证我始终有至少两个正在运行的 pod!
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: my-app
Kubernetes 部署有两种策略:
RollingUpdate:默认更新,部署顺利。重新创建:在启动较新版本的应用程序之前强制应用程序完全关闭。
默认情况下,应用 RollingUpdate 策略。但是您可以使用其他选项(例如最大不可用百分比和最大激增)来调整部署的方式。当您面临繁重的流量负载并且想要控制部署速度以最大程度地减少性能影响时,这些选项非常有用。
不幸的是,自动回滚并不是 Kubernetes 默认提供的功能。一般来说,您必须使用 Helm、ArgoCD、Spinnaker 等第三方工具才能实现自动回滚。
大多数人想要的很简单:如果我的应用程序无法正常启动,不要向其发送流量并回滚。
例如,对于 Helm,使用 Helm 实现它的一些选项很有趣:
为了获得运行良好的解决方案,必须设置并正确配置探针(请参阅下一节)。如果 pod 没有通过其活性探针正常启动,则会自动回滚。
不幸的是,探针经常被低估,但它们对于实现零停机非常重要!
验证应用程序健康状况的两个最重要的探测器是“Liveness”和“Readiness”探测器。比长篇大论更好的是,这里有一个解释目标的模式:
Kubernetes 探针工作流程
Liveness探针可确保您的应用程序处于活动状态,并将决定 pod 是存活还是死亡!
如果活性探测未成功:
Readiness 探针决定是否将流量发送到您的 Pod。
在什么情况下配置自定义活跃度和就绪度探测器有用?
答案是“永远”!当然,您可以使用简单的 TCP 检查,但它永远不会像您自己在应用程序中构建的自定义探针(例如,REST API 上的专用端点)那样可靠。
初始启动时间可能需要延迟。它可能发生在不同的情况下:
但这是可能发生片状启动的示例。如果您遇到这种情况或想要预见它,您应该像这样更新initialDelaySeconds :
livenessProbe:
initialDelaySeconds: 60
httpGet:
...
注意:存在专用启动探测器,但在大多数情况下可能没有用。一般来说,initialDelaySeconds选项就足够了。
这个 Kubernetes 选项并不直接与零停机功能相关,而是更多地涉及忽略应用程序正常关闭的重要性的缺点效应。
仅当应用程序能够拦截 SIGTERM时,优雅终止期才能起作用!如果应用程序未编码为拦截 SIGTERM,则它只会硬终止应用程序,无论是否存在大于 30 秒的优雅终止期,这都可能导致数据丢失。
不管理 SIGTERM 可能会带来几个问题:
还存在其他原因,但您会看到让应用程序足够快地关闭是多么重要。
ℹ️ 硬件故障总是有可能的,因此您的应用程序应该始终能够在此类故障后恢复。然而,类似的常规故障不应频繁发生。
多给你一点时间让你的应用程序正常停止通常是很好的做法(<5 分钟)。Kubernetes 默认值为 30 秒,但您可以使用终止GracePeriodSeconds 选项进行调整。
Pod 反关联性可让您避免同一节点上存在同一应用程序 (Pod) 的多个实例。当所有实例都位于同一节点上时发生节点崩溃时,您可以想象会发生停机。
为了避免这种情况,您可以要求 Kubernetes 避免所有 pod 都位于同一节点上。存在两个版本:
这是它在 Kubernetes 上的样子:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
资源是最常见的问题之一。当设置的资源不足时,您的应用程序可以:
自动缩放是避免流量负载下停机的好方法。默认基于CPU(可以使用其他自定义指标)。这是自动部署更多实例(Pod)的简单方法。
⚠️ 自动缩放并不是魔法!您必须在 Kubernetes 上正确配置您的应用程序。
因此,例如,当您的 pod 短时间内运行超过 60% 的 CPU 时,Kubernetes 会触发一个新的 pod 来处理负载并减少当前正在运行的应用程序的使用率。
以下是 Kubernetes 上的示例:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
...
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
Kubernetes 确实有神奇的作用,但只有当应用程序尽可能是云原生且配置正确时,它才能发挥神奇作用。
总之,当您想将应用程序引入 Kubernetes 时,您至少应该注意:
如果一切设置正确,Kubernetes 体验将令人难以置信,您将不会再遭受任何停机。
原文链接
https://blog.devops.dev/how-to-achieve-zero-downtime-application-with-kubernetes-ba52fdea9a9b