干扰(Disruptions)
本指南针对的是希望构建高可用性应用程序的应用所有者,他们有必要了解可能发生在Pod上的干扰类型。
文档同样适用于想要执行自动化集群操作(例如升级和自动扩展集群)的集群管理员。
自愿干扰和非自愿干扰
Pod不会消失,除非有人(用户或控制器)将其销毁,或者出现了不可避免的硬件或软件系统错误。
我们把这些不可避免的情况称为应用的非自愿干扰(InvoluntaryDisruptions)。例如:
除了资源不足的情况,大多数用户应该都熟悉这些情况;它们不是特定于Kubernetes的。
我们称其他情况为自愿干扰(VoluntaryDisruptions)。包括由应用程序所有者发起的操作和由集群管理员发起的操作。典型的应用程序所有者的操作包括:
集群管理员操作包括:
这些操作可能由集群管理员直接执行,也可能由集群管理员所使用的自动化工具执行,或者由集群托管提供商自动执行。
咨询集群管理员或联系云提供商,或者查询发布文档,以确定是否为集群启用了任何资源干扰源。如果没有启用,可以不用创建PodDisruptionBudgets(Pod干扰预算)
并非所有的自愿干扰都会受到Pod干扰预算的限制。例如,删除Deployment或Pod的删除操作就会跳过Pod干扰预算检查。
处理干扰
以下是减轻非自愿干扰的一些方法:
自愿干扰的频率各不相同。在一个基本的Kubernetes集群中,没有自愿干扰(只有用户触发的干扰)。然而,集群管理员或托管提供商可能运行一些可能导致自愿干扰的额外服务。例如,节点软更新可能导致自愿干扰。另外,集群(节点)自动缩放的某些实现可能导致碎片整理和紧缩节点的自愿干扰。集群管理员或托管提供商应该已经记录了各级别的自愿干扰(如果有的话)。有些配置选项,例如在podspec中使用PriorityClasses也会产生自愿(和非自愿)的干扰。
Kubernetes提供特性来满足在出现频繁自愿干扰的同时运行高可用的应用程序。我们称这些特性为干扰预算(DisruptionBudget)。
干扰预算
FEATURESTATE:Kubernetesv1.21[stable]
即使你会经常引入自愿性干扰,Kubernetes也能够支持你运行高度可用的应用。
应用程序所有者可以为每个应用程序创建PodDisruptionBudget对象(PDB)。PDB将限制在同一时间因自愿干扰导致的复制应用程序中宕机的pod数量。例如,基于票选机制的应用程序希望确保运行的副本数永远不会低于仲裁所需的数量。Web前端可能希望确保提供负载的副本数量永远不会低于总数的某个百分比。
集群管理员和托管提供商应该使用遵循PodDisruptionBudgets的接口(通过调用EvictionAPI),而不是直接删除Pod或Deployment。
例如,kubectldrain命令可以用来标记某个节点即将停止服务。运行kubectldrain命令时,工具会尝试驱逐机器上的所有Pod。kubectl所提交的驱逐请求可能会暂时被拒绝,所以该工具会定时重试失败的请求,直到所有的Pod都被终止,或者达到配置的超时时间。
PDB指定应用程序可以容忍的副本数量(相当于应该有多少副本)。例如,具有.spec.replicas:5的Deployment在任何时间都应该有5个Pod。如果PDB允许其在某一时刻有4个副本,那么驱逐API将允许同一时刻仅有一个而不是两个Pod自愿干扰。
使用标签选择器来指定构成应用程序的一组Pod,这与应用程序的控制器(Deployment,StatefulSet等)选择Pod的逻辑一样。
Pod控制器的.spec.replicas计算“预期的”Pod数量。根据Pod对象的.metadata.ownerReferences字段来发现控制器。
PDB无法防止非自愿干扰;但它们确实计入预算。
由于应用程序的滚动升级而被删除或不可用的Pod确实会计入干扰预算,但是控制器(如Deployment和StatefulSet)在进行滚动升级时不受PDB的限制。应用程序更新期间的故障处理方式是在对应的工作负载资源的spec中配置的。
当使用驱逐API驱逐Pod时,Pod会被体面地终止,期间会参考PodSpec中的terminationGracePeriodSeconds配置值。
PDB例子
假设集群有3个节点,node-1到node-3。集群上运行了一些应用。其中一个应用有3个副本,分别是pod-a,pod-b和pod-c。另外,还有一个不带PDB的无关podpod-x也同样显示出来。最初,所有的Pod分布如下:
node-1node-2node-3
pod-aavailable
pod-bavailable
pod-cavailable
pod-xavailable
3个Pod都是deployment的一部分,并且共同拥有同一个PDB,要求3个Pod中至少有2个Pod始终处于可用状态。
例如,假设集群管理员想要重启系统,升级内核版本来修复内核中的权限。集群管理员首先使用kubectldrain命令尝试排空node-1节点。命令尝试驱逐pod-a和pod-x。操作立即就成功了。两个Pod同时进入terminating状态。这时的集群处于下面的状态:
node-1drainingnode-2node-3
pod-aterminating
pod-bavailable
pod-cavailable
pod-xterminating
Deployment控制器观察到其中一个Pod正在终止,因此它创建了一个替代Podpod-d。由于node-1被封锁(cordon),pod-d落在另一个节点上。同样其他控制器也创建了pod-y作为pod-x的替代品。
(注意:对于StatefulSet来说,pod-a(也称为pod-0)需要在替换Pod创建之前完全终止,替代它的也称为pod-0,但是具有不同的UID。除此之外,此示例也适用于StatefulSet。)
当前集群的状态如下:
node-1drainingnode-2node-3
pod-aterminating
pod-bavailable
pod-cavailable
pod-xterminating
pod-dstarting
pod-y
在某一时刻,Pod被终止,集群如下所示:
node-1drainednode-2node-3
pod-bavailable
pod-cavailable
pod-dstarting
pod-y
此时,如果一个急躁的集群管理员试图排空(drain)node-2或node-3,drain命令将被阻塞,因为对于Deployment来说只有2个可用的Pod,并且它的PDB至少需要2个。经过一段时间,pod-d变得可用。
集群状态如下所示:
node-1drainednode-2node-3
pod-bavailable
pod-cavailable
pod-davailable
pod-y
现在,集群管理员试图排空(drain)node-2。drain命令将尝试按照某种顺序驱逐两个Pod,假设先是pod-b,然后是pod-d。命令成功驱逐pod-b,但是当它尝试驱逐pod-d时将被拒绝,因为对于Deployment来说只剩一个可用的Pod了。
Deployment创建pod-b的替代Podpod-e。因为集群中没有足够的资源来调度pod-e,drain命令再次阻塞。集群最终将是下面这种状态:
node-1drainednode-2node-3no node
pod-bterminating
pod-cavailable
pod-epending
pod-davailable
pod-y
此时,集群管理员需要增加一个节点到集群中以继续升级操作。
可以看到Kubernetes如何改变干扰发生的速率,根据:
分离集群所有者和应用所有者角色
通常,将集群管理者和应用所有者视为彼此了解有限的独立角色是很有用的。这种责任分离在下面这些场景下是有意义的:
Pod干扰预算通过在角色之间提供接口来支持这种分离。
如果你的组织中没有这样的责任分离,则可能不需要使用Pod干扰预算。
如何在集群上执行干扰性操作
如果你是集群管理员,并且需要对集群中的所有节点执行干扰操作,例如节点或系统软件升级,则可以使用以下选项
编写可容忍干扰的应用程序和使用PDB。