Kubernetes 使用 CronJob 进行定时任务


  !版权声明:本博客内容均均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。

目录[-]


系统环境:

  • Kubernetes 版本:1.20.1

参考地址:

示例地址:

注:介绍部分大部分都来自官方文档,这里根据官方文档进行简单介绍 CronJob,最后给出一个使用其的示例,如果感觉该文章对你有帮助,别忘了 Github 点颗星哦~

一、什么是 CronJob

       CronJob 控制器用于管理 Job 控制器资源的运行时间,等到设定的时间后就会创建 Job 资源,而 Job 资源被创建后便会立即会创建一个新的 Pod 去执行指定的任务。这里 CronJob 可以说类似于 Linux 操作系统的周期性任务作业计划(Crontab),其功能如下:

  • 在未来某时间点运行作业一次
  • 在指定的时间点重复运行作

CronJob 对象支持使用的时间格式类似于 Crontab ,略有不同的是 CronJob 控制器在指 定的时间点时,符号"?""*"的意义相同,都表示任何可用的有效值。

在日常中使用 CronJob 常常用作于数据备份、数仓导数、执行任务、邮件发送、数据拉取、数据推送等操作。

二、CronJob 资源示例

正常的 CronJob 资源一般会包含下面字段内容:

apiVersion: batch/v1beta1
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)

三、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

然后将宿主机的时区文件挂载进容器:

......
    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
......

然后等待一段时间,可以观察到 kube-controller-manager 容器会自动重新,这时候该容器会按新挂载的时区文件进行配置。

四、Cron 表达式语法

# ┌───────────── 分钟 (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 * * * *

例如,下面这行指出必须在每个星期五的午夜以及每个月 13 号的午夜开始任务:

0 0 13 * 5

更详细的信息,可以查看 维基百科 Cron 介绍。还可以使用 rontab.guru之类的 Web 工具进行 Cron 参数配置。

五、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.

需要注意的是,如果 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

六、使用 CronJob 备份数据示例

使用 CronJob 经常做的就是进行数据备份,如下就是一个数据备份的 CronJob 配置示例:

  • 系统环境:
    • 存储 NFS1 地址:192.168.2.11
    • 存储 NFS2 地址:192.168.2.12
  • 备份要求:
    • 时间为每天凌晨四点
    • 备份 /data/master/ 的数据。
    • 对备份的数据只保留三天。
kind: CronJob
apiVersion: batch/v1beta1
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

命令说明:

## 将目录 /data 压缩并归档,命名为 xxx-$(date +%Y%m%d).tar.gz,并且存储到 /data-backup/ 目录中
$ tar -zcvf /data-backup/xxx-$(date +%Y%m%d).tar.gz /data

## 删除将目录 /data-backup/ 目录的归档文件,只保留时间为三天内的文件
$ find /data-backup/jenkins-* -mtime +2 -delete

这里就简单的演示了下如何使用 CronJob 进行数据备份,其作用还有很多。例如使用 Etcd 的镜像对 Etcd 数据进行快照等,需要大家自己多多使用进行摸索~

—END—