10.6 Pod Disruption Budget(主动驱逐保护)

在Kubernetes集群运行过程中,许多管理操作都可能对Pod进行主动驱逐,“主动”一词意味着这一操作可以安全地延迟一段时间,目前主要针对以下两种场景。

◎ 节点维护或升级时(kubectl drain)。

◎ 对应用的自动缩容操作(autoscaling down)。

作为对比,由于节点不可用(Not Ready)导致的Pod驱逐就不能被称为主动了,但是Pod的主动驱逐行为可能导致某个服务对应的Pod实例全部或大部分被“消灭”,从而引发业务中断或业务SLA降级,而这是违背Kubernetes的设计初衷的。因此需要一种机制来避免我们希望保护的Pod被主动驱逐,这种机制的核心就是PodDisruptionBudget。通过使用PodDisruptionBudget,应用可以保证那些会主动移除Pod的集群操作永远不会在同一时间停掉太多Pod(从而导致服务中断或者服务降级等)。

PodDisruptionBudget资源对象用于指定一个Pod集合在一段时间内存活的最小实例数量或者百分比。一个PodDisruptionBudget作用于一组被同一个控制器管理的Pod,例如DeploymentReplicaSet或RC。与通常的Pod删除不同,驱逐Pod的控制器将使用/eviction接口对Pod进行驱逐,如果这一主动驱逐行为违反了PodDisruptionBudget的约定,就会被API Server拒绝。kubectl drain操作将遵循PodDisruptionBudget的设定,如果在该节点上运行了属于同一服务的多个Pod,则为了保证最少存活数量,系统将确保每终止一个Pod,就一定会在另一台健康的Node上启动新的Pod,再继续终止下一个Pod。需要注意的是,Disruption Controller不能取代Deployment、Statefulset等具备副本控制能力的Controller。PodDisruptionBudget对象的保护作用仅仅针对主动驱逐场景,而非所有场景,比如针对下面这些场景,PodDisruptionBudget机制完全无效。

◎ 后端节点物理机的硬件发生故障。

◎ 集群管理员错误地删除虚拟机(实例)。

◎ 云提供商或管理程序发生故障,使虚拟机消失。

◎ 内核恐慌(kernel panic)。

◎ 节点由于集群网络分区而从集群中消失。

◎ 由于节点资源不足而将容器逐出。

对PodDisruptionBudget的定义包括如下几个关键参数。

◎ Label Selector:用于筛选被管理的Pod。

◎ minAvailable:指定驱逐过程中需要保障的最少Pod数量。minAvailable可以是一个数字,也可以是一个百分比,例如100%就表示不允许进行主动驱逐。

◎ maxUnavailable:要保证最大不可用的Pod数量或者比例。

◎ minAvailable和maxUnavailable不能被同时定义。

除了Pod对象,PodDisruptionBudget目前也支持了具备扩容能力的CRD对象,即这些CRD拥有Scale子对象资源并支持扩容功能。

PodDisruptionBudget应用示例如下。

(1)创建一个Deployment,设置Pod副本数量为3:

创建后通过kubectl get pods命令查看Pod的创建情况:

(2)接下来创建一个PodDisruptionBudget资源对象:

PodDisruptionBudget使用的是和Deployment一样的Label Selector,并且设置存活Pod的数量不得少于3个。

(3)主动驱逐验证。对Pod的主动驱逐操作将通过驱逐API(/eviction)来完成。可以将这个API看作受策略控制的对Pod的DELETE操作。要实现一次主动驱逐(更准确的说法是创建一个Eviction资源),则需要POST一个JSON请求,以eviction.json文件格式表示,例如希望驱逐名为“nginx-1968750913-0k01k”的Pod,内容如下:

用curl命令执行驱逐操作:

由于PodDisruptionBudget设置存活的Pod数量不能少于3个,因此驱逐操作会失败,在返回的错误信息中会包含如下内容:

使用kubectl get pods命令查看Pod列表,会看到Pod的数量和名称都没有发生变化。

(4)删除PodDisruptionBudget资源对象,再次验证驱逐Pod。用kubectl delete pdb nginx命令删除PodDisruptionBudget资源对象:

再次执行上文中的curl指令,会执行成功。

通过kubectl get pods命令查看Pod列表,会发现Pod的数量虽然没有发生变化,但是指定的Pod已被删除,取而代之的是一个新的Pod。