深入分析Kubernetes Critical Pod(一)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation **scheduler.alpha.kubernetes.io/critical-pod"=""**,以表示这是一个关键服务,那你知道这些Critical Pod跟Regular Pod到底存在哪些不同么?本文就带你一探究竟,看看Critical Pod在哪些时候会有哪些特殊行为(Predicate in Schedule、Kubelet Eviction、DaemonSet、Kubelet Preemption),便于你更优雅的在阿里云上玩转Kubernetes。

大家在Kubernetes集群中部署核心组件时,经常会用到Critical Pod,那么你知道Critical Pod到底有何特别吗?要完整的了解这一点,其实并不是那么简单,它关系到调度、Kubelet Eviction Manager、DaemonSet Controller、Kubelet Preemption等,我将分4个系列为大家剖析。这一篇先介绍Critical Pod在Predicate in Schedule阶段的行为,以及用户期望的行为等。

官方宣布Rescheduler is deprecated as of Kubernetes 1.10 and will be removed in version 1.12,所以本文将不讨论Rescheduler对Critical Pod的处理逻辑。

有什么方法标识一个Pod为Critical Pod

规则1:

  • Enable Feature Gate ExperimentalCriticaPodAnnotation
  • 必须隶属于kube-system namespace;
  • 必须加上Annotation scheduler.alpha.kubernetes.io/critical-pod=""

规则2:

  • Enable Feature Gate ExperimentalCriticaPodAnnotation, PodPriority
  • Pod的Priority不为空,且不小于2 * 10^9;

    > system-node-critical priority = 10^9 + 1000;  
    > system-cluster-critical priority = 10^9;
    

满足规则1或规则2之一,就认为该Pod为Critical Pod;

Schedule Critical Pod

在default scheduler进行pod调度的predicate阶段,会注册GeneralPredicates为default predicates之一,并没有判断critical Pod使用EssentialPredicates来对critical Pod进行predicate process。这意味着什么呢?

我们看看GeneralPredicates和EssentialPredicates的关系就知道了。GeneralPredicates中,先调用noncriticalPredicates,再调用EssentialPredicates。因此如果你给Deployment/StatefulSet等(DeamonSet除外)标识为Critical,那么在scheduler调度时,仍然走GeneralPredicates的流程,会调用noncriticalPredicates,而你却希望它直接走EssentialPredicates。

// GeneralPredicates checks whether noncriticalPredicates and EssentialPredicates pass. noncriticalPredicates are the predicates
// that only non-critical pods need and EssentialPredicates are the predicates that all pods, including critical pods, need
func GeneralPredicates(pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
    var predicateFails []algorithm.PredicateFailureReason
    fit, reasons, err := noncriticalPredicates(pod, meta, nodeInfo)
    if err != nil {
        return false, predicateFails, err
    }
    if !fit {
        predicateFails = append(predicateFails, reasons...)
    }

    fit, reasons, err = EssentialPredicates(pod, meta, nodeInfo)
    if err != nil {
        return false, predicateFails, err
    }
    if !fit {
        predicateFails = append(predicateFails, reasons...)
    }

    return len(predicateFails) == 0, predicateFails, nil
}

noncriticalPredicates原意是想对non-critical pod做的额外predicate逻辑,这个逻辑就是PodFitsResources检查。

pkg/scheduler/algorithm/predicates/predicates.go:1076

// noncriticalPredicates are the predicates that only non-critical pods need
func noncriticalPredicates(pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
    var predicateFails []algorithm.PredicateFailureReason
    fit, reasons, err := PodFitsResources(pod, meta, nodeInfo)
    if err != nil {
        return false, predicateFails, err
    }
    if !fit {
        predicateFails = append(predicateFails, reasons...)
    }

    return len(predicateFails) == 0, predicateFails, nil
}

PodFitsResources就做以下检查资源是否满足要求:

  • Allowed Pod Number;
  • CPU;
  • Memory;
  • EphemeralStorage;
  • Extended Resources;

也就是说,如果你给Deployment/StatefulSet等(DeamonSet除外)标识为Critical,那么对应的Pod调度时仍然会检查Allowed Pod Number, CPU, Memory, EphemeralStorage,Extended Resources是否足够,如果不满足则会触发预选失败,并且在Preempt阶段也只是根据对应的PriorityClass进行正常的抢占逻辑,并没有针对Critical Pod进行特殊处理,因此最终可能会因为找不到满足资源要求的Node,导致该Critical Pod调度失败,一直处于Pending状态。

而用户设置Critical Pod是不想因为资源不足导致调度失败的。那如果我就是想使用Deployment/StatefulSet等(DeamonSet除外)标识为Critical Pod来部署关键服务呢?有以下两个办法:

  1. 按照前面提到的规则2,给Pod设置system-cluster-criticalsystem-node-critical Priority Class,这样就会在scheduler正常的Preempt流程中抢占到资源完成调度。
  2. 按照前面提到的规则1,并且修改GeneralPredicates 的代码如下,检测是否为Critical Pod,如果是,则不执行noncriticalPredicates逻辑,也就是说predicate阶段不对Allowed Pod Number, CPU, Memory, EphemeralStorage,Extended Resources资源进行检查。
func GeneralPredicates(pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
    var predicateFails, resons []algorithm.PredicateFailureReason
    var fit bool
    var err error
    
    // **Modify**: check whether the pod is a Critical Pod, don't invoke noncriticalPredicates if false.
    isCriticalPod := utilfeature.DefaultFeatureGate.Enabled(features.ExperimentalCriticalPodAnnotation) &&
        kubelettypes.IsCriticalPod(newPod)
    
    if !isCriticalPod {
       fit, reasons, err = noncriticalPredicates(pod, meta, nodeInfo)
        if err != nil {
            return false, predicateFails, err
        }
    }
    
    if !fit {
        predicateFails = append(predicateFails, reasons...)
    }

    fit, reasons, err = EssentialPredicates(pod, meta, nodeInfo)
    if err != nil {
        return false, predicateFails, err
    }
    if !fit {
        predicateFails = append(predicateFails, reasons...)
    }

    return len(predicateFails) == 0, predicateFails, nil
}

方法1,其实Kubernetes在Admission Priority检查时已经帮你做了。

// admitPod makes sure a new pod does not set spec.Priority field. It also makes sure that the PriorityClassName exists if it is provided and resolves the pod priority from the PriorityClassName.
func (p *priorityPlugin) admitPod(a admission.Attributes) error {
    ...
    if utilfeature.DefaultFeatureGate.Enabled(features.PodPriority) {
        var priority int32
        if len(pod.Spec.PriorityClassName) == 0 &&
            utilfeature.DefaultFeatureGate.Enabled(features.ExperimentalCriticalPodAnnotation) &&
            kubelettypes.IsCritical(a.GetNamespace(), pod.Annotations) {
            pod.Spec.PriorityClassName = scheduling.SystemClusterCritical
        }
            ...
}

在Admission时候会对Pod的Priority进行检查,如果发现您已经:

  • Enable PriorityClass Feature Gate;
  • Enable ExperimentalCriticalPodAnnotation Feature Gate;
  • 给Pod添加了ExperimentalCriticalPodAnnotation;
  • 部署在kube-system namespace;
  • 没有手动设置自定义PriorityClass;

那么,Admisson Priority阶段会自动给Pod添加SystemClusterCritical(system-cluster-critical) PriorityClass;

最佳实践

通过上面的分析,给出如下最佳实践:在Kubernetes集群中,通过非DeamonSet方式(比如Deployment、RS等)部署关键服务时,为了在集群资源不足时仍能保证抢占调度成功,请确保如下事宜:

  • Enable PriorityClass Feature Gate;
  • Enable ExperimentalCriticalPodAnnotation Feature Gate;
  • 给Pod添加了ExperimentalCriticalPodAnnotation;
  • 部署在kube-system namespace;
  • 千万不要手动设置自定义PriorityClass;

总结

本文介绍了标识一个关键服务为Critical服务的两种方法,并介绍了Critical Pod(DaemonSet部署方式除外)在Predicate in Schedule阶段的行为,给出了最佳实践。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
15天前
|
存储 Kubernetes Docker
【赵渝强老师】Kubernetes中Pod的基础容器
Pod 是 Kubernetes 中的基本单位,代表集群上运行的一个进程。它由一个或多个容器组成,包括业务容器、基础容器、初始化容器和临时容器。基础容器负责维护 Pod 的网络空间,对用户透明。文中附有图片和视频讲解,详细介绍了 Pod 的组成结构及其在网络配置中的作用。
【赵渝强老师】Kubernetes中Pod的基础容器
|
15天前
|
运维 Kubernetes Shell
【赵渝强老师】K8s中Pod的临时容器
Pod 是 Kubernetes 中的基本调度单位,由一个或多个容器组成,包括业务容器、基础容器、初始化容器和临时容器。临时容器用于故障排查和性能诊断,不适用于构建应用程序。当 Pod 中的容器异常退出或容器镜像不包含调试工具时,临时容器非常有用。文中通过示例展示了如何使用 `kubectl debug` 命令创建临时容器进行调试。
|
15天前
|
Kubernetes 调度 容器
【赵渝强老师】K8s中Pod中的业务容器
Pod 是 Kubernetes 中的基本调度单元,由一个或多个容器组成。除了业务容器,Pod 还包括基础容器、初始化容器和临时容器。本文通过示例介绍如何创建包含业务容器的 Pod,并提供了一个视频讲解。示例中创建了一个名为 "busybox-container" 的业务容器,并使用 `kubectl create -f firstpod.yaml` 命令部署 Pod。
|
15天前
|
Kubernetes 容器 Perl
【赵渝强老师】K8s中Pod中的初始化容器
Kubernetes的Pod包含业务容器、基础容器、初始化容器和临时容器。初始化容器在业务容器前运行,用于执行必要的初始化任务。本文介绍了初始化容器的作用、配置方法及优势,并提供了一个示例。
|
15天前
|
存储 Kubernetes 调度
深入理解Kubernetes中的Pod与Container
深入理解Kubernetes中的Pod与Container
25 0
|
15天前
|
Kubernetes Java 调度
Kubernetes中的Pod垃圾回收策略是什么
Kubernetes中的Pod垃圾回收策略是什么
|
15天前
|
存储 Kubernetes 调度
深度解析Kubernetes中的Pod生命周期管理
深度解析Kubernetes中的Pod生命周期管理
|
9天前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
37 1
|
30天前
|
JSON Kubernetes 容灾
ACK One应用分发上线:高效管理多集群应用
ACK One应用分发上线,主要介绍了新能力的使用场景
|
1月前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。

相关产品

  • 容器服务Kubernetes版
  • 下一篇
    无影云桌面