使用 Pod 故障策略处理可重试和不可重试的 Pod 故障
Kubernetes v1.31 [稳定](默认启用)本文档展示了如何使用 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 失败策略,Job 将重试直到达到
backoffLimit(6 次失败)。由于重试使用指数退避,并且在parallelism: 2的情况下,失败成对发生,因此每次重试之间的延迟都会增加。因此,此示例将花费至少 9 分钟才能使 Job 失败。
清理
删除您创建的 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 被调度到的
nodeNamenodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')排出节点以在 Pod 完成之前驱逐 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 重启。
检查以下清单
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 状态中,看到一个 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请注意,输出显示以下内容
- 由于允许一个索引的重试回退限制,因此有两个 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,因此指示的失败 Pod 数量,由状态字段“failed”等于 3。
清理
删除您创建的 Job
kubectl delete jobs/job-backoff-limit-per-index-failindex
集群会自动清理 Pod。
替代方案
您可以仅依赖 Pod 回退失败策略,通过指定 Job 的 .spec.backoffLimit 字段。但是,在许多情况下,很难找到一个平衡点,即设置 .spec.backoffLimit 的低值以避免不必要的 Pod 重试,同时又足够高以确保 Job 不会被 Pod 中断终止。