使用 Pod 故障策略处理可重试和不可重试的 Pod 故障
Kubernetes v1.31 [stable]
(默认启用: true)本文档展示如何使用Pod 失败策略(Pod failure policy),结合默认的Pod 退避失败策略(Pod backoff failure policy),来更好地控制Job中容器或 Pod 级别的故障处理。
Pod 失败策略的定义可能有助于你
- 通过避免不必要的 Pod 重试来更好地利用计算资源。
- 避免由于 Pod 中断(例如抢占(preemption)、API 发起的驱逐(API-initiated eviction)或污点(taint)引起的驱逐)而导致 Job 失败。
开始之前
你应该已经熟悉 Job 的基本用法。
你需要一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在本教程中使用至少有两个非控制平面主机的节点的集群。如果你还没有集群,可以使用 minikube 创建一个,或者使用以下 Kubernetes 练习场之一:
你的 Kubernetes 服务器必须是 v1.25 或更高版本。要检查版本,请输入 kubectl version
。
使用场景
考虑以下为定义了 Pod 失败策略的 Job 所设计的用例:
使用 Pod 失败策略避免不必要的 Pod 重试
通过以下示例,你可以学习如何使用 Pod 失败策略,在 Pod 失败表明存在不可重试的软件缺陷时,避免不必要的 Pod 重启。
检查以下清单:
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 https://k8s.io/examples/controllers/job-pod-failure-policy-failjob.yaml
大约 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
状况具有相同的reason
和message
。Job 控制器在 Job 的所有 Pod 终止后添加此状况。
相比之下,如果 Pod 失败策略被禁用,Pod 将需要重试 6 次,至少耗时 2 分钟。
清理
删除创建的 Job:
kubectl delete jobs/job-pod-failure-policy-failjob
集群会自动清理 Pod。
使用 Pod 失败策略忽略 Pod 中断
通过以下示例,你可以学习如何使用 Pod 失败策略,使 Pod 中断不会增加 Pod 重试计数,从而达到 .spec.backoffLimit
限制。
注意
时机对于本示例非常重要,因此你可能需要在执行前阅读这些步骤。为了触发 Pod 中断,重要的是在 Pod 正在节点上运行时(在 Pod 被调度后 90 秒内)排空节点。检查以下清单:
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 https://k8s.io/examples/controllers/job-pod-failure-policy-ignore.yaml
运行此命令检查 Pod 被调度到哪个
nodeName
:nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
排空节点以在 Pod 完成前将其逐出(在 90 秒内):
kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
检查
.status.failed
以确认 Job 的计数器未增加:kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
Uncordon 节点:
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 重试。
检查以下清单:
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 https://k8s.io/examples/controllers/job-pod-failure-policy-config-issue.yaml
请注意,镜像配置错误,因为它不存在。
运行以下命令检查 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
阶段。原则上,这可能是一个瞬时问题,镜像可能会被拉取。但是,在此示例中,镜像不存在,因此我们通过自定义状况来表明这一事实。添加自定义状况。首先通过运行以下命令准备补丁:
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
通过运行以下命令删除 Pod,使其转换到
Failed
阶段:kubectl delete pods/$podName
运行以下命令检查 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
。
注意
在生产环境中,步骤 3 和 4 应由用户提供的控制器自动化。清理
删除创建的 Job:
kubectl delete jobs/job-pod-failure-policy-config-issue
集群会自动清理 Pod。
使用 Pod 失败策略避免每个索引的不必要的 Pod 重试
为了避免每个索引的不必要的 Pod 重启,你可以使用 Pod 失败策略(Pod failure policy)和每个索引的回退限制(backoff limit per index)特性。本页面的这一节展示如何结合使用这些特性。
检查以下清单:
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]
应用清单
kubectl create -f https://k8s.io/examples/controllers/job-backoff-limit-per-index-failindex.yaml
大约 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 失败策略。
运行以下命令检查 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 退避失败策略(Pod backoff failure policy),通过指定 Job 的 .spec.backoffLimit
字段。但是,在许多情况下,很难在为 .spec.backoffLimit
设置一个较低的值以避免不必要的 Pod 重试,同时又设置一个足够高的值以确保 Job 不会被 Pod 中断终止之间找到平衡。