系列目录html
job是什么
对于ReplicaSet、ReplicationController等类型的控制器而言,它但愿pod保持预期数目、持久运行下去,除非用户明确删除,不然这些对象一直存在,它们针对的是耐久性任务,如web服务等。对于非耐久性任务,好比压缩文件,任务完成后,pod须要结束运行,不须要pod继续保持在系统中,这个时候就要用到Job。所以说Job是对ReplicaSet、ReplicationController等持久性控制器的补充。python
Job与其它控制器的细微差异
Job定义方法与ReplicaSet等控制器类似,只有细微差异,以下:web
-
Job中的restart policy必需是"Never"或者"OnFailure",这个很好理解,由于pod要运行到结束,而不是反复从新启动。redis
-
Job不须要选择器,其中的pod也不须要标签,系统在建立Job时会自动添加相关内容。固然用户也能够出于资源组织的目的添加标签,但这个与Job自己的实现没有关系。json
-
Job新增长两个字段:.spec.completions、.spec.parallelism。详细用法在示例中说明api
-
backoffLimit字段:示例中说明bash
示例
非并发Job
非并发Job的含义是,Job启动后,只运行一个pod,pod运行结束后整个Job也就马上结束。session
如下是简单的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:app
$ 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,那么以上方法就不适用了:
-
须要同时建立太多的Job与pod,很差管理。
-
资源配额限制。
粗并发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能处理多条记录,请看下面示例
细并发Job
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,用户应注意资源回收,避免资源被耗尽。