使用 Pod 失效策略处理可重试和不可重试的 Pod 失效

This is a stable feature in Kubernetes, and has been since version 1.31. It was first available in the v1.25 release. You can no longer disable or opt of of this behavior.

本文向你展示如何结合默认的 Pod 回退失效策略来使用 Pod 失效策略, 以改善 Job 内处理容器级别或 Pod 级别的失效。

Pod 失效策略的定义可以帮助你:

  • 避免不必要的 Pod 重试,以更好地利用计算资源。
  • 避免由于 Pod 干扰(例如抢占API 发起的驱逐或基于污点的驱逐) 而造成的 Job 失败。

准备开始

你应该已熟悉了 Job 的基本用法。

你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.25.

要获知版本信息,请输入 kubectl version.

使用场景

针对定义了 Pod 失效策略的 Job,可以考虑以下一些使用场景:

使用 Pod 失效策略以避免不必要的 Pod 重试

借用以下示例,你可以学习在 Pod 失效表明有一个不可重试的软件漏洞时如何使用 Pod 失效策略来避免不必要的 Pod 重启。

  1. 检查以下清单文件:

    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]
    
  1. 应用此清单:

    kubectl create -f https://k8s.io/examples/controllers/job-pod-failure-policy-failjob.yaml
    
  1. 大约 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 重试计数器朝着 .spec.backoffLimit 限制递增来忽略 Pod 干扰。

  1. 检查以下清单文件:

    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
    
  1. 应用此清单:

    kubectl create -f https://k8s.io/examples/controllers/job-pod-failure-policy-ignore.yaml
    
  1. 运行以下这条命令检查 Pod 被调度到的 nodeName

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

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

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  1. 解除节点的保护:

    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. 检查以下清单文件:

    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
    
  1. 应用此清单:

    kubectl create -f https://k8s.io/examples/controllers/job-pod-failure-policy-config-issue.yaml
    

    请注意,镜像配置不正确,因为该镜像不存在。

  1. 通过执行以下命令检查任务 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 阶段,因为它无法拉取错误配置的镜像。 原则上讲这可能是一个暂时问题,镜像还是会被拉取。然而这种情况下, 镜像不存在,因为我们通过一个自定义状况表明了这个事实。

  1. 添加自定义状况。执行以下命令先准备补丁:

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

    其次,执行以下命令选择通过任务创建的其中一个 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
    
  1. 执行以下命令删除此 Pod 将其过渡到 Failed 阶段:

    kubectl delete pods/$podName
    
  1. 执行以下命令查验 Job 的状态:

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

    在 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 失效策略按索引避免不必要的 Pod 重试

为了按索引避免不必要的 Pod 重启,你可以结合使用 Pod 失效策略按索引的回退限制特性。 本节将展示如何同时使用这两个特性。

  1. 检查以下清单文件:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-backoff-limit-per-index-failindex
    spec:
      completions: 4
      parallelism: 2
      completionMode: Indexed
      backoffLimitPerIndex: 1
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: docker.io/library/python:3
            command:
              # The script:
              # - fails the Pod with index 0 with exit code 1, which results in one retry;
              # - fails the Pod with index 1 with exit code 42 which results
              #   in failing the index without retry.
              # - succeeds Pods with any other index.
              - python3
              - -c
              - |
                import os, sys
                index = int(os.environ.get("JOB_COMPLETION_INDEX"))
                if index == 0:
                  sys.exit(1)
                elif index == 1:
                  sys.exit(42)
                else:
                  sys.exit(0)            
      backoffLimit: 6
      podFailurePolicy:
        rules:
        - action: FailIndex
          onExitCodes:
            containerName: main
            operator: In
            values: [42]
    
  1. 应用此清单:

    kubectl create -f https://k8s.io/examples/controllers/job-backoff-limit-per-index-failindex.yaml
    
  1. 大约 15 秒后,检查 Job 所对应的 Pod 状态。你可以运行以下命令:

    kubectl get pods -l job-name=job-backoff-limit-per-index-failindex -o yaml
    

    你将看到类似如下的输出:

    NAME                                            READY   STATUS      RESTARTS   AGE
    job-backoff-limit-per-index-failindex-0-4g4cm   0/1     Error       0          4s
    job-backoff-limit-per-index-failindex-0-fkdzq   0/1     Error       0          15s
    job-backoff-limit-per-index-failindex-1-2bgdj   0/1     Error       0          15s
    job-backoff-limit-per-index-failindex-2-vs6lt   0/1     Completed   0          11s
    job-backoff-limit-per-index-failindex-3-s7s47   0/1     Completed   0          6s
    

    注意输出显示了以下几点:

    • 索引为 0 的 Pod 有两个,因为回退限制允许该索引重试一次。
    • 索引为 1 的 Pod 只有一个,因为失效 Pod 的退出码符合 Pod 失效策略中指定的 FailIndex 动作。
  1. 运行以下命令来查看 Job 的状态:

    kubectl get jobs -l job-name=job-backoff-limit-per-index-failindex -o yaml
    

    在 Job 的状态中,可以看到 failedIndexes 字段显示为 "0,1",表示两个索引都失效了。 由于索引 1 未被重试,状态字段中的 failed 值为 3,表示有 3 个失效的 Pod。

清理

删除你创建的 Job:

kubectl delete jobs/job-backoff-limit-per-index-failindex

集群自动清理 Pod。

替代方案

通过指定 Job 的 .spec.backoffLimit 字段,你可以完全依赖 Pod 回退失效策略。 然而在许多情况下,难题在于如何找到一个平衡,为 .spec.backoffLimit 设置一个较小的值以避免不必要的 Pod 重试, 同时这个值又足以确保 Job 不会因 Pod 干扰而终止。

最后修改 May 06, 2025 at 10:26 AM PST: [zh] Sync pod-failure-policy.md (2a872badaa)