1. 程式人生 > 實用技巧 >Job和CronJob

Job和CronJob

Kubernetes的主要任務是保證Pod中的應用長久穩定的執行,但是我們有時候也需要一些只需要執行一次,執行完就退出了的"短時"任務,這時候使用Deployment等這類控制器就無法滿足我們的需求,Kubernetes就誕生了Job Controller,專門用來處理這類需求。

1、Job

1.1、基本操作

Job負責處理僅執行一次的任務,它保證批處理的任務的一個或多個成功結束,我們可以通過kubectl explain job來檢視具體語法,如下:

[root@master ~]# kubectl explain job
KIND:     Job
VERSION:  batch/v1

DESCRIPTION:
     Job represents the configuration of a single job.

FIELDS:
   apiVersion   
<string> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources
kind <string> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds
metadata <Object> Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata spec <Object> Specification of the desired behavior of a job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status status <Object> Current status of a job. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

從上面可以看到跟我們定義Deployment,Pod的語法是差不多的,下面我們定義一個Job的YAML檔案:

job-demo.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
      - name: test-job
        image: busybox
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        args:
        - "for i in $(seq 10); do echo $i; done"
      restartPolicy: Never
  backoffLimit: 4

從上面的YAML檔案我們可以看到Job的spec裡就是我們熟悉的Pod模板。我們執行kubectl apply -f job-demo.yaml,檢視執行資訊:



[root@master job]# kubectl get job
NAME       COMPLETIONS   DURATION   AGE
job-demo   1/1           5s         7s

從上面可以看到在Pod裡自動生成了一個controller-uid的label,然後在Job的selector也會自動加上這個label,這就建立Job和它管理的Pod之間的對應關係。

接下來我們檢視這個Pod的狀態,如下:

[root@master job]# kubectl get pod
NAME                                READY   STATUS      RESTARTS   AGE
job-demo-f9hmn                      0/1     Completed   0          2m35s

我們可以看到執行結束後,STATUS變成了Completed狀態,而且RESTARTS為0(表示為重啟)。我們也可以看一下這個Pod的日誌。

我們可以看到這個Pod只運行了一次。

那麼如果這個任務執行失敗了呢?比如我們將上面的YAML檔案中的args引數隨便改一個非Linux命令,如下:

      ......
      args:
        - "xxxxxx"
      ......

然後我們檢視Pod的狀態:



[root@master job]# kubectl get pod
NAME                                READY   STATUS              RESTARTS   AGE
job-demo-2ccjq                      0/1     Error               0          11s
job-demo-hwss6                      0/1     ContainerCreating   0          3s

我們看到這個Job任務沒有執行成功,我們定義了restartPolicy=Never,這時候Job Controller就會建立一個新的Pod。我們在Job的YAML檔案裡定義了backoffLimit=4,這表明重試次數只有4次,預設是6次,4次重試都還未成功,則這個Job則失敗:

[root@master job]# kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
job-demo-2ccjq                      0/1     Error     0          3m21s
job-demo-2kdpc                      0/1     Error     0          2m43s
job-demo-7q62j                      0/1     Error     0          3m3s
job-demo-hwss6                      0/1     Error     0          3m13s
You have new mail in /var/spool/mail/root
[root@master job]# kubectl get job
NAME       COMPLETIONS   DURATION   AGE
job-demo   0/1           5m49s      5m49s
You have new mail in /var/spool/mail/root

如果我們定義restartPolicy=OnFailure,如果作業失敗,Job Controller就不會在建立新的Pod,但是會不斷重啟這個Pod。如下,我們把Job的YAML檔案中的restartPolicy改成restartPolicy=OnFailure,測試如下:



[root@master job]# kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
job-demo-cbmft                      0/1     Error     3          69s
我們可以看到RESTARTS變為了1,表示重啟了3次。

還有一種情況,如果這個Job一直不肯結束怎麼辦呢?比如我們將上面的YAML檔案做如下修改:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
      - name: test-job
        image: busybox
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        args:
        - "sleep 3600"
      restartPolicy: OnFailure
  backoffLimit: 4

然後執行kubectl apply -f job-demo.yaml。我們可以發現這個Pod不會結束直到3600秒後,這時候如果我們加一個引數activeDeadlineSeconds,如下:



spec:
......
      restartPolicy: OnFailure
  backoffLimit: 4
  activeDeadlineSeconds: 100

這個引數的作用是如果這個Pod執行時間超過100s,這個Pod將被終止。

1.2、並行控制

在Job物件中,負責控制並行的引數為:

  • completions:定義Job至少要完成的Pod數目,既Job的最小完成數;
  • parallelism:定義一個Job在任意時間最多可以啟動多少個Pod;

我們定義下面一個Job的YAML檔案:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  parallelism: 2
  completions: 4
  template:
    metadata:
      name: job-demo
    spec:
      containers:
      - name: test-job
        image: busybox
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        args:
        - "for i in $(seq 10); do echo $i; done"
      restartPolicy: OnFailure
  backoffLimit: 4
  activeDeadlineSeconds: 100

parallelism: 2 和 completions: 4表示要完成4個pod,每次可以同時執行兩個Pod,我們建立這個Job。

[root@master job]# kubectl apply -f job-demo.yaml 
job.batch/job-demo created
[root@master job]# kubectl get pod
NAME                                READY   STATUS              RESTARTS   AGE
job-demo-kcm4c                      0/1     ContainerCreating   0          1s
job-demo-kdrxl                      0/1     Completed           0          4s
job-demo-r4k49                      0/1     ContainerCreating   0          2s
job-demo-w9c49                      0/1     Completed           0          4s

可以看到兩個執行結束,另外兩個才開始。

1.3、原理總結

從上面可以知道,Job Controller實際控制的就是Pod,它在建立的時候會在Job和Pod裡自動生成隨機字串的label,然後將它們進行繫結。

Job Controller在實際的調諧操作是根據實際在running狀態的Pod數,還有已經退出的Pod數以及parallelism和completions的引數值共同計算出在Job週期內應該建立或者刪除多少Pod,然後呼叫kube-api來執行這類操作。

所以Job Controller實際上是控制的Pod的並行度以及總共要完成的任務數這兩個重要的引數。

1.4、使用場景

1.4.1、外部管理器+Job模板

用法:把Job的YAML檔案定義為一個模板,然後用外部工具來控制這個模板生成Job。

比如我們定義如下YAML:



apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never

我們在這個YAML檔案裡設定了一個$ITEM的變數,然後我們使用外部指令碼來通過這個YAML檔案生成Job,如下:

$ mkdir ./jobs
$ for i in apple banana cherry
do
  cat job-tmpl.yaml | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
done

然後就會生成三個Job檔案,這時候就可以通過kubectl apply -f .來執行這些Job。

1.4.2、固定任務數的並行Job

在這種場景下,我們只關注是否有我們定義的指定數目的任務成功退出,並不會去關心並行度是多少。

比如:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: myrepo/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

上面的檔案會以併發度為2的方式建立8個Pod,然後每個Pod各自去處理任務,我們最終只關心是否有8個Pod啟動並且退出,只要這個目標達成,我們就認為這個Job成功執行。

1.4.3、設定並行度,不設定固定值

這種情況就必須自己決定什麼時候啟動Pod,什麼時候執行完成,由於沒有限制任務的總數,所以不僅需要一個工作佇列來決定任務的分發,還需要能夠判定工作佇列是否為空,也就是任務是否完成。

如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job2
      restartPolicy: OnFailure

由於任務數不確定,所以每個Pod必須能夠知道,自己什麼時候可以退出。

2、CronJob

CronJob其實就在Job的基礎上加了時間排程,類似於用Deployment管理Pod一樣。它和我們Linux上的Crontab差不多。

比如:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - "/bin/sh"
            - "-c"
            args:
            - "for i in $(seq 10); do echo $i; done"
          restartPolicy: OnFailure

我們可以看到spec裡其實就是一個Job Template。另外其schedule就是一個便準的Cron格式,

分鐘   小時    日    月    星期
*      *      *     *      *

我們建立上面的YAML檔案。檢視器結果:

[root@master job]# kubectl apply -f cronjob-demo.yaml 
cronjob.batch/hello created
[root@master job]# kubectl get cronjobs.batch 
NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          5s

需要注意的是,由於cron的特殊性,有時候會存在由於上一個定時任務還沒有執行完成,新的定時任務又開始了的情況,我們可以通過定義spec.concurrencyPolicy欄位來定義規則,比如:

  • concurrencyPolicy=Allow:表示這些Job可以同時存在
  • concurrencyPolicy=Firbid:表示不會建立新的Job,也就是這個定時任務被跳過
  • concurrencyPolicy=Replace:表示產生的新Job會替代舊的Job

如果某一個Job建立失敗,那麼這次建立就會被標記為miss,當在指定的時間視窗內,Miss的數達到100,那麼CronJob就會停止再建立這個Job。這個時間視窗可以通過spec.startingDeadlineSeconds來指定。