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

特性状态: Kubernetes v1.31 [stable] (enabled by default: true)

本文向你展示如何结合默认的 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:
              # 此脚本:
              # - 使索引为 0 的 Pod 以退出码 1 失败,这将导致一次重试;
              # - 使索引为 1 的 Pod 以退出码 42 失败,这将导致此索引直接失败且不重试;
              # - 使所有其他索引的 Pod 成功。
              - 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)