- CronJob用于实现定时任务,像Linux的Crontab一样。
- 定时任务
- 应用场景:通知,备份
官档,https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/
1. 什么是 CronJob
CronJob 控制器用于管理 Job 控制器资源的运行时间,等到设定的时间后就会创建 Job 资源,而 Job 资源被创建后便会立即会创建一个新的 Pod 去执行指定的任务。这里 CronJob 可以说类似于 Linux 操作系统的周期性任务作业计划(Crontab)
,其功能如下:
- 在未来某时间点运行作业一次
- 在指定的时间点重复运行作
CronJob 对象支持使用的时间格式类似于 Crontab
,略有不同的是 CronJob 控制器在指 定的时间点时,符号"?"
和"*"
的意义相同,都表示任何可用的有效值。
在日常中使用 CronJob 常常用作于数据备份、数仓导数、执行任务、邮件发送、数据拉取、数据推送等操作。
2. CronJob 资源示例
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 1
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 1
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
常用的配置项如下表所示:
参数名称 | 默认值 | 描述 |
---|---|---|
suspend | false | 是否挂起后续要执行的任务。 |
schedule | - | 定时任务执行规则(使用 Cron 表达式编写表达式)。 |
failedJobsHistoryLimit | 1 | 保留失败任务(执行任务的 job 与 pod)的数量。 |
successfulJobsHistoryLimit | 3 | 保留成功任务(执行任务的 job 与 pod)的数量。 |
concurrencyPolicy | Allow | 并发执行策略: - Allow: 允许并发任务执行。 - Forbid: 不允许并发任务执行。(如果新任务的执行时间到了而老任务没有执行完,CronJob 会忽略新任务的执行。) - Replace: 如果新任务的执行时间到了而老任务没有执行完,用新任务替换当前正在运行的任务。 |
startingDeadlineSeconds | - | 设置任务在多少时间内错误次数达到100后就不再调度。(单位s) |
3. CronJob 的时区
CronJob 的 schedule
时间都是基于 kube-controller-manager
的时区。如果你的控制平面在 Pod 或是"裸容器"中运行了 kube-controller-manager
,那么为该容器所设置的时区将会决定 CronJob 的控制器
所使用的时区。所以,我们在使用 CronJob 之前一点要确保 kube-controller-manager
组件中的时区是正确的。
如果我们的时区不对,且 kube-controller-manager
是通过容器化部署的,那么我们可以进入 Master 节点所在服务器,修改里面的 kube-controller-manager.yaml 文件:
$ vi /etc/kubernetes/manifests/kube-controller-manager.yaml
$ vi /etc/kubernetes/manifests/kube-controller-manager.yaml
......
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /usr/libexec/kubernetes/kubelet-plugins/volume/exec
name: flexvolume-dir
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /etc/kubernetes/controller-manager.conf
name: kubeconfig
readOnly: true
- name: localtime ## --- volumeMounts 中挂载时区 ---
mountPath: /etc/localtime
readOnly: true
hostNetwork: true
priorityClassName: system-node-critical
volumes:
- name: localtime ## --- volumes 中挂载时区 ---
hostPath:
path: /etc/localtime
......
......
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /usr/libexec/kubernetes/kubelet-plugins/volume/exec
name: flexvolume-dir
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /etc/kubernetes/controller-manager.conf
name: kubeconfig
readOnly: true
- name: localtime ## --- volumeMounts 中挂载时区 ---
mountPath: /etc/localtime
readOnly: true
hostNetwork: true
priorityClassName: system-node-critical
volumes:
- name: localtime ## --- volumes 中挂载时区 ---
hostPath:
path: /etc/localtime
......
4. Cron 表达式语法
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6) (周日到周一;在某些系统上,7 也是星期日)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
* * * * *
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6) (周日到周一;在某些系统上,7 也是星期日)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
* * * * *
输入 | 描述 | 相当于 |
---|---|---|
@yearly | (or @annually) 每年 1 月 1 日的午夜运行一次 | 0 0 1 1 * |
@monthly | 每月第一天的午夜运行一次 | 0 0 1 * * |
@weekly | 每周的周日午夜运行一次 | 0 0 * * 0 |
@daily | (or @midnight) 每天午夜运行一次 | 0 0 * * * |
@hourly | 每小时的开始一次 | 0 * * * * |
更详细的信息,可以查看 维基百科 Cron 介绍。还可以使用 rontab.guru之类的 Web 工具进行 Cron 参数配置。
5. CronJob 限制
CronJob 执行时,可能会重复创建或不创建任务
CronJob 会根据配置的 Schedule 参数进行执行,每次到达指定的时间后应该会创建一个 Job 来执行任务,之所以说是"应该",这是因为有些情况可能会创建两个 Job 或者不创建 Job,所以,我们要执行的任务最好是幂等的,以防止重复执行任务这种情况。
配置 concurrencyPolicy 来保证至少运行一次
如果在创建 CronJob 时指定了 startingDeadlineSeconds 参数(默认不会配置该参数),并且 concurrencyPolicy 设置为 Allow,则 Cronjob 的 Job 能够至少运行一次。
CronJob 达到一定错误次数后,将会终止执行
CronJob 控制器会检查从上一次调度的时间点到现在所错过了调度次数。如果在周期内的调度次数超过 100 次, 那么它就不会启动这个任务,并记录这个错误:
Cannot determine if job needs to be started. Too many missed start time (> 100).
Set or decrease .spec.startingDeadlineSeconds or check clock skew.
Cannot determine if job needs to be started. Too many missed start time (> 100).
Set or decrease .spec.startingDeadlineSeconds or check clock skew.
bash
需要注意的是,如果 CronJob 配置了 startingDeadlineSeconds 参数,则控制器会统计从 startingDeadlineSeconds 设置的值到现在,而不是从上一个计划时间到现在错过了多少次 Job。例如,如果 startingDeadlineSeconds 是 200,则控制器会统计在过去 200 秒中错过了多少次 Job。
如果未能在调度时间内创建 CronJob,则计为错过。例如,如果 concurrencyPolicy 被设置为 Forbid,并且当前有一个调度仍在运行的情况下, 试图调度的 CronJob 将被计算为错过。
例如,一个 CronJob 被设置为从 08:30:00 开始每隔一分钟创建一个新的 Job,并且 CronJob 未配置 startingDeadlineSeconds 参数。如果 CronJob 控制器从 08:29:00 到 10:21:00 终止运行,则该 Job 将不会启动,因为其错过的调度次数超过了 100 次。
为了进一步阐述这个概念,假设将 CronJob 设置为从 08:30:00 开始每隔一分钟创建一个新的 Job, 并将其 startingDeadlineSeconds 字段设置为 200 秒。 如果 CronJob 控制器恰好在与上一个示例相同的时间段(08:29:00 到 10:21:00)终止运行, 则 Job 仍将从 10:22:00 开始。 造成这种情况的原因是控制器现在检查在最近 200 秒(即 3 个错过的调度)中发生了多少次错过的 Job 调度,而不是从现在为止的最后一个调度时间开始。
CronJob 管理 Job,而 Job 管理执行任务的 Pod
CronJob 仅负责创建与其调度时间相匹配的 Job,而 Job 又负责管理其代表的 Pod
6. CronJob控制机制
CronJob 控制器是一个更高级别的资源,它以 Job 控制器资源为其管控对象,并借助它管理 Pod 资源的对象。
如果作业重复执行时指定的时间点较近,而作业执行时长跨过了其两次执行的时间长度,则会出现两个 Job 对象同时存在的情形。
有些 Job 对象可能会存在无法或不能同时运行情形,这个时候就要通过 cronjob.spec.concruuencyPolicy 属性控制作业并存的机制,其默认值为
"Allow",即允许前后 Job,甚至属于同一个 CronJob 的更多 Job 同时运行。其他两个可用值中,
"Forbid" 用于禁止前后两个 Job 同时运行,如果前一个尚未结束,后一个则不予启动(跳过)。
"Replcae" 用于让后一个 Job 取代前一个,即终止前一个并启动后一个。
7. 案例
使用 CronJob 经常做的就是进行数据备份,如下就是一个数据备份的 CronJob 配置示例:
- 系统环境:
- 存储 NFS1 地址:192.168.2.11
- 存储 NFS2 地址:192.168.2.12
- 备份要求:
- 时间为每天凌晨四点
- 备份 /data/master/ 的数据。
- 对备份的数据只保留三天。
apiVersion: batch/v1
kind: CronJob
metadata:
name: data-backup
namespace: devops
labels:
app: data-backup
spec:
schedule: 0 4 * * *
concurrencyPolicy: Allow
suspend: false
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
volumes:
- name: data
nfs:
server: 192.168.2.11 #挂入 NFS 存储1
path: /data/master/
- name: data-backup
nfs:
server: 192.168.2.12 #挂入 NFS 存储2
path: /data/backup/
- name: time-zone #挂入宿主机的时区文件
hostPath:
path: /etc/localtime
containers:
- name: data-backup
image: 'busybox:1.32.0' #使用 busybox 镜像执行备份操作
command: #配置要执行的备份命令
- sh
- '-c'
args:
- |
tar -zcvf /data-backup/xxx-$(date +%Y%m%d).tar.gz /data
find /data-backup/xxx-* -mtime +2 -delete
volumeMounts:
- name: data #应用运行的数据的存储的 NFS 服务器地址
mountPath: /data
- name: data-backup #存储备份数据的 NFS 服务器地址
mountPath: /data-backup
- name: time-zone #挂载系统时区,防止时区产生的影响
mountPath: /etc/localtime
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Never
apiVersion: batch/v1
kind: CronJob
metadata:
name: data-backup
namespace: devops
labels:
app: data-backup
spec:
schedule: 0 4 * * *
concurrencyPolicy: Allow
suspend: false
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
volumes:
- name: data
nfs:
server: 192.168.2.11 #挂入 NFS 存储1
path: /data/master/
- name: data-backup
nfs:
server: 192.168.2.12 #挂入 NFS 存储2
path: /data/backup/
- name: time-zone #挂入宿主机的时区文件
hostPath:
path: /etc/localtime
containers:
- name: data-backup
image: 'busybox:1.32.0' #使用 busybox 镜像执行备份操作
command: #配置要执行的备份命令
- sh
- '-c'
args:
- |
tar -zcvf /data-backup/xxx-$(date +%Y%m%d).tar.gz /data
find /data-backup/xxx-* -mtime +2 -delete
volumeMounts:
- name: data #应用运行的数据的存储的 NFS 服务器地址
mountPath: /data
- name: data-backup #存储备份数据的 NFS 服务器地址
mountPath: /data-backup
- name: time-zone #挂载系统时区,防止时区产生的影响
mountPath: /etc/localtime
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Never