使用 Pod 故障策略处理可重试和不可重试的 Pod 故障

功能状态: Kubernetes v1.26 [beta]

本文档介绍了如何使用 Pod 失败策略,结合默认的 Pod 回退失败策略,来更好地控制 Job 中容器或 Pod 级别失败的处理方式。

Pod 失败策略的定义可以帮助您

  • 通过避免不必要的 Pod 重试来更好地利用计算资源。
  • 避免由于 Pod 中断(例如 抢占API 驱逐污染 驱逐)导致的 Job 失败。

开始之前

您应该已经熟悉 Job 的基本用法。

您需要有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,可以使用 minikube 创建一个,或者可以使用以下 Kubernetes 游乐场之一

您的 Kubernetes 服务器必须是 v1.25 或更高版本。要检查版本,请输入 kubectl version

确保 功能网关 PodDisruptionConditionsJobPodFailurePolicy 在您的集群中都已启用。

使用 Pod 失败策略来避免不必要的 Pod 重试

通过以下示例,您可以了解如何使用 Pod 失败策略来避免在 Pod 失败表明不可重试的软件错误时不必要的 Pod 重启。

首先,根据配置创建 Job

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-failjob
spec:
  completions: 8
  parallelism: 2
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]
        args:
        - -c
        - echo "Hello world! I'm going to exit with 42 to simulate a software bug." && sleep 30 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main
        operator: In
        values: [42]

通过运行

kubectl create -f job-pod-failure-policy-failjob.yaml

大约 30 秒后,整个 Job 应该会终止。通过运行以下命令检查 Job 的状态

kubectl get jobs -l job-name=job-pod-failure-policy-failjob -o yaml

在 Job 状态中,可以看到一个 job Failed 条件,其字段 reason 等于 PodFailurePolicy。此外,message 字段包含有关 Job 终止的更详细的信息,例如:Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0

为了比较,如果 Pod 失败策略被禁用,它将需要 6 次 Pod 重试,至少需要 2 分钟。

清理

删除您创建的 Job

kubectl delete jobs/job-pod-failure-policy-failjob

集群会自动清理 Pod。

使用 Pod 失败策略来忽略 Pod 中断

通过以下示例,您可以了解如何使用 Pod 失败策略来忽略 Pod 中断,从而避免将 Pod 重试计数器递增到 .spec.backoffLimit 限制。

  1. 根据配置创建 Job

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-ignore
    spec:
      completions: 4
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: docker.io/library/bash:5
            command: ["bash"]
            args:
            - -c
            - echo "Hello world! I'm going to exit with 0 (success)." && sleep 90 && exit 0
      backoffLimit: 0
      podFailurePolicy:
        rules:
        - action: Ignore
          onPodConditions:
          - type: DisruptionTarget
    

    通过运行

    kubectl create -f job-pod-failure-policy-ignore.yaml
    
  2. 运行此命令以检查 Pod 被调度到的 nodeName

    nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
    
  3. 在 Pod 完成之前(在 90 秒内)将节点排空以驱逐 Pod

    kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
    
  4. 检查 .status.failed 以查看 Job 的计数器是否未递增

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  5. 解除节点隔离

    kubectl uncordon nodes/$nodeName
    

Job 恢复并成功。

为了比较,如果 Pod 失败策略被禁用,Pod 中断将导致整个 Job 终止(因为 .spec.backoffLimit 设置为 0)。

清理

删除您创建的 Job

kubectl delete jobs/job-pod-failure-policy-ignore

集群会自动清理 Pod。

使用 Pod 失败策略来避免基于自定义 Pod 条件的不必要的 Pod 重试

通过以下示例,您可以了解如何使用 Pod 失败策略来避免基于自定义 Pod 条件的不必要的 Pod 重启。

  1. 首先,根据配置创建 Job

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-config-issue
    spec:
      completions: 8
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: "non-existing-repo/non-existing-image:example"
      backoffLimit: 6
      podFailurePolicy:
        rules:
        - action: FailJob
          onPodConditions:
          - type: ConfigIssue
    

    通过运行

    kubectl create -f job-pod-failure-policy-config-issue.yaml
    

    请注意,镜像配置错误,因为它不存在。

  2. 通过运行以下命令检查 Job 的 Pod 状态

    kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    您将看到类似于以下内容的输出

    containerStatuses:
    - image: non-existing-repo/non-existing-image:example
       ...
       state:
       waiting:
          message: Back-off pulling image "non-existing-repo/non-existing-image:example"
          reason: ImagePullBackOff
          ...
    phase: Pending
    

    请注意,Pod 仍然处于 Pending 阶段,因为它无法拉取配置错误的镜像。原则上,这可能是一个瞬态问题,镜像可以被拉取。但是,在这种情况下,镜像不存在,因此我们通过自定义条件来指示这一事实。

  3. 添加自定义条件。首先通过运行以下命令准备补丁

    cat <<EOF > patch.yaml
    status:
      conditions:
      - type: ConfigIssue
        status: "True"
        reason: "NonExistingImage"
        lastTransitionTime: "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
    EOF
    

    其次,通过运行以下命令选择 Job 创建的 Pod 之一

    podName=$(kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o jsonpath='{.items[0].metadata.name}')
    

    然后,通过运行以下命令将补丁应用于 Pod 之一

    kubectl patch pod $podName --subresource=status --patch-file=patch.yaml
    

    如果应用成功,您将收到类似于以下内容的通知

    pod/job-pod-failure-policy-config-issue-k6pvp patched
    
  4. 通过运行以下命令删除 Pod 以将其过渡到 Failed 阶段

    kubectl delete pods/$podName
    
  5. 通过运行以下命令检查 Job 的状态

    kubectl get jobs -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    在 Job 状态中,可以看到一个 job Failed 条件,其字段 reason 等于 PodFailurePolicy。此外,message 字段包含有关 Job 终止的更详细的信息,例如:Pod default/job-pod-failure-policy-config-issue-k6pvp has condition ConfigIssue matching FailJob rule at index 0

清理

删除您创建的 Job

kubectl delete jobs/job-pod-failure-policy-config-issue

集群会自动清理 Pod。

替代方案

您可以完全依赖 Pod 回退失败策略,通过指定 Job 的 .spec.backoffLimit 字段。但是,在许多情况下,在设置 .spec.backoffLimit 的低值以避免不必要的 Pod 重试,以及设置足够高的值以确保 Job 不会因 Pod 中断而终止之间找到平衡是比较困难的。

最后修改时间:2024 年 2 月 20 日,太平洋标准时间上午 9:48:将更多功能状态短代码改为数据驱动 (7b6866063f)