使用 Pod 失效策略处理可重试和不可重试的 Pod 失效

特性状态: Kubernetes v1.26 [beta]

本文向你展示如何结合默认的 Pod 回退失效策略来使用 Pod 失效策略, 以改善 Job 内处理容器级别或 Pod 级别的失效。

Pod 失效策略的定义可以帮助你:

  • 避免不必要的 Pod 重试,以更好地利用计算资源。
  • 避免由于 Pod 干扰(例如抢占API 发起的驱逐或基于污点的驱逐) 而造成的 Job 失败。

准备开始

你应该已熟悉了 Job 的基本用法。

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.25. 要获知版本信息,请输入 kubectl version.

确保特性门控 PodDisruptionConditionsJobPodFailurePolicy 在你的集群中均已启用。

使用 Pod 失效策略以避免不必要的 Pod 重试

借用以下示例,你可以学习在 Pod 失效表明有一个不可重试的软件漏洞时如何使用 Pod 失效策略来避免不必要的 Pod 重启。

首先,基于配置创建一个 Job:

/controllers/job-pod-failure-policy-failjob.yaml使用 Pod 失效策略处理可重试和不可重试的 Pod 失效 - 图1

  1. apiVersion: batch/v1
  2. kind: Job
  3. metadata:
  4. name: job-pod-failure-policy-failjob
  5. spec:
  6. completions: 8
  7. parallelism: 2
  8. template:
  9. spec:
  10. restartPolicy: Never
  11. containers:
  12. - name: main
  13. image: docker.io/library/bash:5
  14. command: ["bash"]
  15. args:
  16. - -c
  17. - echo "Hello world! I'm going to exit with 42 to simulate a software bug." && sleep 30 && exit 42
  18. backoffLimit: 6
  19. podFailurePolicy:
  20. rules:
  21. - action: FailJob
  22. onExitCodes:
  23. containerName: main
  24. operator: In
  25. values: [42]

运行以下命令:

  1. kubectl create -f job-pod-failure-policy-failjob.yaml

大约 30 秒后,整个 Job 应被终止。通过运行以下命令来查看 Job 的状态:

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

在 Job 状态中,看到一个任务状况为 Failed,其 reason 字段等于 PodFailurePolicy。 此外,message 字段包含有关 Job 终止更详细的信息,例如: Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0

为了比较,如果 Pod 失效策略被禁用,将会让 Pod 重试 6 次,用时至少 2 分钟。

清理

删除你创建的 Job:

  1. kubectl delete jobs/job-pod-failure-policy-failjob

集群自动清理这些 Pod。

使用 Pod 失效策略来忽略 Pod 干扰

通过以下示例,你可以学习如何使用 Pod 失效策略将 Pod 重试计数器朝着 .spec.backoffLimit 限制递增来忽略 Pod 干扰。

注意:

这个示例的时机比较重要,因此你可能需要在执行之前阅读这些步骤。 为了触发 Pod 干扰,重要的是在 Pod 在其上运行时(自 Pod 调度后的 90 秒内)腾空节点。

  1. 基于配置创建 Job:

    /controllers/job-pod-failure-policy-ignore.yaml使用 Pod 失效策略处理可重试和不可重试的 Pod 失效 - 图2

    1. apiVersion: batch/v1
    2. kind: Job
    3. metadata:
    4. name: job-pod-failure-policy-ignore
    5. spec:
    6. completions: 4
    7. parallelism: 2
    8. template:
    9. spec:
    10. restartPolicy: Never
    11. containers:
    12. - name: main
    13. image: docker.io/library/bash:5
    14. command: ["bash"]
    15. args:
    16. - -c
    17. - echo "Hello world! I'm going to exit with 0 (success)." && sleep 90 && exit 0
    18. backoffLimit: 0
    19. podFailurePolicy:
    20. rules:
    21. - action: Ignore
    22. onPodConditions:
    23. - type: DisruptionTarget

    运行以下命令:

    1. kubectl create -f job-pod-failure-policy-ignore.yaml
  2. 运行以下这条命令检查将 Pod 调度到的 nodeName

    1. nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
  3. 腾空该节点以便在 Pod 完成任务之前将其驱逐(90 秒内):

    1. kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
  4. 查看 .status.failed 以检查针对 Job 的计数器未递增:

    1. kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
  5. 解除节点的保护:

    1. kubectl uncordon nodes/$nodeName

Job 恢复并成功完成。

为了比较,如果 Pod 失效策略被禁用,Pod 干扰将使得整个 Job 终止(随着 .spec.backoffLimit 设置为 0)。

清理

删除你创建的 Job:

  1. kubectl delete jobs/job-pod-failure-policy-ignore

集群自动清理 Pod。

基于自定义 Pod 状况使用 Pod 失效策略避免不必要的 Pod 重试

根据以下示例,你可以学习如何基于自定义 Pod 状况使用 Pod 失效策略避免不必要的 Pod 重启。

说明:

以下示例自 v1.27 起开始生效,因为它依赖于将已删除的 Pod 从 Pending 阶段过渡到终止阶段 (参阅 Pod 阶段)。

  1. 首先基于配置创建一个 Job:

    /controllers/job-pod-failure-policy-config-issue.yaml使用 Pod 失效策略处理可重试和不可重试的 Pod 失效 - 图3

    1. apiVersion: batch/v1
    2. kind: Job
    3. metadata:
    4. name: job-pod-failure-policy-config-issue
    5. spec:
    6. completions: 8
    7. parallelism: 2
    8. template:
    9. spec:
    10. restartPolicy: Never
    11. containers:
    12. - name: main
    13. image: "non-existing-repo/non-existing-image:example"
    14. backoffLimit: 6
    15. podFailurePolicy:
    16. rules:
    17. - action: FailJob
    18. onPodConditions:
    19. - type: ConfigIssue

    执行以下命令:

    1. kubectl create -f job-pod-failure-policy-config-issue.yaml

    请注意,镜像配置不正确,因为该镜像不存在。

  2. 通过执行以下命令检查任务 Pod 的状态:

    1. kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o yaml

    你将看到类似以下输出:

    1. containerStatuses:
    2. - image: non-existing-repo/non-existing-image:example
    3. ...
    4. state:
    5. waiting:
    6. message: Back-off pulling image "non-existing-repo/non-existing-image:example"
    7. reason: ImagePullBackOff
    8. ...
    9. phase: Pending

    请注意,Pod 依然处于 Pending 阶段,因为它无法拉取错误配置的镜像。 原则上讲这可能是一个暂时问题,镜像还是会被拉取。然而这种情况下, 镜像不存在,因为我们通过一个自定义状况表明了这个事实。

  3. 添加自定义状况。执行以下命令先准备补丁:

    1. cat <<EOF > patch.yaml
    2. status:
    3. conditions:
    4. - type: ConfigIssue
    5. status: "True"
    6. reason: "NonExistingImage"
    7. lastTransitionTime: "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
    8. EOF

    其次,执行以下命令选择通过任务创建的其中一个 Pod:

    1. podName=$(kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o jsonpath='{.items[0].metadata.name}')

    随后执行以下命令将补丁应用到其中一个 Pod 上:

    1. kubectl patch pod $podName --subresource=status --patch-file=patch.yaml

    如果被成功应用,你将看到类似以下的一条通知:

    1. pod/job-pod-failure-policy-config-issue-k6pvp patched
  4. 执行以下命令删除此 Pod 将其过渡到 Failed 阶段:

    1. kubectl delete pods/$podName
  5. 执行以下命令查验 Job 的状态:

    1. 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:

  1. kubectl delete jobs/job-pod-failure-policy-config-issue

集群自动清理 Pod。

替代方案

通过指定 Job 的 .spec.backoffLimit 字段,你可以完全依赖 Pod 回退失效策略。 然而在许多情况下,难题在于如何找到一个平衡,为 .spec.backoffLimit 设置一个较小的值以避免不必要的 Pod 重试, 同时这个值又足以确保 Job 不会因 Pod 干扰而终止。