本篇概述
在前面两篇中,我已经介绍了 Replicas 和 Deployment,这两种资源类型都是用于控制 Worklload 的。当你使用的时候,可以感受到,这两种类型的资源一般都是持续运行的,同时还有一些辅助的方式帮助在 Workload 出现异常时恢复,以及根据情况进行动态伸缩的特性。
那么,如果我不需要保持我的 workload 持续运行可不可以,例如,在一个 Web 应用真正运行之前,我希望有个 container 能够帮我初始化一下 DB;又或者,我希望有个定时任务可以帮助我定时地刷新一下 DB 的状态,这些场景都不需要 Pod 保持持续地运行,虽然我们了解 Pod 的运行并不会占用太多资源。
事实上,Kubernetes 充分地考虑了这些场景,并且给出了解决方案,那就是 Job 和 CronJob。
Job
在 Kubernetes 的定义中,Job 其实和前面介绍的 Replicas 和 Deployment 原理类似,都是通过定义 Pod 的模板,然后根据 Job 的定义创建出对应的 Pod,然后关注 Pod 的状态,直到满足定义,例如 Pod 执行成功了或者说执行失败了,并且达到了重试次数之类的。这里来看一个简单的 Pod 的例子:
---
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
这个 Job 的功能很简单,就是计算一下 Pi 的值,完成之后就退出了。我们先将它添加到 Kubernetes 中,然后再看看状态:
[root@liqinag.io]# kubectl apply -f job00.yaml
[root@liqiang.io]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
pi 0/1 8m 8m
这个时候 Job 就在准备执行了,可以看到 COMPLETIONS 这一项,期望是 1,实际是 0,然后再看下 Pods 列表:
[root@liqinag.io]# kubectl get pods
k get pods
NAME READY STATUS RESTARTS AGE
pi-6rsxc 0/1 ContainerCreating 0 2m18s
可以发现确实有 Pod 在执行,并且还没有开始运行。我们等它执行完毕再看下效果:
[root@liqiang.io]# kubectl get pods
k get pods
NAME READY STATUS RESTARTS AGE
pi-6rsxc 0/1 Completed 0 4m47s
[root@liqiang.io]# kubectl get jobs
k get jobs
NAME COMPLETIONS DURATION AGE
pi 1/1 14m 17m
可以看到,这里的预期状态已经达成一致了。这是一个只执行一次的任务的 Job,它的操作方式就是创建一个 Pod,然后运行一遍(默认),然后就退出了。如果我们想要运行两遍呢?该怎么处理?其实,在 Job 的描述中加入一个配置项:
加上这一项之后,Kubernetes 就会创建分别创建两次 Pod,然后保证他们都执行成功之后才确认这个 Job 是完成的。
这里有一个注意的地方就是,我们在使用 Deployment 等 Workload 的时候,一般都会指定一个 restartPolicy,默认都是 RestartOnFail,那么在 Job 中,不能这么指定,因为这个逻辑应由 Job 来控制,而不应该让 Pod 来控制。
同时,Job 也不是说会无限期地执行下去,可以通过执行 backoffLimit
来限制失败重试的次数。
CronJob
Job 确实很有用,但是,很多时候我们更需要的是定时执行任务,例如,每小时统计一次商城的销售数据和趋势,类似这样周期性的任务,Kubernetes 在 Job 之上,又定义了 CronJob。之所以说是在 Job 之上,是因为 CronJob 就是在 Job 的基础上加上了周期定义的接口:
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-pi
spec:
schedule: "0,15,30,45 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: pi-job
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
从这份定义中可以看到,除了在外层套了一层 CronJob 的 Meta 数据之外,还有一个 schedule,里面的其实就是一个 Job 的定义了。这份 CronJob 的功能就是每隔 15 分钟算一次 Pi(毫无意义的事情)。然后我们将这份申明应用到 Kubernetes 中之后,每隔 15 分钟就会出现一个 Job。
这里需要说明的一点是,CronJob 目前还不稳定,有诸多问题,例如:
- 执行时间不是准点的,可能到点了还不会执行。因此,你可以通过设置
startingDeadlineSeconds
来设置截至时间,如果在截至时间后还没有执行,那么就会认为本次执行是失败的; - 创建 Job 的时候可能会创建 2 个 Job,这个就很尴尬了,所以,你得保证你的 Job 是幂等的。