使用 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 失效策略的定义可以帮助你:
准备开始
你应该已熟悉了 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
状况:有一个设置为PodFailurePolicy
的reason
字段和一个包含更多有关终止信息的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 重试计数器朝着 .spec.backoffLimit
限制递增来忽略 Pod 干扰。
注意:
这个示例的时机比较重要,因此你可能需要在执行之前阅读这些步骤。 为了触发 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
-
解除节点的保护:
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 重启。
说明:
以下示例自 v1.27 起开始生效,因为它依赖于将已删除的 Pod 从 Pending
阶段过渡到终止阶段
(参阅 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
请注意,镜像配置不正确,因为该镜像不存在。
-
通过执行以下命令检查任务 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
其次,执行以下命令选择通过任务创建的其中一个 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 失效策略和按索引的回退限制特性。 本节将展示如何同时使用这两个特性。
-
检查以下清单文件:
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
注意输出显示了以下几点:
- 索引为 0 的 Pod 有两个,因为回退限制允许该索引重试一次。
- 索引为 1 的 Pod 只有一个,因为失效 Pod 的退出码符合 Pod 失效策略中指定的
FailIndex
动作。
-
运行以下命令来查看 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 干扰而终止。