This is a stable feature in Kubernetes, and has been since the 1.31 release. You can no longer toggle this feature (the associated feature gate has been removed).
本文向你展示如何结合默认的 Pod 回退失效策略来使用 Pod 失效策略, 以改善 Job 内处理容器级别或 Pod 级别的失效。
Pod 失效策略的定义可以帮助你:
你应该已熟悉了 Job 的基本用法。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 v1.25.要获知版本信息,请输入 kubectl version.
针对定义了 Pod 失效策略的 Job,可以考虑以下一些使用场景:
借用以下示例,你可以学习在 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 失效策略被禁用,Job 将重试直至达到 backoffLimit(6 次失败)。
由于重试采用指数退避算法,并且在 parallelism: 2 的情况下,
故障成对发生,因此每次重试之间的延迟都会增加。
因此,此示例至少需要 9 分钟才会导致 Job 失败。
删除你创建的 Job:
kubectl delete jobs/job-pod-failure-policy-failjob
集群自动清理这些 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 重启。
以下示例自 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 失效策略和按索引的回退限制特性。 本节将展示如何同时使用这两个特性。
检查以下清单文件:
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]
应用此清单:
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
注意输出显示了以下几点:
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 干扰而终止。