kubernetes對象之Job


系列目錄

job是什么

對於ReplicaSet、ReplicationController等類型的控制器而言,它希望pod保持預期數目、持久運行下去,除非用戶明確刪除,否則這些對象一直存在,它們針對的是耐久性任務,如web服務等。對於非耐久性任務,比如壓縮文件,任務完成后,pod需要結束運行,不需要pod繼續保持在系統中,這個時候就要用到Job。因此說Job是對ReplicaSet、ReplicationController等持久性控制器的補充。

Job與其它控制器的細微差別

Job定義方法與ReplicaSet等控制器相似,只有細微差別,如下:

  • Job中的restart policy必需是"Never"或者"OnFailure",這個很好理解,因為pod要運行到結束,而不是反復重新啟動。

  • Job不需要選擇器,其中的pod也不需要標簽,系統在創建Job時會自動添加相關內容。當然用戶也可以出於資源組織的目的添加標簽,但這個與Job本身的實現沒有關系。

  • Job新增加兩個字段:.spec.completions、.spec.parallelism。詳細用法在示例中說明

  • backoffLimit字段:示例中說明

示例

非並發Job

非並發Job的含義是,Job啟動后,只運行一個pod,pod運行結束后整個Job也就立刻結束。

以下是簡單的Job配置文件,只包含一個pod,輸出圓周率小數點后2000位,運行時間大概為10s:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

以上示例無需設置選擇器、pod標簽。無需設置.spec.completions、.spec.parallelism,這兩個字段的默認值都是1。backoffLimit=4,表示允許pod失敗的次數。將以上內容保存成文件並創建Job:

$ kubectl create -f https://k8s.io/examples/controllers/job.yaml
job "pi" created

確認Job狀態:

$ kubectl describe jobs/pi
Name:             pi
Namespace:        default
Selector:         controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
Labels:           controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
                  job-name=pi
Annotations:      <none>
Parallelism:      1
Completions:      1
Start Time:       Tue, 07 Jun 2016 10:56:16 +0200
Pods Statuses:    0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
                job-name=pi
  Containers:
   pi:
    Image:      perl
    Port:
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  1m           1m          1        {job-controller }                Normal      SuccessfulCreate  Created pod: pi-dtn4q

從以上輸出可以看到系統自動添加的Selector、Pod labels。注意Events的輸出,全程只創建了一個pod。

列出Job的所有pod:

$ pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath={.items..metadata.name})
$ echo $pods
pi-aiw0a

查看Pod的輸出

$ kubectl logs $pods
3.14159265358979323846264......

以上是最簡單的Job應用示例,輸出圓周率小數點后2000位。但是,考慮另外一種情況,假如我們需要計算圓周率小數點后3000、4000、5000位怎么辦?一種方案是先將上例中的2000改成3000,創建並運行。然后再改成4000,再創建並運行,一直到5000。顯然這種方案並不高明:麻煩、資源利用率低。另外一種方法是同時創建4個Job,分別計算2000、3000、4000、5000。注意4個Job的name字段不能沖突分別是pi-2000、pi-3000、pi-4000、pi-5000,
文件名分別為pi-2000.yaml、pi-3000.yaml、pi-4000.yaml、pi-5000.yaml,並保存在目錄/tmp/pi下。利用kubectl對目錄的支持一次性創建4個Job:

$ kubectl create -f /tmp/pi
job "pi-2000" created
job "pi-3000" created
job "pi-4000" created
job "pi-5000" created

以上方法是偽並發,只適用於任務量少的情況。假如我們需要處理的任務是從pi-1到pi-10000,那么以上方法就不適用了:

  • 需要同時創建太多的Job與pod,不好管理。

  • 資源配額限制。

粗並發Job

本例創建一個Job,但Job要創建多個pod。了解完示例后就明白為什么叫“粗並發”。

本示例需要一個消息隊列服務的配合,不詳細描述如何部署、填充消息隊列服務。假設我們有一個RabbitMQ服務,集群內訪問地址為:amqp://guest:guest@rabbitmq-service:5672。其有一個名為job1的隊列,隊列內有apple banana cherry date fig grape lemon melon共8個成員。

另外假設我們有一個名為gcr.io/ /job-wq-1的image,其功能是從隊列中讀取出一個元素並打印到標准輸出,然后結束。注意,它只處理一個元素就結束了。接下來創建如下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: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

上例中,completions的值為8,等於job1隊列中元素的個數。因為每個成功的pod處理一個元素,所以需要成功8次,job1中的所有成員就會被處理完成。在粗並發模式下,completions的值必需指定,否則其默認值為1,整個Job只處理一個成員就結束了。

上例中,parallelism的值是2。雖然需要pod成功8次,但在同一時間,只允許有兩個pod並發。一個成功結束后,再啟動另一個。這個參數的主要目的是控制並發pod的個數,可根據實際情況調整。當然可以不指定,那么默認的並發個數就是1。

env中的內容告訴image如何訪問隊列。

將以上內容保存在job.yaml文件中,運行Job:

kubectl create -f ./job.yaml

稍等片刻Job運行完成,查看結果:

$ kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2017 16:42:02 +0800
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      gcr.io/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

查看Events,可以看到總共創建了8個pod。在本例中,每處理隊列中的一個成員都需要創建一個pod,開銷很大。如果隊列中的成員個數非常龐大,那么這種處理方式就不適用。我們希望少創建pod、每個pod能處理多條記錄,請看下面示例

細並發Job

redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

接下來創建image,詳細過程不描述。只需確定這個image運行如下名為worker.py的python程序

#!/usr/bin/env python
 
import time
import rediswq
 
host="redis"
# Uncomment next two lines if you do not have Kube-DNS working.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")
 
q = rediswq.RedisWQ(name="job2", host="redis")
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2) 
  if item is not None:
    itemstr = item.decode("utf=8")
    print("Working on " + itemstr)
    time.sleep(10) # Put your actual work here instead of sleep.
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

首先連接到redis的job2隊列。然后是一個while循環,每次讀job2中的一條記錄並輸出,然后sleep 10s。循環退出的條件是job2隊列為空。這個image與示例2不同,示例2只處理一條記錄這就結束,而這個可以處理多條一直到隊列為空。

接下來定義Job:

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
      restartPolicy: OnFailure

上例中,無需像示例2一樣指定 completions的值,因為結束條件是job2為空,已經內嵌在image的邏輯中。parallelism=2表示可以並發兩個pod,不設置默認為1,在實際應用中可據實際情況自行調整。

運行Job:

kubectl create -f ./job.yaml

過一會查看Job運行狀況:

$ kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2016 17:07:59 -0800
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              gcr.io/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

雖然允許的最大並發數是2,但Events顯示只創建成功一個pod,這個是正常情況,最大並非必需,可能系統因為資源問題達不到最大。

查看pod輸出:

$ kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

細並發相比與粗並發,減少了創建pod的開銷,使每個pod能處理多條記錄,但是pod要自己決定退出條件,如果不退出,那么Job永遠無法結束。

關於資源回收

Job創建的pod在結束運行后,無論是成功還是失敗,不會默認刪除,仍然保留在系統中,這樣用戶才可以查看其日志、狀態信息、排除錯誤。用戶需要手動運行kubectl delete刪除所有運行結束的pod,為了方便組織資源,一次性刪除會部pod,可以被pod自定義標簽。Job在運行完成后也仍然保留在系統中,由用戶刪除。所以使用Job,用戶應注意資源回收,避免資源被耗盡。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM