深入分析Kubernetes Critical Pod(一)

大家在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阶段的行为,给出了最佳实践。

### Docker 代码审计的方法、工具及最佳实践 #### 使用静态分析工具进行Dockerfile审查 为了确保容器镜像的安全性稳定性,建议采用静态分析工具来审核`Dockerfile`文件。这类工具能够自动检测常见的错误配置以及潜在的安全风险。Clair是个开源项目,它能帮助识别Linux应用程序中存在的已知漏洞;而Hadolint则专注于检查语法正确性并提供改进建议。 ```bash # 安装 Hadolint 并对其执行基本的 Dockerfile 检查 $ sudo apt-get install hadolint $ hadolint ./path/to/Dockerfile ``` 对于更深入的安全评估,则可借助Anchore Engine这样的平台来进行全面的内容扫描[^1]。 #### 动态测试与持续集成/部署(CI/CD)管道集成 除了静态分析外,在CI/CD流程中加入动态测试环节也常重要。Trivy是款集成了多种功能于体的容器安全解决方案,可以在构建阶段就发现问题所在,并阻止有问题的映像进入生产环境。此外,还可以利用Aqua Security提供的Kube-bench等工具针对Kubernetes集群内的Pod资源对象实施合规性检验。 ```yaml # .github/workflows/ci.yml 中的部分 CI 测试工作流定义 name: Build and Test on: push: branches: - main jobs: build-and-test: runs-on: ubuntu-latest steps: - name: Checkout codebase uses: actions/checkout@v2 - name: Run Trivy scanner against built image run: | docker pull trivyrepo/trivy:latest trivy image myapp:tagged_version --exit-code=0 --severity HIGH,CRITICAL ``` #### 日志监控入侵检测系统的应用 启用详细的日志记录机制同样不可忽视。通过合理配置syslog-ng或者rsyslog服务端程序,可以将所有重要的操作事件集中保存起来以便日后查询。与此同时,考虑引入OSSEC HIDS (Host-based Intrusion Detection System),它可以实时监视系统调用活动并对可疑模式发出警报通知管理员采取相应措施[^3]。 #### 实施最小权限原则(Minimal Privilege Principle) 最后但并最不重要的点就是遵循最小化授权策略。即只赋予必要的访问控制权给各个组件服务账号,从而减少攻击面。具体做法包括但不限于:创建专用的服务账户而使用root用户启动进程;限制宿主机上的挂载点数量及其读写属性;关闭不必要的守护进程等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值