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#resourceskind <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-kindsmetadata <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來指定。