1. Job

Job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。

Job 会创建一个或者多个 Pods,并确保指定数量的 Pods 成功终止。 随着 Pods 成功结束,Job 跟踪记录成功完成的 Pods 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pods。

一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。

一次任务,也可以使用 Job 以并行的方式同时运行多个 Pod。

1.1. API 版本对照表

Kubernetes 版本 Batch API 版本 默认开启
v1.5+ batch/v1

1.2. Job Spec

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      name: pi
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

1.2.1. RestartPolicy

spec.RestartPolicy: 可选的配置, Job 中 RestartPolicy 仅支持 Never 或 OnFailure。

  • Never : 无论任务执行是否成功都不会重新执行。
  • OnFailure : 只有当任务执行失败时才会重新执行。

单个 Pod 时,默认 Pod 成功运行后 Job 即结束。正常运行结束的 pod 状态是 Completed

1.2.2. activeDeadlineSeconds

spec.activeDeadlineSeconds: 可选的配置,标志失败 Pod 的可重试最大时间(截止时间),超过这个时间不会继续重试。

该配置只有在 spec.RestartPolicyOnFailure 时才会有效。

1.2.3. completions

.spec.completions: 可选的配置,标志 Job 结束需要成功运行的 Pod 个数,默认为1。

默认情况下,该配置项的值为1,即job只运行一次后就完成。

1.2.4. parallelism

.spec.parallelism: 可选的配置,标志并行(并发)运行的 Pod 的个数,默认为1。如果指定为0,则 Job 就被有效地暂停直到该配置值增加。

一般需要结合 .spec.completions 使用,在 spec.completios 大于 1 时才有意义。

下面是适合作为 Job 运行的三种主要任务类型:

  • 非并行 Job

    • 通常,除非 pod 发生故障,否则仅启动一个 pod。
    • 一旦 pod 成功终止,Job 即完成。
  • 配置了*固定完成计数*的并行 Job:

    • .spec.completions 指定一个非零正数值。
    • Job 代表整体任务,并且在 1 到 .spec.completions 范围内都有一个 pod 成功完成时,Job 才算完成。
    • 尚未实现: 每个 pod 都会被传递一个从 1 到 .spec.completions 之间的索引值。
  • 具有工作队列的并行 Job:

    • 不指定 .spec.completions 的话, 默认为 .spec.parallelism
    • Pod 必须在彼此之间或外部服务之间进行协调,来确定每个 pod 该处理什么任务。例如,一个 pod 可以从工作队列中获取一批任务(最多 N 个)。
    • 每个 pod 都可以独立确定其所有对端是否完成,从而确定整个 Job 完成。
    • 当 Job 中的任何 pod 成功结束后,不会再创建新的 pod。
    • 一旦至少一个 pod 成功终止并且所有的 pod 都终止了,则 Job 就成功完成了。
    • 一旦 任何 pod 成功退出,其他 pod 都不应该为此任务做任务工作或编写任何输出。它们都应该处在退出过程中。

对于非并行 Job,可以不设置 .spec.completions.spec.parallelism。两者均未设置时,默认值为 1。

对于一个固定完成计数 Job,应该将 .spec.completions 设置为所需的完成数量。.spec.parallelism 可以设置,也可以不设置,默认值为 1。

对于一个工作队列 Job,不要设置 .spec.completions,并且将 .spec.parallelism 设置一个非负整数。

实际的并发数(在任何时刻运行着的 pod 数量)由于下面的一些原因,可能大于或小于请求的并发数:

  • 对于固定完成计数 Job,实际上并行运行的 pod 数量不会超过剩余的完成数。更高的 .spec.parallelism 值将被优先忽略。
  • 对于工作队列 Job,任何 pod 成功之后都不会启动新的 pod。然而,剩余的 pod 可以完成。
  • 如果控制器还没来得及反应。
  • 如果控制器由于任何原因(缺少 ResourceQuota,缺少权限等)未能创建 pod,则 pod 数量将少于请求数。
  • 控制器可能会因为同一 Job 中有过多之前失败的 pod,从而限制新的 pod 的创建。
  • 当 pod 优雅关闭时,需要花费一些时间才能停止。

1.2.5. backoffLimit

.spec.backoffLimit: 可选字段,将作业标记为失败状态之前的重试次数,默认值为6。

  backoffLimit: 5
  activeDeadlineSeconds: 100

如上文中示例配置中,其失败重试次数为5次,并且如果超出100s时间仍未运行完成,那么其将被终止。

默认情况下,除非 pod 失败(restartPolicy=Never)或容器错误退出(restartPolicy=OnFailure),否则 Job 将不会被中断运行。Job 中断将遵循上面介绍的 .spec.backoffLimit。一旦 .spec.backoffLimit 达到,Job 将被标记为失败,其他别的所有正运行的 pod 将被终止。

在某些情况下(配置中的逻辑错误等),你需要在重试一定次数后才使 Job 失败。为此需要设置 .spec.backoffLimit 配置值为判断 Job 失败前的重试次数。 与 Job 相关的 pod 在失败后,经过一个指数级退避延迟(10 秒,20 秒,40 秒,上限为 6 分钟),由 Job 控制器重新创建。 如果在 Job 的下一次状态检查之前未出现新的失败 pod,则会重置退避计数。

注意: 1.12 之前的 Kubernetes 版本仍然存在问题 #54870

注意: 如果你的 Job 外配置了 restartPolicy = "OnFailure",请记住,一旦 Job 达到退避限制,运行在该 Job 的容器将被终止。这会使得调试 Job 的可执行将变得更加困难。我们建议在调试 Job 或使用日志系统时设置 restartPolicy = "Never",以确保失败 Job 的输出不会在不经意间丢失。

1.2.6. ttlSecondsAfterFinished

.spec.ttlSecondsAfterFinished : 可选参数,清理已完成 Job(Completed 或 Failed)通过指定 Job 的 .spec.ttlSecondsAfterFinished 字段来清理已完成资源。

当 TTL 控制器清理 Job 时,它将级联删除 Job,即删除它的依赖对象,比如 pod 将和 Job 一起被删除。请注意,当 Job 被删除后,它的生命周期保证,如终结者,将被触发。

注意: 该功能支持版本: Kubernetes v1.12 alpha

例如:

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 控制器中关于已完成资源的处理。

1.2.7. template

spec.template: 是必选字段,声明 pod 的模板信息,格式同 Pod。

其定义规范与 Pod 完全相同,只是其中不再需要 apiVersion 或 kind 字段。

1.2.8. selector

.spec.selector: 可选字段,指定 pod 选择器

通常,在创建 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 来告诉系统你知道自己在做什么,并使其允许这种不匹配。

1.3. 示例

1.3.1. 创建一个job示例

  • 创建job服务
kubectl create -f ./job.yaml
kubectl apply  -f ./job.yaml

创建时可以使用 createapply,区别是 create 是创建,只能在集群中没有该对象时才能使用,如果集群中已经存在时会报错异常。而 apply 是声明式的执行应用,如果存在会覆盖,但不会报错。

  • 查看job列表
Kubectl get job pi
  • 查看job信息

使用 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-dtn4qName:             pi

1.3.2. 要查看 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

输出 logs 日志内容如下:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

job运行的pod完成状态

输出类似于:

NAME                        READY     STATUS      RESTARTS   AGE
pi-aiw0a                    0/1       Completed   0          4m

1.3.3. 删除所创建的 Job

集群会自动清理 Job 对应的 Pod

kubectl delete job pi
或
kubectl delete -f job.yaml

1.4. 扩展阅读

1.4.1. Job 和 Pod 上的标签

你创建了 Job 之后,Kubernetes 自动为 Job 的 Pod 添加 标签,以便能够将一个 Job 的 Pod 与另一个 Job 的 Pod 区分开来。

在本例中,每个 Job 及其 Pod 模板有一个标签: jobgroup=jobexample

Kubernetes 自身对标签名 jobgroup 没有什么要求。 为创建自同一模板的所有 Job 使用同一标签使得我们可以方便地同时操作组中的所有作业。 在第一个例子中,你使用模板来创建了若干 Job。 模板确保每个 Pod 都能够获得相同的标签,这样你可以用一条命令检查这些模板化 Job 所生成的全部 Pod。

说明: 标签键 jobgroup 没什么特殊的,也不是保留字。 你可以选择你自己的标签方案。 如果愿意,有一些建议的标签 可供使用。

1.4.2. 处理 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 必须容忍并发。

1.4.3. Job 终止和清理

当 Job 完成后,不会再创建 pod,但是 pod 也不会被删除。将这些 pod 保留,使你仍然可以查看已完成 pod 的日志来检查错误,警告或其他诊断输出。Job 对象在完成后也同样保留下来,以便你可以查看它的状态。由用户来决定在查看完状态后删除旧的 Job。使用 kubectl(例如 kubectl delete jobs/pikubectl 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: Failedreason: 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 基于指定的基于容量的清理策略来进行清除。

1.4.4. Job 并行模式

Job 对象可用于支持 pod 的可靠并行执行。Job 对象并非设计为支持紧密通信的并行过程(常见于科学计算领域)。它被用于支持一组独立但相关的工作项的并行处理。这些可能是要发送的电子邮件,要渲染的帧,要编码转换的文件,要扫描的 NoSQL 数据库中一段范围内的键等等。

在复杂的系统中,可能会有多个不同的工作项集。这里我们只考虑用户想要一起管理一组工作项 — 批处理任务

并行计算有几种不同的模式,每种都有各自的优缺点。权衡点如下:

  • 一个 Job 对象处理一个工作项,还是一个 Job 对象处理所有的工作项。后者适用于有大量工作项的场景。前者在管理大量 Job 对象时会对用户和系统带来一些开销。

  • 创建和工作项数量一样多的 pod,还是一个 pod 处理多个工作项。前者通常对现有代码和容器进行较少的修改。后者更适用于处理大量工作项(理由和前一条的相似)。

几种方法使用工作队列。这需要运行一个队列服务,并对现有的程序和容器进行修改以使其能够使用工作队列。其他方法更容易适应现有的容器化应用程序。

权衡总结如下表,第 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 任意

1.4.5. Bare Pods

所谓 Bare Pods 是指直接用 PodSpec 来创建的 Pod(即不在 ReplicaSets 或者 ReplicationController 的管理之下的 Pods)。这些 Pod 在 Node 重启后不会自动重启,但 Job 则会创建新的 Pod 继续任务。所以,推荐使用 Job 来替代 Bare Pods,即便是应用只需要一个 Pod。

1.4.6. 在真实负载中使用 Job

在真实的负载中,每个 Job 都会执行一些重要的计算,例如渲染电影的一帧, 或者处理数据库中的若干行。这时,$ITEM 参数将指定帧号或行范围。

在此任务中,你运行一个命令通过取回 Pod 的日志来收集其输出。 在真实应用场景中,Job 的每个 Pod 都会在结束之前将其输出写入到某持久性存储中。 你可以为每个 Job 指定 PersistentVolume 卷,或者使用其他外部存储服务。 例如,如果你在渲染视频帧,你可能会使用 HTTP 协议将渲染完的帧数据 用 'PUT' 请求发送到某 URL,每个帧使用不同的 URl。

1.4.7. 副本控制器

如果你有计划创建大量 Job 对象,你可能会发现:

即使使用标签,管理这么多 Job 对象也很麻烦。 如果你一次性创建很多 Job,很可能会给 Kubernetes 控制面带来很大压力。 一种替代方案是,Kubernetes API 可能对请求施加速率限制,通过 429 返回 状态值临时拒绝你的请求。 你可能会受到 Job 相关的资源配额 限制:如果你在一个批量请求中触发了太多的任务,API 服务器会永久性地拒绝你的某些请求。 还有一些其他作业模式 可供选择,这些模式都能用来处理大量任务而又不会创建过多的 Job 对象。

你也可以考虑编写自己的控制器 来自动管理 Job 对象。

Job 是副本控制器的补充。副本控制器管理预期不会终止的 pod(比如 web 服务器),而 Job 管理预期将会终止的 pod(比如批处理任务)。

正如在 Pod 生命周期中所讨论的,Job 适用于具有 RestartPolicy 值为 OnFailure 或 Never 的 pod。(注意:如果 RestartPolicy 未指定,默认值为 Always。)

1.4.8. 单 Job 启动控制 pod

单个 Job 的另一种模式是创建一个创建其他 pod 的 pod,充当这些 pod 的一种自定义控制器。这提供了更大的灵活性,但是入门起来可能有点复杂,并且与 Kubernetes 的集成较少。

这种模式的一个样例是:一个 Job 启动一个 pod 来运行一个脚本,该脚本依次启动 Spark 主控制器(参阅 spark 例子),并运行 spark 驱动,然后进行清理。

这种方法的优点是整个过程可以获取 Job 对象的完成保证,不是完全控制 pod 的创建以及如何将工作分配给 pod。

1.4.9. Cron Jobs

你可以使用 CronJob 来创建一个会在指定的时间/日期执行的 Job,类似于 Unix 工具 cron。

Copyright © 温玉 2021 | 浙ICP备2020032454号 all right reserved,powered by Gitbook该文件修订时间: 2022-01-09 10:28:35

results matching ""

    No results matching ""