官方文档: Jobs
Jobs
Job 会创建一个或者多个 Pods,并将继续重试 Pods 的执行,直到指定 Pods 成功终止。随着 Pods 成功结束,Job 跟踪记录成功完成的 Pods 个数。当数量达到指定的成功个数阈值时,任务(即 Job)结束。删除 Job 的操作会清除 Job 所创建的的全部 Pods。挂起 Job 的操作会删除 Job 的所有活跃 Pods,直到 Job 被再次恢复执行。
编写 Job 的规范
与 Kubernetes 中其他资源的配置类似,job 也需要 apiVersion
,kind
和 metadata
字段。Job 的名字必须是合法的 DNS 子域名。Job 配置还需要一个 .spec
配置段.
Job 中的 Pod 模板
Job 的 .spec
中只有 .spec.template
是必需的字段。.spec.template
的值是一个 Pod 模板。其定义规范与 Pod 完全相同,只是其中不再需要 apiVersion
或 kind
字段。除了作为 Pod 所必须的字段之外,Job 中的 Pod 模板必须设置合适的标签和合适的重启策略。Job 中的 Pod 的 restartPolicy
只能设置为 Never
或 OnFailure
之一。
Pod 选择符
字段 .spec.selector 是可选的。在绝大多数场合,都不需要为其赋值。详细信息参阅 设置自己的 Pod 选择算符
处理 Pod 和容器失效
Pod 中的容器可能因为多种不同原因失效,例如因为其中的进程退出时返回值为非零,或者容器因为超出内存约束而被杀死等等。如果发生这类事件,并且 .spec.template.spec.restartPolicy = "OnFailure"
,Pod 则继续留在当前节点,但容器会被重新运行。因此,你的程序 需要能够处理在本地被重启的情况,或者要设置 .spec.template.spec.restartPolicy = "Never"
。
整个 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 也必须能够处理并发性问题。
Pod 回退失效策略
在有些情形下,你可能希望 Job 在经历若干次重试之后直接进入失败状态,因为这很可能意味着遇到了配置错误。为了实现这点,可以将 .spec.backoffLimit
设置为视 Job 为失败之前的重试次数。失效回退的限制值默认为 6。与 Job 相关的失效的 Pod 会被 Job 控制器重建,并且以指数型回退计算重试延迟(从 10 秒、20 秒到 40 秒,最多 6 分钟)。当 Job 的 Pod 被删除时,或者 Pod 成功时没有其它 Pod 处于失败状态,失效回退的次数也会被重置(为 0)。
注意:如果你的 Job 的
restartPolicy
被设置为"OnFailure"
,就要注意运行该 Job 的容器会在 Job 到达失效回退次数上限时自动被终止。这会使得调试 Job 中可执行文件的工作变得非常棘手。我们建议在调试 Job 时将restartPolicy
设置为 “Never”, 或者使用日志系统来确保失效 Jobs 的输出不会意外遗失。
Job 的终止与清理
Job 完成时不会再创建新的 Pod,不过已有的 Pod 也不会被删除。保留这些 Pod 使得你可以查看已完成 Pod 的日志输出,以便检查错误、警告 或者其它诊断性输出。Job 完成时 Job 对象也一样被保留下来,这样你就可以查看它的状态。在查看了 Job 状态之后删除老的 Job 的操作留给了用户自己。你可以使用 kubectl
来删除 Job(例如,kubectl delete jobs/pi
或者 kubectl delete -f ./job.yaml
)。 当使用 kubectl 来删除 Job 时,该 Job 所创建的 Pods 也会被删除。
默认情况下,Job 会持续运行,除非某个 Pod 失败(restartPolicy=Never
)或者某个容器出错退出(restartPolicy=OnFailure
)。这时,Job 基于前述的 .spec.backoffLimit
来决定是否以及如何重试。一旦重试次数到达 .spec.backoffLimit
所设的上限,Job 会被标记为失败,其中运行的 Pods 都会被终止。
终止 Job 的另一种方式是设置一个活跃期限。 你可以为 Job 的 .spec.activeDeadlineSeconds
设置一个秒数值。 该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。 一旦 Job 运行时间达到 activeDeadlineSeconds
秒,其所有运行中的 Pod 都会被终止,并且 Job 的状态更新为 type: Failed
及 reason: DeadlineExceeded
。
注意 Job 的 .spec.activeDeadlineSeconds
优先级高于其 .spec.backoffLimit
设置。因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达 activeDeadlineSeconds
所设的时限即不再部署额外的 Pod,即使其重试次数还未达到 backoffLimit
所设的限制。
- 如下示例设置了
activeDeadlineSeconds: 100
1 | apiVersion: batch/v1 |
注意: Job 规约和 Job 中的 Pod 模版规约都有
activeDeadlineSeconds
字段。请确保在合适的层次设置正确的字段。还要注意的是,restartPolicy
对应的是 Pod,而不是 Job 本身; 一旦 Job 状态变为type: Failed
,就不会再发生 Job 重启的动作。换言之,由.spec.activeDeadlineSeconds
和.spec.backoffLimit
所触发的 Job 终结机制 都会导致 Job 永久性的失败,而这类状态都需要手工干预才能解决。
自动清理完成的 Job
完成的 Job 通常不需要留存在系统中。在系统中一直保留它们会给 API 服务器带来额外的压力。 如果 Job 由某种更高级别的控制器来管理,例如 CronJobs
, 则 Job 可以被 CronJob
基于特定的根据容量裁定的清理策略清理掉。
TTL 机制
自动清理已完成 Job (状态为 Complete 或 Failed)的另一种方式是使用由 TTL 控制器所提供 的 TTL 机制。 通过设置 Job 的 .spec.ttlSecondsAfterFinished
字段,可以让该控制器清理掉已结束的资源。
TTL 控制器清理 Job 时,会级联式地删除 Job 对象。 换言之,它会删除所有依赖的对象,包括 Pod 及 Job 本身。 注意,当 Job 被删除时,系统会考虑其生命周期保障,例如其 Finalizers。例如:
1 | apiVersion: batch/v1 |
Job pi-with-ttl
在结束 100 秒之后,可以成为被自动删除的 Job 的。如果该字段设置为 0,Job 在结束之后立即成为可被自动删除的对象。如果该字段没有设置,Job 不会在结束之后被 TTL 控制器自动清除。
注意这种 TTL 机制仍然是一种
Alpha
状态的功能特性,需要配合TTLAfterFinished
特性门控使用。有关详细信息,可参考 TTL 控制器的文档。
Job 示例
一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。你也可以使用 Job 以并行的方式运行多个 Pod。
下面是一个 Job 的配置示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22apiVersion: batch/v1
kind: Job
metadata:
labels:
job-name: pi
name: pi
namespace: default
spec:
suspend: true
ttlSecondsAfterFinished: 100
backoffLimit: 4
completions: 1
parallelism: 1
template:
spec:
containers:
- name: pi
image: perl
imagePullPolicy: Always
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
resource: {}
restartPolicy: Never参数解释:
- suspend: 1.21+ 版本才有这个参数
- ttlSecondsAfterFinished: Job 在执行结束多长时间后(状态为 completed 或 Failed)自动清理,设置为 0 表示执行结束立即删除,不设置则不会清除,设置该参数需要开启 TTLAfterFinished 特性.
- backoffLimit: 执行失败多少次之后就不再执行
- completions: 有多少个 Pod 执行成功,则认为是成功的
- parallelism: 并行执行任务的数量
- 如果 parallelism 数值大于未完成任务数,只会创建未完成的数量;如果 completions 是 4,并发是 3,第一次会创建 3 个 Pod 执行任务,第二次只会创建一个 Pod 执行任务
使用下面的命令运行此示例
1
kubectl apply -f job.yaml
使用 kubectl 检查 Job 的状态
1
kubectl describe job pi
查看 Job 对应已完成的 Pods,可以执行以下命令
1
kubectl get pods
要以机器可读的方式列举隶属于某 Job 的全部 Pods,你可以使用类似下面这条命令:
1
2pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods查看一个 Pod 的标准输出,使用 logs 子命令
1
kubectl logs $pods
CronJob
CronJob 创建基于时间重复调度的 Jobs。一个 CronJob 对象就像 crontab (cron table) 文件中的一行。它用 Cron 格式进行编写,并周期性地在给定的调度时间执行 Job。
注意:所有 CronJob 的
schedule:
时间都是基于kube-controller-manager
. 的时区。如果你的控制平面在 Pod 或是裸容器中运行了 kube-controller-manager, 那么为该容器所设置的时区将会决定 Cron Job 的控制器所使用的时区。
为 CronJob 资源创建清单时,请确保所提供的名称是一个合法的 DNS 子域名。名称不能超过 52 个字符。这是因为 CronJob 控制器将自动在提供的 Job 名称后附加 11 个字符,并且存在一个限制,即 Job 名称的最大长度不能超过 63 个字符。
CronJob 能干嘛
CronJobs 对于创建周期性的、反复重复的任务很有用,例如执行数据备份或者发送邮件。 CronJobs 也可以用来计划在指定时间来执行的独立任务,例如计划当集群看起来很空闲时 执行某个 Job。
示例
下面的 CronJob 示例清单会在每分钟打印出当前时间和问候消息
1 | apiVersion: batch/v1 |
Cron 时间表语法
1 | # ┌───────────── 分钟 (0 - 59) |
特殊语法
输入 | 描述 | 相当于 |
---|---|---|
@yearly(or annually) | 每年1月1日的午夜运行一次 | 0 0 1 1 * |
@monthly | 每月的第一天的午夜运行一次 | 0 0 1 * * |
@weekly | 每周的周日午夜运行一次 | 0 0 * * 0 |
@daily(or midnight) | 每天午夜运行一次 | 0 0 * * * |
@hourly | 每小时运行一次 | 0 * * * * |
CronJob 限制
CronJob 根据其计划编排,在每次该执行任务的时候大约会创建一个 Job。 我们之所以说 “大约”,是因为在某些情况下,可能会创建两个 Job,或者不会创建任何 Job。 我们试图使这些情况尽量少发生,但不能完全杜绝。因此,Job 应该是 幂等的。
如果 startingDeadlineSeconds
设置为很大的数值或未设置(默认),并且 concurrencyPolicy
设置为 Allow,则作业将始终至少运行一次。
注意: 如果
startingDeadlineSeconds
的设置值低于 10 秒钟,CronJob 可能无法被调度。这是因为 CronJob 控制器每 10 秒钟执行一次检查。
对于每个 CronJob,CronJob 控制器(Controller) 检查从上一次调度的时间点到现在所错过了的调度次数。如果错过的调度次数超过 100 次,那么它就不会启动这个任务,并记录这个错误:
1 | Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew. |
需要注意的是,如果 startingDeadlineSeconds
字段非空,则控制器会统计从 startingDeadlineSeconds
设置的值到现在而不是从上一个计划时间到现在错过了多少次 Job。 例如,如果 startingDeadlineSeconds
是 200,则控制器会统计在过去 200 秒中错过了多少次 Job。
如果未能在调度时间内创建 CronJob,则计为错过。 例如,如果 concurrencyPolicy
被设置为 Forbid
,并且当前有一个调度仍在运行的情况下, 试图调度的 CronJob 将被计算为错过。
例如,假设一个 CronJob 被设置为从 08:30:00 开始每隔一分钟创建一个新的 Job, 并且它的 startingDeadlineSeconds
字段未被设置。如果 CronJob 控制器从 08:29:00 到 10:21:00 终止运行,则该 Job 将不会启动,因为其错过的调度 次数超过了 100。
为了进一步阐述这个概念,假设将 CronJob 设置为从 08:30:00 开始每隔一分钟创建一个新的 Job, 并将其 startingDeadlineSeconds 字段设置为 200 秒。 如果 CronJob 控制器恰好在与上一个示例相同的时间段(08:29:00 到 10:21:00)终止运行, 则 Job 仍将从 10:22:00 开始。 造成这种情况的原因是控制器现在检查在最近 200 秒(即 3 个错过的调度)中发生了多少次错过的 Job 调度,而不是从现在为止的最后一个调度时间开始。
CronJob 仅负责创建与其调度时间相匹配的 Job,而 Job 又负责管理其代表的 Pod。
从 Kubernetes v1.21 版本开始,CronJob 控制器的第二个版本被用作默认实现。要禁用此默认 CronJob 控制器而使用原来的 CronJob 控制器,请在 kube-controller-manager
中设置特性门控 CronJobControllerV2
,将此标志设置为 false
。例如:
1 | --feature-gates="CronJobControllerV2=false" |