在日常的工作中经常会遇到一些需要进行批量数据处理和分析的需求,当然也会有按时间来进行调度的工作,在k8s中提供了JobCronJob两种资源对象来应对这种需求。

Job负责处理任务,即仅执行一次的任务,他保证批处理任务的一个或多个Pod成功结束。而CronJob则就是在Job上加上了时间调度。

Job

定义一个Job来执行一个倒计时的任务,对应的资源清单如下:(job-demo.yaml)

  1. apiVersion: batch/v1
  2. kind: Job
  3. metadata:
  4. name: job-demo
  5. spec:
  6. template:
  7. spec:
  8. restartPolicy: Never
  9. containers:
  10. - name: counter
  11. image: busybox
  12. command:
  13. - "/bin/sh"
  14. - "-c"
  15. - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"

Job中也是一个Pod模板,和之前的Deployment、StatefulSet之类是一致的,只是Pod中的容器要求是一个任务,而不是常驻前台的进程了,因为需要退出,另外值得注意的是Job的RestartPolicy仅支持NeverOnFailure两种。

直接创建Job对象:

$ kubectl apply -f job-demo.yaml
job.batch/job-demo created
$ kubectl get job 
NAME       COMPLETIONS   DURATION   AGE
job-demo   1/1           26s        3m4s
$ kubectl get pods         
NAME                      READY   STATUS      RESTARTS   AGE
job-demo-frs2r            0/1     Completed   0          2m41s

Job对象创建完成后,查看下对象的详细描述信息:

$ kubectl describe job job-demo
Name:           job-demo
Namespace:      default
Selector:       controller-uid=b1191631-c71c-45e7-86c9-ebca69cb6125
Labels:         controller-uid=b1191631-c71c-45e7-86c9-ebca69cb6125
                job-name=job-demo
.......
Pod Template:
  Labels:  controller-uid=b1191631-c71c-45e7-86c9-ebca69cb6125
           job-name=job-demo
  Containers:
   ......
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  49s   job-controller  Created pod: job-demo-z48h2

Job对象创建后,它的Pod模板,被自动加上了一个controller-uid=< 一个随机字符串 >这样的Label标签,而这个Job对象本身,则被自动了这个Label对应的selector,从而保证了Job与它所管理的Pod之间的匹配关系。而Job控制器之所以要使用这种携带了UID的Label,就是为了避免不同Job对象所管理的Pod发生重合。

Pod 变成了 Completed 状态,这是因为容器的任务执行完成正常退出了,查看对应的日志:

$ kubectl logs job-demo-frs2r
9
8
7
6
5
4
3
2
1

如果任务执行失败了,定义了RestartPolicy=OnFailure,那么任务在执行失败后Job控制器就会不断尝试创建一个新Pod,当然肯定不能无限进行下去。通过Job对象的spec.backoffLimit字段来定义重试次数,另外需要注意的是Job控制器重新创建Pod的间隔是呈指数增加的,及下一次重新创建Pod的动作会分别发生在10s,20s,40s…..后。如果定义的RestartPolicy=Never,那么任务执行失败后,Job控制器就不会去尝试创建新的Pod了,它会不断的尝试重启Pod里面的容器。

Job 任务对应的 Pod 在运行结束后,会变成 Completed 状态,但是如果执行任务的 Pod 因为某种原因一直没有结束怎么办呢?同样我们可以在 Job 对象中通过设置字段spec.activeDeadlineSeconds来限制任务运行的最长时间,比如:

spec:
 activeDeadlineSeconds: 100

该参数表明任务Pod运行超过100s后,这个job的所有pod都会被终止,并且Pod的终止原因会变成DeadlineExceeded

除此之外,还可以通过设置spec.parallelism参数来进行并行控制,该参数定义了一个Job在任意时间最多可以有多少个Pod同时运行。spec.completions参数可以定义Job至少要完成的Pod数目。

CronJob

CronJob 其实就是在 Job 的基础上加上了时间调度,我们可以在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。

一个CronJob对象其实就对应crontab文件中的一行,他根据配置的时间格式周期性的运行一个Job。
crontab 的格式为:分 时 日 月 星期 要运行的命令 。

  • 第1列分钟0~59
  • 第2列小时0~23)
  • 第3列日1~31
  • 第4列月1~12
  • 第5列星期0~7(0和7表示星期天)
  • 第6列要运行的命令

用 CronJob 来管理上面的 Job 任务,定义如下所示的资源清单:(cronjob-demo.yaml)

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-demo
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: hello
            image: busybox
            args:
            - "bin/sh"
            - "-c"
            - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"

这里的 Kind 变成了 CronJob 了,要注意的是.spec.schedule字段是必须填写的,用来指定任务运行的周期,格式就和 crontab 一样,另外一个字段是.spec.jobTemplate, 用来指定需要运行的任务,格式当然和 Job 是一致的。还有一些值得关注的字段:

.spec.successfulJobHistroyLimit(默认为3)和.spec.failedJobHistory(默认为1),表示历史限制,是可选字段,指定可以保留多少完成和失败的Job。然而,当运行一个CronJob时,Job可以很快就堆积很多,所以一般推荐设置这两个字段的值。如果设置限制的值为0,那么相关类型的Job完成后将不会保留。

创建上面的资源对象:

$ kubectl create -f cronjob-demo.yaml
cronjob "cronjob-demo" created

对应的CronJob资源对象:

$ kubectl get cronjob
NAME           SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob-demo   */1 * * * *   False     0        <none>          7s

稍微等一会儿查看可以发现多了几个 Job 资源对象,这个就是因为上面设置的 CronJob 资源对象,每1分钟执行一个新的 Job:

$ kubectl get job 
NAME                      COMPLETIONS   DURATION   AGE
cronjob-demo-1574147280   1/1           6s         2m45s
cronjob-demo-1574147340   1/1           11s        105s
cronjob-demo-1574147400   1/1           5s         45s
$ kubectl get pods
NAME                      READY   STATUS      RESTARTS   AGE
cronjob-demo-1574147340-ksd5x   0/1     Completed           0          3m7s
cronjob-demo-1574147400-pts94   0/1     Completed           0          2m7s
cronjob-demo-1574147460-t5hcd   0/1     Completed           0          67s
cronjob-demo-1574147520-vmjfr   0/1     ContainerCreating   0          7s

这个就是 CronJob 的基本用法,一旦不再需要 CronJob,我们可以使用 kubectl 命令删除它:

$ kubectl delete cronjob cronjob-demo
cronjob "cronjob-demo" deleted

不过需要注意的是这将会终止正在创建的 Job,但是运行中的 Job 将不会被终止,不会删除 Job 或 它们的 Pod。