Job: 批處理調(diào)度
Kubernetes從1.2版本開(kāi)始支持批處理類型的應(yīng)用倘零,我們可以通過(guò)Kubernetes Job資源對(duì)象來(lái)定義并啟動(dòng)一個(gè)批處理任務(wù)称杨。批處理任務(wù)通常并行(或者串行)啟動(dòng)多個(gè)計(jì)算進(jìn)(work item)赖草,處理完成后,整個(gè)批處理任務(wù)結(jié)束凳枝。按照批處理任務(wù)實(shí)現(xiàn)方式的不同驴剔,批處理任務(wù)可以分為以下的幾種模式省古。
Job Template Expansion模式:一個(gè)Job對(duì)象對(duì)應(yīng)一個(gè)待處理的Work item,有幾個(gè)Work item就產(chǎn)生幾個(gè)獨(dú)立的Job丧失,通常適合Work item數(shù)量少豺妓、每個(gè)Work item要處理的數(shù)據(jù)量比較大的場(chǎng)景,比如有一個(gè)100GB的文件作為一個(gè)Work item布讹,總共有10個(gè)文件需要處理琳拭。
Queue with Pod Per Work Item模式:采用一個(gè)任務(wù)隊(duì)列存放Work item,一個(gè)Job對(duì)象作為消費(fèi)者去完成這些Work item描验,在這種模式下白嘁,Job會(huì)啟動(dòng)N個(gè)Pod,每個(gè)Pod都對(duì)應(yīng)一個(gè)Work item膘流。
Queue with Variable Pod Count模式:也是采用一個(gè)任務(wù)隊(duì)列存放Work item絮缅,一個(gè)Job對(duì)象作為消費(fèi)者去完成這些Work item,但與上面的模式不同呼股,Job啟動(dòng)的Pod數(shù)量是可變的耕魄。
還有一種被稱為Single Job with Static Work Assignment的模式,也是一個(gè)Job產(chǎn)生多個(gè)Pod彭谁,但它采用程序靜態(tài)方式分配任務(wù)項(xiàng)吸奴,而不是采用隊(duì)列模式進(jìn)行動(dòng)態(tài)分配
以下是幾種模式的一個(gè)對(duì)比表:
模式名稱 | 是否是一個(gè)Job | Pod的數(shù)量少于Work Item | 用戶程序是否要做相應(yīng)的修改 | k8s是否支持 |
---|---|---|---|---|
Job Template Expansion | / | / | 是 | 是 |
Qucue with Pod Per Work Item | 是 | / | 有時(shí)候需要 | 是 |
Qucue with Variable Pod Count | 是 | / | / | 是 |
Single Job with Work Assignment | 是 | / | 是 | / |
考慮到批處理的并行問(wèn)題,Kubernetes將Job分以下三種類型缠局。
1.Non-parallel Jobs
通常一個(gè)Job只啟動(dòng)一個(gè)Pod则奥,除非Pod異常,才會(huì)重啟該P(yáng)od狭园,一旦此Pod正常結(jié)束读处,Job將結(jié)束。
2.Parallel Jobs with a fixed completion count
并行Job會(huì)啟動(dòng)多個(gè)Pod妙啃,此時(shí)需要設(shè)定Job的.spec.completions參數(shù)為一個(gè)正數(shù)档泽,當(dāng)正常結(jié)束的Pod數(shù)量達(dá)至此參數(shù)設(shè)定的值后,Job結(jié)束揖赴。此外,Job的.spec.parallelism參數(shù)用來(lái)控制并行度抑胎,即同時(shí)啟動(dòng)幾個(gè)Job來(lái)處理Work Item燥滑。
3.Parallel Jobs with a work queue
任務(wù)隊(duì)列方式的并行Job需要一個(gè)獨(dú)立的Queue,Work item都在一個(gè)Queue中存放阿逃,不能設(shè)置Job的.spec.completions參數(shù)铭拧,此時(shí)Job有以下特性赃蛛。
- 每個(gè)Pod都能獨(dú)立判斷和決定是否還有任務(wù)項(xiàng)需要處理。
- 如果某個(gè)Pod正常結(jié)束搀菩,則Job不會(huì)再啟動(dòng)新的Pod呕臂。
- 如果一個(gè)Pod成功結(jié)束,則此時(shí)應(yīng)該不存在其他Pod還在工作的情況肪跋,它們應(yīng)該都處于即將結(jié)束歧蒋、退出的狀態(tài)。
- 如果所有Pod都結(jié)束了州既,且至少有一個(gè)Pod成功結(jié)束谜洽,則整個(gè)Job成功結(jié)束。
以下分別是三種批處理模型在Kubernetes中的應(yīng)用例子
首先是Job Template Expansion模式吴叶,由于在這種模式下每個(gè)Work item對(duì)應(yīng)一個(gè)Job實(shí)例阐虚,所以這種模式首先定義一個(gè)Job模板,模板里的主要參數(shù)是Work item的標(biāo)識(shí)蚌卤,因?yàn)槊總€(gè)Job都處理不同的Work item实束。如下所示的Job模板(文件名為job.yaml.txt)中的$ITEM可以作為任務(wù)項(xiàng)的標(biāo)識(shí):
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
cpmmand: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
restartPolicy: Never
生成三個(gè)job.yaml
for i in apple banana cherry ;do cat job.yaml.txt |sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml ;done
[root@k8s-master01 ~]# ls jobs
job-apple.yaml job-banana.yaml job-cherry.yaml
[root@k8s-master01 ~]# kubectl apply -f jobs
job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created
[root@k8s-master01 ~]# kubectl get jobs
NAME COMPLETIONS DURATION AGE
process-item-apple 1/1 9s 75s
process-item-banana 0/1 75s 75s
process-item-cherry 1/1 12s 75s
其次,我們看看Queue with Pod Per Work Item模式逊彭,在這種模式下需要一個(gè)任務(wù)隊(duì)列存放Work item磕洪,比如RabbitMQ,客戶端程序先把要處理的任務(wù)變成Work item放入任務(wù)隊(duì)列诫龙,然后編寫(xiě)Worker程序析显、打包鏡像并定義成為Job中的Work Pod。Worker程序的實(shí)現(xiàn)邏輯是從任務(wù)隊(duì)列中拉取一個(gè)Work item并處理签赃,在處理完成后即結(jié)束進(jìn)程谷异。并行度為2的Demo示意圖如圖
最后,我們看看Queue with Variable Pod Count模式锦聊,如圖所示歹嘹。由于這種模式下,Worker程序需要知道隊(duì)列中是否還有等待處理的Work item孔庭,如果有就取出來(lái)處理尺上,否則就認(rèn)為所有工作完成并結(jié)束進(jìn)程,所以任務(wù)隊(duì)列通常要采用Redis或者數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)圆到。
CronJob:定時(shí)任務(wù)
Kubernetes從1.5版本開(kāi)始增加了一種新類型的Job怎抛,即類似Linux Cron的定時(shí)任務(wù)Cron Job,下面看看如何定義和使用這種類型的Job芽淡。
首先马绝,確保Kubernetes的版本為1.8及以上。
其次挣菲,需要掌握Cron Job的定時(shí)表達(dá)式富稻,它基本上照搬了Linux Cron的表達(dá)式掷邦,區(qū)別是第1位是分鐘而不是秒,格式如下:
Minutes Hours DayofMonth Moth DaYofWeek Year
其中每個(gè)域都可出現(xiàn)的字符如下椭赋。
Minutes:可出現(xiàn)“,” “-” “*” “/”這4個(gè)字符抚岗,有效范圍為0~59的整數(shù)。
Hours:可出現(xiàn)“,” “-” “*” “/”這4個(gè)字符哪怔,有效范圍為0~23的整數(shù)宣蔚。
DayofMonth:可出現(xiàn)“,” “-” “*” “/” “?” “L” “W” “C”這8個(gè)字符,有效范圍為0~31的整數(shù)蔓涧。
Month:可出現(xiàn)“,” “-” “*” “/”這4個(gè)字符件已,有效范圍為1~12的整數(shù)或JAN~DEC。
DayofWeek:可出現(xiàn)“,” “-” “*” “/” “?” “L” “C” “T”這8個(gè)字符篷扩,有效范圍為1~7的整數(shù)或SUN~SAT。1表示星期天茉盏,2表示星期一鉴未,以此類推。
表達(dá)式中的特殊字符“*”與“/”的含義如下鸠姨。
-
*
:表示匹配該域的任意值铜秆,假如在Minutes域使用“*”,則表示每分鐘都會(huì)觸發(fā)事件讶迁。 - /:表示從起始時(shí)間開(kāi)始觸發(fā)连茧,然后每隔固定時(shí)間觸發(fā)一次,例如在Minutes域設(shè)置為5/20巍糯,則意味著第1次觸發(fā)在第5min時(shí)啸驯,接下來(lái)每20min觸發(fā)一次,將在第25min祟峦、第45min等時(shí)刻分別觸發(fā)罚斗。
比如,我們要每隔1min執(zhí)行一次任務(wù)宅楞,則Cron表達(dá)式如下:
*/1 * * * *
Cron Job配置文件示例
# cron.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
該例子定義了一個(gè)名為hello的Cron Job针姿,任務(wù)每隔1min執(zhí)行一次,運(yùn)行的鏡像是busybox厌衙,執(zhí)行的命令是Shell腳本距淫,腳本執(zhí)行時(shí)會(huì)在控制臺(tái)輸出當(dāng)前時(shí)間和字符串“Hello from the Kubernetes cluster”。
[root@k8s-master01 cronjob]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-1593261240-scc7c 0/1 Completed 0 2m44s
hello-1593261300-w82vg 0/1 Completed 0 104s
hello-1593261360-jdc7g 0/1 Completed 0 43s
[root@k8s-master01 cronjob]# kubectl logs hello-1593261540-srhqz
Sat Jun 27 12:39:04 UTC 2020
Hello from the Kubernetes cluster
[root@k8s-master01 cronjob]# kubectl get jobs --watch
NAME COMPLETIONS DURATION AGE
hello-1593261420 1/1 3s 2m57s
hello-1593261480 1/1 21s 117s
hello-1593261540 1/1 3s 57s
process-item-apple 1/1 9s 123m
process-item-banana 1/1 79s 123m
process-item-cherry 1/1 12s 123m
hello-1593261600 0/1 0s
hello-1593261600 0/1 0s 0s
# 刪除cronjob
[root@k8s-master01 cronjob]# kubectl delete cronjob hello
cronjob.batch "hello" deleted
自定義調(diào)度器
如果Kubernetes調(diào)度器的眾多特性還無(wú)法滿足我們的獨(dú)特調(diào)度需求迅箩,則還可以用自己開(kāi)發(fā)的調(diào)度器進(jìn)行調(diào)度溉愁。從1.6版本開(kāi)始,Kubernetes的多調(diào)度器特性也進(jìn)入了快速發(fā)展階段饲趋。
一般情況下拐揭,每個(gè)新Pod都會(huì)由默認(rèn)的調(diào)度器進(jìn)行調(diào)度。但是如果在Pod中提供了自定義的調(diào)度器名稱奕塑,那么默認(rèn)的調(diào)度器會(huì)忽略該P(yáng)od堂污,轉(zhuǎn)由指定的調(diào)度器完成Pod的調(diào)度。
在下面的例子中為Pod指定了一個(gè)名為my-scheduler的自定義調(diào)度器:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
shedulerName: my-sheduler
containers:
- name: nginx
image: nginx
如果自定義的調(diào)度器還未在系統(tǒng)中部署龄砰,則默認(rèn)的調(diào)度器會(huì)忽略這個(gè)Pod盟猖,這個(gè)Pod將會(huì)永遠(yuǎn)處于Pending狀態(tài)。
下面看看如何創(chuàng)建一個(gè)自定義的調(diào)度器换棚。
可以用任何語(yǔ)言來(lái)實(shí)現(xiàn)簡(jiǎn)單或復(fù)雜的自定義調(diào)度器式镐。下面的簡(jiǎn)單例子使用Bash腳本進(jìn)行實(shí)現(xiàn),調(diào)度策略為隨機(jī)選擇一個(gè)Node(注意固蚤,這個(gè)調(diào)度器需要通過(guò)kubectl proxy來(lái)運(yùn)行)
#!/bin/bash
SERVER='localhost:8001'
while true;
do
for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"');
do
NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
NUMNODES=${#NODES[@]}
CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"
: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
echo "Assigned $PODNAME to $CHOSEN"
done
sleep 1
done