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

特性状态: Kubernetes v1.31 [stable] (默认启用: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 故障表示不可重试的软件 bug 时,不必要的 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]
    
  2. 应用清单

    kubectl create -f https://k8s.io/examples/controllers/job-pod-failure-policy-failjob.yaml
    
  3. 大约 30 秒后,整个 Job 应该会终止。运行以下命令检查 Job 的状态:

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

    在 Job 状态中,显示以下状况:

    • FailureTarget 状况:其 reason 字段设置为 PodFailurePolicy,并且 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. 检查以下清单:

    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
    
  2. 应用清单

    kubectl create -f https://k8s.io/examples/controllers/job-pod-failure-policy-ignore.yaml
    
  3. 运行此命令检查 Pod 调度到的 nodeName

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

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

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  6. 恢复调度节点

    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
    
  2. 应用清单

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

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

  3. 运行以下命令检查 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 阶段。原则上,这可能是一个瞬态问题,镜像可以被拉取。但是,在这种情况下,镜像不存在,因此我们通过自定义状况来指示此事实。

  4. 添加自定义状况。首先通过运行以下命令准备补丁:

    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
    
  5. 通过运行以下命令删除 Pod 以将其转换为 Failed 阶段:

    kubectl delete pods/$podName
    
  6. 通过运行以下命令检查 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 故障策略避免每个索引不必要的 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]
    
  2. 应用清单

    kubectl create -f https://k8s.io/examples/controllers/job-backoff-limit-per-index-failindex.yaml
    
  3. 大约 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
    

    请注意,输出显示以下内容:

    • 由于允许对索引进行一次重试的退避限制,有两个 Pod 的索引为 0。
    • 只有一个 Pod 的索引为 1,因为失败 Pod 的退出代码与 FailIndex 动作的 Pod 故障策略匹配。
  4. 通过运行以下命令检查 Job 的状态:

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

    在 Job 状态中,可以看到 failedIndexes 字段显示“0,1”,因为两个索引都失败了。由于索引 1 未重试,因此状态字段“failed”表示的失败 Pod 数量等于 3。

清理

删除你创建的 Job

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

集群会自动清理 Pod。

替代方案

你可以完全依赖 Pod 退避故障策略,通过指定 Job 的 .spec.backoffLimit 字段。然而,在许多情况下,在为 .spec.backoffLimit 设置一个低值以避免不必要的 Pod 重试,同时又足够高以确保 Job 不会被 Pod 中断终止之间找到平衡是很有问题的。

上次修改于 2025 年 3 月 27 日太平洋标准时间下午 2:06:文档更新,因为我们将 JobBackoffLimitPerIndex 提升到稳定版本 (#49811) (0d639b9629)