Job 创建一个或多个 pod,并且确保指定数量的 pod 成功终止。Pod 成功完成后,Job 将跟踪成功完成的情况。当达到指定的成功完成次数时,任务(即 Job)就完成了。删除 Job 将清除其创建的 pod。
一种简单的情况时创建一个 Job 对象,以便可靠地运行一个 Pod 来完成。如果第一个 pod 失败了或被删除了(例如,由于节点硬件故障或节点重启),则 Job 对象将启动一个新的 pod。
也可以使用 Job 去并行运行多个 pod。
下面是一个 Job 配置样例。它计算 π 到2000位并将其打印出来。大约需要 10 秒钟才能完成。
controllers/job.yaml
|
---|
|
可以使用以下命令运行示例:
kubectl apply -f https://k8s.io/examples/controllers/job.yaml
job "pi" created
使用 kubectl
命令检查 Job 的状态:
kubectl describe jobs/pi
Name: pi
Namespace: default
Selector: controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
Labels: controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
job-name=pi
Annotations: <none>
Parallelism: 1
Completions: 1
Start Time: Tue, 07 Jun 2016 10:56:16 +0200
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
job-name=pi
Containers:
pi:
Image: perl
Port:
Command:
perl
-Mbignum=bpi
-wle
print bpi(2000)
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {job-controller } Normal SuccessfulCreate Created pod: pi-dtn4q
要查看 Job 的已完成 pod,可以使用 kubectl get pods
命令。
要以机器可读的形式列出属于 Job 的所有 pod,可以使用以下命令:
pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods
pi-aiw0a
这里,选择器与 Job 的选择器相同。--output=jsonpath
选项指定一个表达式来从 pod 返回列表中获取每个 pod 的名称。
查看其中一个 pod 的标准输出:
kubectl logs $pods
The output is similar to this:
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
和其他 Kubernetes 配置一样,Job 需要 apiVersion
,kind
和 metadata
字段。
Job 也需要 .spec
部分。
.spec.template
是 .spec
中唯一必填字段。
.spec.template
是 pod 模板。它具有与 pod 完全相同的架构,只是它是嵌套的并且没有 apiVersion
或 kind
。
除了 pod 的必填字段,Job 中的 pod 模板还必须指定合适的标签(参考 pod 选择器)和适当的重启策略。
RestartPolicy
只能等于 Never
或 OnFailure
。
.spec.selector
字段是可选的。在大多数场景下不必指定它。请参阅指定自己的 pod 选择器。
下面是适合作为 Job 运行的三种主要任务类型:
.spec.completions
指定一个非零正数值。.spec.completions
范围内都有一个 pod 成功完成时,Job 才算完成。.spec.completions
之间的索引值。.spec.completions
的话, 默认为 .spec.parallelism
。对于非并行 Job,可以不设置 .spec.completions
和 .spec.parallelism
。两者均未设置时,默认值为 1。
对于一个固定完成计数 Job,应该将 .spec.completions
设置为所需的完成数量。.spec.parallelism
可以设置,也可以不设置,默认值为 1。
对于一个工作队列 Job,不要设置 .spec.completions
,并且将 .spec.parallelism
设置一个非负整数。
有关如何使用不同类型 Job 的更多信息,请参考 Job 模式部分。
请求的并发数(.spec.parallelism
)可以设置为任何非负的整数。如果未指定,默认值为 1。如果指定为 0,则 Job 就被有效地暂停直到该配置值增加。
实际的并发数(在任何时刻运行着的 pod 数量)由于下面的一些原因,可能大于或小于请求的并发数:
.spec.parallelism
值将被优先忽略。ResourceQuota
,缺少权限等)未能创建 pod,则 pod 数量将少于请求数。Pod 中的容器可能由于多种原因而失败,例如,由于容器中的进程以非零退出码退出,或者容器由于超过内存限制而被杀掉等。如果容器失败发生,并且 .spec.template.spec.restartPolicy = "OnFailure"
,则 pod 停留在节点上,但是容器会重新运行。因此,你的程序需要处理本地重启的情况,或者指定 .spec.template.spec.restartPolicy = "Never"
。参阅 pod 生命周期获取更多关于 restartPolicy
的信息。
由于多种原因,整个 pod 也可能失败,比如,当 pod 从节点上被踢走(节点在升级,重启,被删除等),或者 pod 中的容器失败且设置了 .spec.template.spec.restartPolicy = "Never"
。当 pod 失败后,Job 控制器将启动一个新的 pod。这意味着你的应用程序需要处理这种情况(应用程序在新的 pod 中重启)。特别是,它需要处理由先前运行引起的临时文件,锁,不完整输出等等。
请注意,即使你指定了 .spec.parallelism = 1
,.spec.completions = 1
和 .spec.template.spec.restartPolicy = "Never"
,统一程序有时也会启动两次。
如果你指定 .spec.parallelism
和 .spec.completions
配置都大于 1,则可能同时有多个 pod 运行。因此,你的 pod 必须容忍并发。
在某些情况下(配置中的逻辑错误等),你需要在重试一定次数后才使 Job 失败。为此需要设置 .spec.backoffLimit
配置值为判断 Job 失败前的重试次数。 退避限制默认设置为 6。与 Job 相关的 pod在失败后,经过一个指数级退避延迟(10 秒,20 秒,40 秒,上限为 6 分钟),由 Job 控制器重新创建。 如果在 Job 的下一次状态检查之前未出现新的失败 pod,则会重置退避计数。
注意:1.12 之前的 Kubernetes 版本仍然存在问题 #54870
注意:如果你的 Job 外配置了
restartPolicy = "OnFailure"
,请记住,一旦 Job 达到退避限制,运行在该 Job 的容器将被终止。这会使得调试 Job 的可执行将变得更加困难。我们建议在调试 Job 或使用日志系统时设置restartPolicy = "Never"
,以确保失败 Job 的输出不会在不经意间丢失。
当 Job 完成后,不会再创建 pod,但是 pod 也不会被删除。将这些 pod 保留,使你仍然可以查看已完成 pod 的日志来检查错误,警告或其他诊断输出。Job 对象在完成后也同样保留下来,以便你可以查看它的状态。由用户来决定在查看完状态后删除旧的 Job。使用 kubectl
(例如 kubectl delete jobs/pi
或 kubectl delete -f ./job.yaml
)来删除 Job。当你使用 kubectl
删除 Job 后,它所创建的所有 pod 也会被删除。
默认情况下,除非 pod 失败(restartPolicy=Never
)或容器错误退出(restartPolicy=OnFailure
),否则 Job 将不会被中断运行。Job 中断将遵循上面介绍的 .spec.backoffLimit
。一旦 .spec.backoffLimit
达到,Job 将被标记为失败,企鹅别所有正运行的 pod 将被终止。
终止 Job 的另一种方式是设置有限期限。通过设置 Job 对象的 .spec.activeDeadlineSeconds
字段为秒数来达到。activeDeadlineSeconds
适用于 Job 存在期间,不管有多少 pod 被创建。一旦 Job 达到 activeDeadlineSeconds
,所有运行中的 pod将被终止,并且 Job 的状态将变成 type: Failed
与 reason: DeadlineExceeded
。
请注意,Job 的 .spec.activeDeadlineSeconds
配置优先于 .spec.backoffLimit
配置。因此,重试一个或多个失败 pod 的 Job 在达到 activeDeadlineSeconds
指定的时间限制前,不会部署其他的 pod,即使 backoffLimit
尚未达到。
例子:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-timeout
spec:
backoffLimit: 5
activeDeadlineSeconds: 100
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
请注意,Job 对象中的 Job 规范和 Pod 模板规范都有 activeDeadlineSeconds
字段。确保将此字段设置在适当的层级。
系统中已完成的 Job 通常不再需要。将它们保留在系统中将会给 API 服务器带来压力。如果 Job 由更高级别的控制器管理,比如 CronJobs,则 Job 可以由 CronJobs 基于指定的基于容量的清理策略来进行清除。
Kubernetes v1.12
alpha
清理已完成 Job(Complete
或 Failed
)的另一个方式是通过使用 TTL 控制器提供的 TTL 机制,TTL 通过指定 Job 的 .spec.ttlSecondsAfterFinished
字段来清理已完成资源。
当 TTL 控制器清理 Job 时,它将级联删除 Job,即删除它的依赖对象,比如 pod 将和 Job 一起被删除。请注意,当 Job 被删除后,它的生命周期保证,如终结者,将被触发。
例如:
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-ttl
spec:
ttlSecondsAfterFinished: 100
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
pi-with-ttl
Job 在完成后 100 秒就会自动删除掉。
如果该字段设为为 0
,那么 Job 将在完成后立即被自动删除掉。如果该字段未设置,则 Job 在完成后将不会被 TTL 控制器清理掉。
请注意,此 TTL 机制处于 alpha 阶段,由 TTLAfterFinished
功能门控制是否启动。有关详细信息,请参阅 TTL 控制器中关于已完成资源的处理。
Job 对象可用于支持 pod 的可靠并行执行。Job 对象并非设计为支持紧密通信的并行过程(常见于科学计算领域)。它被用于支持一组独立但相关的*工作项*的并行处理。这些可能是要发送的电子邮件,要渲染的帧,要编码转换的文件,要扫描的 NoSQL 数据库中一段范围内的键等等。
在复杂的系统中,可能会有多个不同的工作项集。这里我们只考虑用户想要一起管理一组工作项 — *批处理任务*。
并行计算有几种不同的模式,每种都有各自的优缺点。权衡点如下:
权衡总结如下表,第 2 至 4 列对应上面的权衡点。模式名称也是样例和详细描述的链接。
模式 | 单个 Job 对象 | 比工作项更少的 pod? | 使用未修改的 app? | Kube 1.1 中生效? |
---|---|---|---|---|
Job 模板扩展 | ✓ | ✓ | ||
一个项对应一个 pod 的队列 | ✓ | 有时 | ✓ | |
具有可变 pod 数量的队列 | ✓ | ✓ | ✓ | |
具有静态工作分配的单个 Job | ✓ | ✓ |
当使用 .spec.completions
指定完成数时,由 Job 控制器创建的每个 pod 都有一个相同的 规范
。这意味着一个任务的所有 pod 将有相同的命令行和镜像,相同的卷以及(几乎)相同的环境变量。这些模式通过不同的方式安排 pod 去处理不同的任务。
下表列出了每种模式下对 .spec.parallelism
和 .spec.completions
需要做的设置。
模式 | .spec.completions |
.spec.parallelism |
---|---|---|
Job 模板扩展 | 1 | 应该为 1 |
一个项对应一个 pod 的队列 | W | 任意 |
具有可变 pod 数量的队列 | 1 | 任意 |
具有静态工作分配的单个 Job | W | 任意 |
通常,在创建 Job 对象时,不用指定 .spec.selector
。系统的默认逻辑会在创建 Job 时添加这个字段。它会选择一个不会和其他 pod 重叠的选择器。
但是,在某些情况下,你可能需要去覆盖默认设置的选择器。为此,你需要指定 Job 的 .spec.selector
。
当设置时需要特别小心。如果你指定的标签选择器不是 Job 的 pod 所独有的,并且匹配到了不相关的 pod,那么不相关 Job 的 pod 可能会被删除,或此 Job 可能将其他 pod 统计为已完成,或者其中一个或两个 Job 可能拒绝创建 pod 或运行完成。如果一个选择了非唯一的选择器,则其他的控制器(比如 ReplicationController)和它的 pod 的行为也将是不可预测的。当指定了 .spec.selector
,Kubernetes将不会阻止你产生错误。
下面的例子描述了你想要使用该功能的场景。
假设 old
Job 已经在运行了。你想要运行中的 pod 保持运行,但是你想 Job 创建的其他 pod 使用不同的 pod 模板,并使 Job 具有一个新的名称。你不能更新 Job 对象,因为这些字段是不可更新的。因此,你需要删除 old
但是_让它的 pod 运行_,可以使用 kubectl delete jobs/old --cascade=false
命令。在删除之前,你需要记下它所使用的选择器:
kubectl get job old -o yaml
kind: Job
metadata:
name: old
...
spec:
selector:
matchLabels:
controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
然后使用新名称 new
创建一个新的 Job ,并且显示指定相同的选择器。由于现有的 pod 具有 controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002
标签,它们也受 new
Job 的控制。
由于你不再使用系统通常为你自动生成的选择器,你需要在新的 Job 中指定 manualSelector: true
。
kind: Job
metadata:
name: new
...
spec:
manualSelector: true
selector:
matchLabels:
controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
...
新的 Job 本身将具有一个与 a8f3d00d-c6d2-11e5-9f87-42010af00002
不通的 uid。设置 manualSelector: true
来告诉系统你知道自己在做什么,并使其允许这种不匹配。
当 pod 所在节点重启或发生故障时,pod 将终止并且不会重启。然后,Job 将创建新的 pod 替换终止的 pod。因此,即使你的应用仅需要一个 pod,我们还是推荐使用 Job 而不是裸 pod。
Job 是副本控制器的补充。副本控制器管理预期不会终止的 pod(比如 web 服务器),而 Job 管理预期将会终止的 pod(比如批处理任务)。
正如在 Pod 生命周期中所讨论的,Job
*仅*适用于具有 RestartPolicy
值为 OnFailure
或 Never
的 pod。(注意:如果 RestartPolicy
未指定,默认值为 Always
。)
单个 Job 的另一种模式是创建一个创建其他 pod 的 pod,充当这些 pod 的一种自定义控制器。这提供了更大的灵活性,但是入门起来可能有点复杂,并且与 Kubernetes 的集成较少。
这种模式的一个样例是:一个 Job 启动一个 pod 来运行一个脚本,该脚本依次启动 Spark 主控制器(参阅 spark 例子),并运行 spark 驱动,然后进行清理。
这种方法的优点是整个过程可以获取 Job 对象的完成保证,不是完全控制 pod 的创建以及如何将工作分配给 pod。
你可以使用 CronJob
来创建一个会在指定的时间/日期执行的 Job,类似于 Unix 工具 cron
。
此页是否对您有帮助?
感谢反馈。如果您有一个关于如何使用 Kubernetes 的特定的、需要答案的问题,可以访问 Stack Overflow. 在 GitHub 仓库上登记新的问题 报告问题 或者 提出改进建议.