Kubernetes之Job

Job是什么?

对于ReplicaSet、ReplicationController等类型的控制器而言,它希望pod保持预期数目、持久运行下去,除非用户明确删除,否则这些对象一直存在,它们针对的是耐久性任务,如web服务等。对于非耐久性任务,比如压缩文件,任务完成后,pod需要结束运行,不需要pod继续保持在系统中,这个时候就要用到Job。因此说Job是对ReplicaSet、ReplicationController等持久性控制器的补充。

Job与其它控制器的细微差别

Job定义方法与ReplicaSet等控制器相似,只有细微差别,如下:

  1. Job中的restart policy必需是"Never"或者"OnFailure",这个很好理解,因为pod要运行到结束,而不是反复重新启动。
  2. Job不需要选择器,其中的pod也不需要标签,系统在创建Job时会自动添加相关内容。当然用户也可以出于资源组织的目的添加标签,但这个与Job本身的实现没有关系。
  3. Job新增加两个字段:.spec.completions、.spec.parallelism。详细用法在示例中说明。
  4. backoffLimit字段:示例中说明

示例1:非并发Job

非并发Job的含义是,Job启动后,只运行一个pod,pod运行结束后整个Job也就立刻结束。

以下是简单的Job配置文件,只包含一个pod,输出圆周率小数点后2000位,运行时间大概为10s:

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

以上示例无需设置选择器、pod标签。无需设置.spec.completions、.spec.parallelism,这两个字段的默认值都是1。backoffLimit=4,表示允许pod失败的次数。将以上内容保存成文件并创建Job:

$ kubectl create -f https://k8s.io/examples/controllers/job.yaml
job "pi" created

确认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

从以上输出可以看到系统自动添加的Selector、Pod labels。注意Events的输出,全程只创建了一个pod。 

 列出Job的所有pod:

$ pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath={.items..metadata.name})
$ echo $pods
pi-aiw0a

查看pod输出:

$ kubectl logs $pods
3.14159265358979323846264......

以上是最简单的Job应用示例,输出圆周率小数点后2000位。但是,考虑另外一种情况,假如我们需要计算圆周率小数点后3000、4000、5000位怎么办?一种方案是先将上例中的2000改成3000,创建并运行。然后再改成4000,再创建并运行,一直到5000。显然这种方案并不高明:麻烦、资源利用率低。另外一种方法是同时创建4个Job,分别计算2000、3000、4000、5000。注意4个Job的name字段不能冲突分别是pi-2000、pi-3000、pi-4000、pi-5000,
文件名分别为pi-2000.yaml、pi-3000.yaml、pi-4000.yaml、pi-5000.yaml,并保存在目录/tmp/pi下。利用kubectl对目录的支持一次性创建4个Job:

$ kubectl create -f /tmp/pi
job "pi-2000" created
job "pi-3000" created
job "pi-4000" created
job "pi-5000" created

以上方法是伪并发,只适用于任务量少的情况。假如我们需要处理的任务是从pi-1到pi-10000,那么以上方法就不适用了:

  1. 需要同时创建太多的Job与pod,不好管理。 
  2. 资源配额限制。

示例2:粗并发Job

本例创建一个Job,但Job要创建多个pod。了解完示例后就明白为什么叫“粗并发”。

本示例需要一个消息队列服务的配合,不详细描述如何部署、填充消息队列服务。假设我们有一个RabbitMQ服务,集群内访问地址为:amqp://guest:guest@rabbitmq-service:5672。其有一个名为job1的队列,队列内有apple banana cherry date fig grape lemon melon共8个成员。

另外假设我们有一个名为gcr.io/<project>/job-wq-1的image,其功能是从队列中读取出一个元素并打印到标准输出,然后结束。注意,它只处理一个元素就结束了。接下来创建如下Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

上例中,completions的值为8,等于job1队列中元素的个数。因为每个成功的pod处理一个元素,所以需要成功8次,job1中的所有成员就会被处理完成。在粗并发模式下,completions的值必需指定,否则其默认值为1,整个Job只处理一个成员就结束了。

上例中,parallelism的值是2。虽然需要pod成功8次,但在同一时间,只允许有两个pod并发。一个成功结束后,再启动另一个。这个参数的主要目的是控制并发pod的个数,可根据实际情况调整。当然可以不指定,那么默认的并发个数就是1。

env中的内容告诉image如何访问队列。

将以上内容保存在job.yaml文件中,运行Job:

kubectl create -f ./job.yaml

 稍等片刻Job运行完成,查看结果:

$ kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2017 16:42:02 +0800
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      gcr.io/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

 查看Events,可以看到总共创建了8个pod。在本例中,每处理队列中的一个成员都需要创建一个pod,开销很大。如果队列中的成员个数非常庞大,那么这种处理方式就不适用。我们希望少创建pod、每个pod能处理多条记录,请看示例3。

示例3:细并发Job

并发处理都需要某种队列作为协调工具,本例换成redis,同样如何部署、填充redis队列不描述。只需要明确redis有一个job2队列,其内有如下9个成员:

redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

接下来创建image,详细过程不描述。只需确定这个image运行如下名为worker.py的python程序:

#!/usr/bin/env python

import time
import rediswq

host="redis"
# Uncomment next two lines if you do not have Kube-DNS working.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")

q = rediswq.RedisWQ(name="job2", host="redis")
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2) 
  if item is not None:
    itemstr = item.decode("utf=8")
    print("Working on " + itemstr)
    time.sleep(10) # Put your actual work here instead of sleep.
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

 首先连接到redis的job2队列。然后是一个while循环,每次读job2中的一条记录并输出,然后sleep 10s。循环退出的条件是job2队列为空。这个image与示例2不同,示例2只处理一条记录这就结束,而这个可以处理多条一直到队列为空。

接下来定义Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
      restartPolicy: OnFailure

上例中,无需像示例2一样指定 completions的值,因为结束条件是job2为空,已经内嵌在image的逻辑中。parallelism=2表示可以并发两个pod,不设置默认为1,在实际应用中可据实际情况自行调整。

运行Job:

kubectl create -f ./job.yaml

过一会查看Job运行状况:

$ kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2016 17:07:59 -0800
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              gcr.io/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

 虽然允许的最大并发数是2,但Events显示只创建成功一个pod,这个是正常情况,最大并非必需,可能系统因为资源问题达不到最大。

查看pod输出:

$ kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

 细并发相比与粗并发,减少了创建pod的开销,使每个pod能处理多条记录,但是pod要自己决定退出条件,如果不退出,那么Job永远无法结束。

关于资源回收

Job创建的pod在结束运行后,无论是成功还是失败,不会默认删除,仍然保留在系统中,这样用户才可以查看其日志、状态信息、排除错误。用户需要手动运行kubectl delete删除所有运行结束的pod,为了方便组织资源,一次性删除会部pod,可以被pod自定义标签。Job在运行完成后也仍然保留在系统中,由用户删除。所以使用Job,用户应注意资源回收,避免资源被耗尽。

参考:https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kubernetes Job Controller 是 Kubernetes 中的一种 Controller,用于管理 Job 资源,确保它们成功地完成任务。在 Kubernetes 中,Job 是一种用于运行任务的资源类型,通常用于批处理处理和定时任务。 Job Controller 的主要功能是监控 Job 资源的状态,并根据需要创建、更新、删除 Pod 资源,以确保 Job 能够成功地运行。具体来说,Job Controller 会创建一个或多个 Pod 来执行 Job 的任务,如果 Pod 运行成功,则 Job 将被视为已完成;如果 Pod 运行失败,则 Job 将被视为已失败;如果 Pod 没有运行成功或失败,则 Job 将被视为正在运行。 Job Controller 的源码实现位于 Kubernetes 代码库中的 `k8s.io/kubernetes/pkg/controller/job` 目录下。其中,Job Controller 的主要代码实现位于 `job_controller.go` 文件中。 Job Controller 的主要实现逻辑如下: 1. Job Controller 会使用 Kubernetes API 客户端来监视 Job 资源的变化,包括创建、更新和删除操作。 2. 当 Job 资源发生变化时,Job Controller 会根据 Job 的当前状态来决定如何处理它。如果 Job 还没有创建任何 Pod,则 Job Controller 将创建一个或多个 Pod 来执行 Job 的任务。如果 Job 已经创建了 Pod,则 Job Controller 将检查这些 Pod 的状态,并根据需要创建、更新或删除 Pod。 3. 当一个或多个 Pod 成功地完成 Job 的任务后,Job Controller 将删除这些 Pod。如果 Job 的任务失败,则 Job Controller 将根据需要重试任务,直到达到最大重试次数或任务成功为止。 4. 当 Job 被删除时,Job Controller 将删除与该 Job 相关的所有 Pod。 总之,Job Controller 是 Kubernetes 中非常重要的一种 Controller,它可以确保 Job 资源的正确执行,并帮助用户轻松地管理批处理处理和定时任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值