这篇文章我们学习一下kubernete对Job的编排。不同于前面讲的Deployment、StatefulSet的编排,Job是一个执行一次就结束的pod,并不会滚动更新。
Job
定义job非常简单,只要在yaml文件中把kind字段定义成Job就可以了。如下kubejob.yaml:
apiVersion: batch/v1 kind: Job metadata: name: kubejob spec: parallelism: 2 completions: 4 template: spec: containers: - name: myjob image: zjj2006forever/kubejob:1.0 imagePullPolicy: IfNotPresent restartPolicy: Never backoffLimit: 4 activeDeadlineSeconds: 600
restartPolicy:重启策略,这儿我们定义是Never,就是如果任务失败后不会重启,而是会创建一个新pod出来,直到创建次数超过了backoffLimit。如果设置成OnFailure,则失败后不会重新创建pod而是会重启。
backoffLimit:默认是6,我们定义失败次数是4
activeDeadlineSeconds:控制pod重新创建时间,防止失败后无限制的重新创建,Job运行结束后就会进入Completed状态,如果Job一直没有执行成功,就会反复地重新创建,直到时间超过activeDeadlineSeconds的值,上面我设置了600s如果Job在600s后还没有完成,那就会结束。
parallelism:任务并行度,上面设置是2个,这样就会生成2个pod并行执行
completions:期望有多少个pod执行完成后整个任务结束,上面设置是4
注意:上面的Job中,需要创建pod的数量 = 期望总共完成的pod数量(completions参数) - Completed状态的pod数量(已完成) - running状态的pod数量(运行中)
下面我们写一段java代码,这段代码很简单,每隔12s一次依次输出0到9的数,我把它打成一个镜像,提交到我的dockerhub,名称:zjj2006forever/kubejob:1.0
public class JobTest { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++){ System.out.println(i); Thread.sleep(12000); } } }
上面的镜像打包好后,执行如下命令创建Job
[root@master kubejob]# kubectl create -f kubejob.yaml job.batch/kubejob created
这时我们看一下Job的描述
[root@master kubejob]# kubectl describe jobs/kubejob Name: kubejob Namespace: default Selector: controller-uid=190e811d-4d89-4d1d-9911-144499413c3a Labels: controller-uid=190e811d-4d89-4d1d-9911-144499413c3a job-name=kubejob Annotations: <none> Parallelism: 2 Completions: 4 Start Time: Thu, 30 Jul 2020 07:14:53 -0400 Active Deadline Seconds: 600s Pods Statuses: 2 Running / 0 Succeeded / 0 Failed Pod Template: Labels: controller-uid=190e811d-4d89-4d1d-9911-144499413c3a job-name=kubejob Containers: myjob: Image: zjj2006forever/kubejob:1.0 Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 105s job-controller Created pod: kubejob-vxgzg Normal SuccessfulCreate 105s job-controller Created pod: kubejob-tbgqj
上面的输出我要说明一下,我们看到有一个Selector,是给了controller一个uid,这个uid的作用是匹配job和它管理的pod的对应关系,也就是通过这个uid,Job才可以管理它创建的pod。
然后查看pod数量,2个pod都已经进入了running状态。
[root@master kubejob]# kubectl get pods NAME READY STATUS RESTARTS AGE kubejob-tbgqj 1/1 Running 0 5s kubejob-vxgzg 1/1 Running 0 5s
上面的输出我们看到当前有2个pod正在并行执行,4分钟以后,我们查看所有的pod,都已经完成
[root@master kubejob]# kubectl get pods NAME READY STATUS RESTARTS AGE kubejob-fnx6b 0/1 Completed 0 3m9s kubejob-tbgqj 0/1 Completed 0 5m13s kubejob-vxgzg 0/1 Completed 0 5m13s kubejob-w7zlb 0/1 Completed 0 3m9s
而我们这时再查看pod的状态,COMPLETIONS说明期望4个任务都已经完成
[root@master kubejob]# kubectl get Job NAME COMPLETIONS DURATION AGE kubejob 4/4 4m8s 19m
我们查看其中一个pod的日志,输出正确,如下图:
[root@master kubejob]# kubectl logs kubejob-fnx6b 0 1 2 3 4 5 6 7 8 9
CronJob
从名字中我们也可以看出,这是一个定时任务控制器,事实上,它也是用标准的Unix cron表达式来控制任务的执行。它的定义也非常简单,把kind定义成CronJob。我们先定义一个yaml文件cronjob.yaml如下:
apiVersion: batch/v1beta1 kind: CronJob metadata: name: mycronjob spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: mycronjob image: zjj2006forever/kubejob:1.0 imagePullPolicy: IfNotPresent restartPolicy: OnFailure concurrencyPolicy: Replace
从上面的yaml定义中我们看到有一个jobTemplate属性,他是一个job的模板,用来控制Job对象。所以,CronJob其实是一个Job对象的控制器。还记得之前文章《kubernete编排技术二:deployment》中用deployment来控制ReplicaSet对象吗,其实是一个道理。
CronJob对Job的控制,是通过参数schedule来进行的,这个参数的表达式就跟我们在linux下创建定时任务配置的cron时间格式一样。
这儿要注意,我上面定义的cron表达式是每分钟执行一次,但是上面我写的java应用是每2分钟才能执行,那是不是会有时间段内同时存在2个任务呢?
我们可以用这个字段来控制spec.concurrencyPolicy,它有3个属性值:默认是Allow,允许job同时存在;Forbid表示上一个任务没有执行完成这个任务不允许创建;Replace表示新产生的任务pod会替换掉旧的。
接下来我们创建这个CronJob,执行下面命令:
kubectl create -f cronjob.yaml
查看创建的任务:
[root@master kubejob]# kubectl get CronJob NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE mycronjob */1 * * * * False 1 21s 4m14s
再看一下pod和日志打印:
[root@master kubejob]# kubectl get pods NAME READY STATUS RESTARTS AGE mycronjob-1596280800-9xnpm 1/1 Running 0 52s [root@master kubejob]# kubectl logs mycronjob-1596280800-9xnpm 0 1 2 3 4
这时我们看到其中一个pod打印出5个数之后(用了一分钟),就消失了,出现了新的Pod,这也印证了我们yaml文件中的spec.concurrencyPolicy=Replace
[root@master kubejob]# kubectl logs mycronjob-1596280800-9xnpm Error from server (NotFound): pods "mycronjob-1596280800-9xnpm" not found
下面的输出我们还看到了pod被Replace的过程
[root@master kubejob]# kubectl get pods NAME READY STATUS RESTARTS AGE mycronjob-1596280920-2vlhr 1/1 Terminating 0 60s mycronjob-1596280980-gvqqd 0/1 ContainerCreating 0 0s [root@master kubejob]# kubectl get pods NAME READY STATUS RESTARTS AGE mycronjob-1596280980-gvqqd 1/1 Running 0 14s
最后说明一下,如果一个Job创建失败,这个Job就标记为miss,一旦指定时间内miss次数达到100,CronJob会停止再创建这个Job。这个时间可以由spec.startingDeadlineSeconds参数指定,单位是s。这里我就不再实验了。
总结
相对于之前讲的编排技术,Job和CronJob是相对比较简单一种的编排技术,但是也非常重要,包括怎么控制并发任务(parallelism),怎么控制完成数量(completions),还有定时任务配置(schedule)。这些对kubernete中的应用起了很好的支持。