ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage
Edit page

2.6 K8s控制器资源对象之job& Cronjob

Job

仅仅执行一次,而且保证能够正常退出。

job也是一个Pod控制器。

apiVersion: batch/v1
kind: Job
metadata:
 labels:
   job-name: job-app
 name: job-app
 namespace: default
spec:
 selector:
   matchLabels:
     job-name: job-app
 template:
   metadata:
     labels:
       job-name: job-app
   spec:
     containers:
     - command: ['date']
       image: busybox
       imagePullPolicy: Always
       name: job-app

Cronjob

Cronjob创建

cronjob会周期地创建job来执行,通过spec.jobTemplate指定job模板。

手动创建cronjob:

kubectl run mysql-backup --schedule="0/5 * * * ?" --image=58.56.88.22:5000/mariadb:10.1 --restart=OnFailure -- /bin/sh -c "mysqldump mydatabase > /mysql-backup/mysql-`date +"%Y%m%d"`.sql"

资源配置清单:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    run: mysql-backup
  name: mysql-backup
spec:
  schedule: 0/5 * * * *  # k8s cron默认使用时区UTC,如果想CHINA的9:00AM执行,需要将cron改成01:00。在Minutes设置的是5/20,则表示第一次触发是在第5min时,接下来每20min触发一次,即,第25min, 45min等时刻触发
  suspend: false  #如果其值为True表示此CronJob暂时失效,不变成False之前不再运行job。对于已经创建的job没有影响。
  concurrencyPolicy: Allow  #并发策略:Allow运行并发运行job(默认值);Forbid:禁止并发运行,如果前一个job没有执行完,跳过不执行;Replace:取消当前运行的job,用一个新的job替换。
  startingDeadlineSeconds: 600 # 强烈建议设置错过调度的计算时间。这里表示:最后600秒内错过了的调度次数
  successfulJobsHistoryLimit: 3  #保留多少次成功执行的job历史
  failedJobsHistoryLimit: 1  # 强烈建议设置失败任务数,用于排查任务失败根因,以优化任务
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            run: mysql-backup
        spec:
          containers:
          - name: mysql-backup
            args:          
            - /bin/sh
            - -c
            - mysqldump mydatabase > /mysql-backup/mysql-20190617.sql
            image: 58.56.88.22:5000/mariadb:10.1
            imagePullPolicy: IfNotPresent
            resources: {}
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
          dnsPolicy: ClusterFirst
          restartPolicy: OnFailure  #只支持Never和OnFailure。Pod默认值为Always,所以必须指定。
          schedulerName: default-scheduler
          securityContext: {}
          terminationGracePeriodSeconds: 30

CronJob简单介绍

一个CronJob对象就像是一个Linux环境的crontab文件一样,它会在给定的调度周期(crontab格式)内定期的创建一些job.

注意:所有的定时任务的调度周期都依赖于k8s的master节点的时区

通常情况下,CronJob对于创建定期和重复的任务非常有用,比如定期的备份和邮件发送之类的任务场景。

当然了,在Kubernetes集群中,Cronjob也有一些局限性和特性,需要详细了解下才能用的比较好。

注意: Cronjob控制器当前官方仍然是beta版本,也就意味着还是有一些问题存在的。

Cronjob的局限性

一个Cronjob会在它每执行一次调度就大概会创建一个Jobs对象。大概是因为有时候可能会有两个job被创建,或者没有任务创建。官方实现中尝试去解决这种问题,但是目前仍然无法避免。因此在设计过程中,所有的Job都应该是幂等性的(idempotent)

如果startingDeadlineSeconds参数被设置为一个比较大的值,或者没有设置(默认),并且concurrencyPolicy设置为Allow,那么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.

值得关注的是,如果设置了startingDeadlineSeconds参数(不为空),控制器统计错过的调度次数将不再是从最后一次调度时间,而是从startingDeadlineSeconds的值到现在进行统计。比如,如果设置startingDeadlineSeconds: 200,控制器会统计在最后200秒内错过了的调度次数。

如果CronJob未能在预定时间创建,则该任务将被视为错过调度。比如,当设置concurrencyPolicy: Forbid时,当前一个任务还在运行时CronJob尝试再次被调度,此时会被forbid掉,因此也会被记录为错过一次调度。

再比如,我们假设一个定时任务被设置在08:30:00后每一分钟执行一次,并且startingDeadlineSeconds参数没有被设置。如果CronJob控制器在08:29:00到10:21:00之间故障了,Job将不会运行,因此错过调度的任务数量将远超过100。

为了更深层次说明这个问题,假设一个定时任务被设置在08:30:00开始每一分钟执行一次,并且startingDeadlineSeconds:200。如果CronJob控制器依然在相同时间段故障了,Job将会在10:22:00开始继续执行。 因为控制器仅会计算在过去的200秒内,错过调度的次数有多少,因此仅会错过调度3次,远远小于100次,所有定时任务会在控制器恢复后继续调度,而不会影响正常的任务。

另外需要注意的是,CronJob仅负责调度和创建匹配的Jobs,而由Jobs真正去管理真正执行任务的Pods。

Cronjob的参数详情

  • spec.startingDeadlineSeconds: 表示统计错过调度次数(100次)的开始时间,默认从最后一次调度时间开始统计错过调度次数(超 过100不再调度)

  • spec.concurrencyPolicy: 并发调度策略,可选值:{“Allow”:“允许并发”,“Forbid”:“不允许”,“Replace”:“调度覆盖”}.

  • Allow: 注意:当设置为Allow时,需要考虑到任务执行时间和调度周期,因为可能上个任务没执行成功,下个任务就到执行时间了,如此下来可能会有很多任务都执行积压,造成资源误使用;

  • Replace: 当使用Replace遇到上述情况,后个任务会将前一个任务替换掉,如此以来所有的任务可能都不会完整执行;

  • Forbid: 则不允许并发调度,也即就调度一次,下一次调度周期再调度,但是可能由于任务执行过长,导致大部分的任务在每一 次调度时间都完美的错过了,此时startingDeadlineSeconds参数也并没有设置,就可能会出现该任务不会再调度,对应到k8s里的事 件可能是Cannot determine if job needs to be started: too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew

  • spec.schedule: 调度周期,格式为标准的crontab格式[分 时 日 月 周]

  • spec.failedJobsHistoryLimit: 历史失败的任务数限制(通常可以保留1-2个,用于查看失败详情,以调整调度策略)

  • spec.successfulJobsHistoryLimit: 历史成功的任务数限制(可以自己决定保留多少个成功任务)

  • spec.jobTemplate: 标准的pod运行的模板(容器运行时的相关参数)

  • spec.suspend: 可选参数,如果设置为true,所有后续的任务都会被暂停执行,该参数不适用于已经运行的任务,默认为False

# 配置了一个定期去阿里云云解析获取解析详情的数据
$ cat dnsall-cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    run: dnsall
  name: dnsall
  namespace: myapp
spec:
  # 强烈建议设置并发策略,根据调度周期和任务特性进行设置
  concurrencyPolicy: Forbid
  # 强烈建议设置失败任务数,用于排查任务失败根因,以优化任务
  failedJobsHistoryLimit: 1
  successfulJobsHistoryLimit: 3
  # 强烈建议设置错过调度的计算时间
  startingDeadlineSeconds: 600
  # 调度周期
  schedule: '05,15,25,35,45,55 */1 * * *'
  suspend: false
  jobTemplate:
    metadata:
    spec:
      template:
        metadata:
          labels:
            run: dnsall
        spec:
          imagePullSecrets:
          - name: mydocker
          containers:
          - args:
            - -cmdbtype
            - dns
            image: harbor.bgbiao.top/cron-job:2019-12-04
            imagePullPolicy: Always
            name: dnsall
            resources: {}
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
          dnsPolicy: ClusterFirst
          # 强烈建议设置任务的重启策略(任务的失败会触及到Jobs控制器中的Backofflimit参数,导致job失败)
          restartPolicy: OnFailure
          schedulerName: default-scheduler
          securityContext: {}
          terminationGracePeriodSeconds: 30

$ kubectl  get cronjob -n myapp
NAME             SCHEDULE                      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
dnsall           05,15,25,35,45,55 */1 * * *   False     0        8m41s           23h

# cronjob其实定期的创建了job,因此具体的任务pod其实是由job控制器来维护的
# 这里可以看到,我们上面的cronjob保存的三个执行成功的任务
$ kubectl  get jobs -n myapp  | grep dns
dnsall-1577597100           1/1           23s        22m
dnsall-1577597700           1/1           24s        12m
dnsall-1577598300           1/1           24s        2m22s

# 再查看一个job真正管理的pod任务的执行
# 任务已经已完成,所以任务的期望值为1,当前值为0
$ kubectl  get pods -n myapp | grep dnsall-1577598300
dnsall-1577598300-hdl4z           0/1     Completed   0          3m29s

如何让k8s使用的宿主机的时区?

如果k8s组件是pod形式存在,默认使用的是UTC时区,如果要使用中国时区,需要修改/etc/kubernetes/manifests下几个组件的yaml文件,配置这几个k8s组件的pod时区,添加:

    volumeMounts:
    - name: host-time
      mountPath: /etc/localtime
      readOnly: true
    
  volumes:
  - name: host-time
    hostPath:
      path: /etc/localtime

或:

    env:
    - name: TZ
      value: "Asia/Shanghai"