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

功能状态: Kubernetes v1.31 [稳定]

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

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

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

开始之前

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

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

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

使用 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 状态中,显示以下条件

  • FailureTarget 条件:具有设置为 PodFailurePolicyreason 字段和包含有关终止的更多信息的 message 字段,例如 Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0。Job 控制器在 Job 被视为失败时立即添加此条件。有关详细信息,请参阅 Job Pod 的终止
  • Failed 条件:与 FailureTarget 条件相同的 reasonmessage。Job 控制器在所有 Job 的 Pod 都终止后添加此条件。

为了比较,如果 Pod 失败策略被禁用,它将重试 Pod 6 次,至少需要 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 年 7 月 29 日太平洋时间下午 8:56: 将 Job Pod 失败策略升级到稳定状态 (#46807) (45a47d170f)