本文已发表超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已发生变化。
Kubernetes 1.28:改进的 Job 故障处理
本文讨论了 Kubernetes 1.28 中用于改进批处理用户 Jobs 的两项新功能:Pod 替换策略(Pod replacement policy)和每索引回退限制(Backoff limit per index)。
这些功能延续了Pod 故障策略(Pod failure policy)开始的努力,以改进 Job 中 Pod 故障的处理。
Pod 替换策略
默认情况下,当一个 Pod 进入终止状态(例如,由于抢占或驱逐)时,Kubernetes 会立即创建一个替换 Pod。因此,两个 Pod 会同时运行。在 API 术语中,当 Pod 具有 deletionTimestamp
且其 phase 为 Pending
或 Running
时,被视为正在终止。
两个 Pod 在给定时间同时运行的场景对某些流行的机器学习框架(如 TensorFlow 和 JAX)来说是有问题的,这些框架要求对于给定索引,在同一时间最多只能有一个 Pod 运行。如果给定索引有两个 Pod 正在运行,Tensorflow 会报错如下:
/job:worker/task:4: Duplicate task registration with task_name=/job:worker/replica:0/task:4
更多详情请参阅(issue)。
在前一个 Pod 完全终止之前创建替换 Pod 也可能在资源匮乏或预算紧张的集群中引发问题,例如:
- 对于待调度的 Pods 来说,集群资源可能难以获得,因为 Kubernetes 可能需要很长时间才能找到可用节点,直到现有 Pods 完全终止。
- 如果启用了集群自动伸缩器(cluster autoscaler),替换 Pods 可能会导致不期望的扩容。
如何使用?
这是一项 Alpha 功能,您可以通过在集群中启用 JobPodReplacementPolicy
功能门(feature gate)来开启。
在集群中启用该功能后,您可以通过创建一个新的 Job 并指定 podReplacementPolicy
字段来使用它,示例如下:
kind: Job
metadata:
name: new
...
spec:
podReplacementPolicy: Failed
...
在该 Job 中,Pods 只会在达到 Failed
阶段后才会被替换,而不会在终止时被替换。
此外,您可以检查 Job 的 .status.terminating
字段。该字段的值表示当前正在终止的、属于该 Job 的 Pod 数量。
kubectl get jobs/myjob -o=jsonpath='{.items[*].status.terminating}'
3 # three Pods are terminating and have not yet reached the Failed phase
这对于外部排队控制器(例如 Kueue)尤其有用,这些控制器会跟踪 Job 中正在运行的 Pods 的配额,直到从当前终止的 Job 中回收资源。
请注意,当使用自定义Pod 故障策略(Pod failure policy)时,podReplacementPolicy: Failed
是默认值。
每索引回退限制(Backoff limit per index)
默认情况下,Indexed Jobs 的 Pod 故障会被计入全局重试限制,由 .spec.backoffLimit
表示。这意味着,如果某个索引持续失败,它会被反复重启直到耗尽限制。一旦达到限制,整个 Job 将被标记为失败,并且某些索引甚至可能从未启动。
这对于希望独立处理每个索引的 Pod 故障的用例来说是有问题的。例如,如果您使用 Indexed Jobs 运行集成测试,其中每个索引对应一个测试套件。
在这种情况下,您可能希望考虑到可能的“不稳定测试”(flake tests),允许每个套件重试 1 或 2 次。可能存在一些有缺陷的套件,导致相应的索引持续失败。在这种情况下,您可能更倾向于限制有缺陷套件的重试次数,同时允许其他套件完成。
- 该功能使您能够:
- 完成所有索引的执行,即使某些索引失败。
如何使用?
通过避免对持续失败索引的不必要重试来更好地利用计算资源。
这是一项 Alpha 功能,您可以通过在集群中启用 JobBackoffLimitPerIndex
功能门(feature gate)来开启。
在集群中启用该功能后,您可以创建一个指定 .spec.backoffLimitPerIndex
字段的 Indexed Job。
示例
apiVersion: batch/v1
kind: Job
metadata:
name: job-backoff-limit-per-index-execute-all
spec:
completions: 8
parallelism: 2
completionMode: Indexed
backoffLimitPerIndex: 1
template:
spec:
restartPolicy: Never
containers:
- name: example # this example container returns an error, and fails,
# when it is run as the second or third index in any Job
# (even after a retry)
image: python
command:
- python3
- -c
- |
import os, sys, time
id = int(os.environ.get("JOB_COMPLETION_INDEX"))
if id == 1 or id == 2:
sys.exit(1)
time.sleep(1)
以下示例展示了如何使用此功能来确保 Job 执行所有索引(前提是没有其他原因导致 Job 提前终止,例如达到 activeDeadlineSeconds
超时或被用户手动删除),并且每次失败都按索引进行控制。
kubectl get pods -l job-name=job-backoff-limit-per-index-execute-all
现在,Job 完成后检查 Pods:
NAME READY STATUS RESTARTS AGE
job-backoff-limit-per-index-execute-all-0-b26vc 0/1 Completed 0 49s
job-backoff-limit-per-index-execute-all-1-6j5gd 0/1 Error 0 49s
job-backoff-limit-per-index-execute-all-1-6wd82 0/1 Error 0 37s
job-backoff-limit-per-index-execute-all-2-c66hg 0/1 Error 0 32s
job-backoff-limit-per-index-execute-all-2-nf982 0/1 Error 0 43s
job-backoff-limit-per-index-execute-all-3-cxmhf 0/1 Completed 0 33s
job-backoff-limit-per-index-execute-all-4-9q6kq 0/1 Completed 0 28s
job-backoff-limit-per-index-execute-all-5-z9hqf 0/1 Completed 0 28s
job-backoff-limit-per-index-execute-all-6-tbkr8 0/1 Completed 0 23s
job-backoff-limit-per-index-execute-all-7-hxjsq 0/1 Completed 0 22s
返回类似以下输出:
kubectl get jobs job-backoff-limit-per-index-fail-index -o yaml
此外,您可以查看该 Job 的状态:
status:
completedIndexes: 0,3-7
failedIndexes: 1,2
succeeded: 6
failed: 4
conditions:
- message: Job has failed indexes
reason: FailedIndexes
status: "True"
type: Failed
输出的末尾状态类似于:
这里,索引 1
和 2
都重试了一次。在它们各自的第二次失败后,超出了指定的 .spec.backoffLimitPerIndex
,因此停止了重试。对比之下,如果禁用每索引回退,那么有问题的索引会一直重试直到超出全局 backoffLimit
,然后整个 Job 将被标记为失败,而不会启动某些更高的索引。
- 如何了解更多?
- 阅读面向用户的Pod 替换策略(Pod replacement policy)、每索引回退限制(Backoff limit per index)和Pod 故障策略(Pod failure policy)文档。
阅读Pod Replacement Policy、Backoff limit per index 和 Pod failure policy 的 KEP。
参与贡献
这些功能由 SIG Apps 赞助。批处理工作组(batch working group)正在积极改进 Kubernetes 用户的批处理用例体验。工作组是专注于特定目标的相对短期的倡议。WG Batch 的目标是改进批处理工作负载用户的体验,为批处理用例提供支持,并增强 Job API 以支持常见用例。如果您对此感兴趣,请通过订阅我们的邮件列表或加入 Slack 来加入工作组。
致谢
与任何 Kubernetes 功能一样,多个人为此付出了贡献,从测试和提交错误到代码评审。