Kubernetes 實戰 —— 04. 副本機制和其他控制器:部署託管的 pod

滿賦諸機發表於2021-03-30

保持 pod 健康 P84

只要 pod 排程到某個節點,該節點上的 Kubelet 就會執行 pod 的容器,從此只要該 pod 存在,就會保持執行。如果容器的主程式奔潰, Kubelet 就會自動重啟容器;如果應用程式奔潰, Kubelet 就會自動重啟應用程式。 P84

應用程式也可能因為無限迴圈或死鎖等情況而停止響應。為確保應用在這種情況下可以重新啟動,必須從外部檢查應用程式的執行狀況,而不是依賴於應用的內部檢測。 P84

介紹存活探測器 P84

Kubernetes 可以通過存活探測器 (liveness probe) 檢查容器是否還在執行。可以為 pod 中的每個容器單獨指定存活探測器。 Kubernetes 將定期執行探測器,如果探測失敗,就會自動重啟容器。 P84

注意:Kubernetes 還支援就緒探測器 (readiness probe) ,兩者適用於兩種不同的場景。 P84

Kubernetes 有三種探測容器的機制: P84

  • HTTP GET 探測器:對容器的 IP 地址(指定的埠和路徑)執行 HTTP GET 請求。如果探測器收到響應,並且響應狀態碼不代表錯誤(狀態碼為 2xx 或 3xx ),則認為探測成功。如果伺服器返回錯誤響應狀態碼或者沒有響應,那麼探測就被認為是失敗的,容器將被重啟。
  • TCP Socket探測器:嘗試與容器指定埠建立 TCP 連線。如果連線成功建立,則探測成功。否則,容器將被重啟。
  • Exec 探測器:在容器內執行任意命令,並檢查命令的退出狀態碼。如果狀態碼是 0 ,則探測成功。所有其他狀態碼都被認為失敗,容器將被重啟。
建立基於 HTTP 的存活探測器 P85

為了讓 HTTP GET 探測器探測失敗,我們需要修改 kubia 原始碼,使得其從第五次訪問之後開始一直返回 500 狀態碼 (Internal Server Error) 。 P85

然後我們可以通過以下描述檔案 kubia-liveness-probe.yaml 建立一個包含 HTTP GET 存活探測器的 pod 。 P85

# 遵循 v1 版本的 Kubernetes API
apiVersion: v1
# 資源型別為 Pod
kind: Pod
metadata:
  # pod 的名稱
  name: kubia-liveness
spec:
  containers:
    # 建立容器所使用的映象
    - image: idealism/kubia-unhealthy
      # 容器的名稱
      name: kubia
      ports:
        # 應用監聽的埠
        - containerPort: 8080
          protocol: TCP
      # 開啟一個存活探測器
      livenessProbe:
        # 存活探測器的型別為 HTTP GET
        httpGet:
          # 探測器連線的網路埠
          port: 8080
          # 探測器請求的路徑
          path: /
使用存活探測器 P86

使用 kubectl create -f kubia-liveness-probe.yaml 建立完 pod 後,等待一段時間後,容器將會重啟。可以通過 kubectl get pod kubia-liveness 看到容器會重啟,並且無限迴圈下去: 86

NAME             READY   STATUS    RESTARTS   AGE
kubia-liveness   1/1     Running   2          4m9s

kubectl logs kubia-liveness --previous: 檢視前一個容器的日誌,可以瞭解前一個容器停止的原因。 P86

kubectl describe pod kubia-liveness: 檢視 pod 詳情。可以發現在 Containers 和 Events 裡面有終止的相關資訊。 P86

...
Containers:
  kubia:
    ...
    State:          Running  # 容器目前正常執行
      Started:      Sun, 07 Jun 2020 17:59:35 +0800
    Last State:     Terminated  # 前一個容器由於錯誤被終止,錯誤碼是 137
      Reason:       Error
      Exit Code:    137
      Started:      Sun, 07 Jun 2020 17:57:44 +0800
      Finished:     Sun, 07 Jun 2020 17:59:27 +0800
    Ready:          True
    Restart Count:  2  # 該容器已被重啟 2 次
    Liveness:       http-get http://:8080/ delay=0s timeout=1s period=10s #success=1 #failure=3
    ...
Events:
  Type     Reason     Age                  From                   Message
  ----     ------     ----                 ----                   -------
  Normal   Scheduled  <unknown>            default-scheduler      Successfully assigned default/kubia-liveness to minikube-m02
  Warning  Unhealthy  48s (x6 over 2m58s)  kubelet, minikube-m02  Liveness probe failed: HTTP probe failed with statuscode: 500  # 發現容器不健康
  Normal   Killing    48s (x2 over 2m38s)  kubelet, minikube-m02  Container kubia failed liveness probe, will be restarted  # 終止該容器
  ...

錯誤碼 137 是兩個數字的總和: 128 + x , x 是終止程式的訊號編號。 P86

  • x=9 表示是 SIGKILL 的訊號編號,意味著這個程式被強行終止,這個訊號不能被捕獲或忽略,並且在接收過程中不能執行任何清理在接收到該訊號
  • x=15 表示是 SIGTERM 的訊號編號,意味著這個程式被終止,先進行詢問程式終止,讓其清理檔案和關閉,可以被捕獲和解釋或忽略

底部列出的事件顯示了 Kubernetes 發現容器不健康,所以終止並重新建立。 P86

注意:當容器被強行終止時,會建立一個全新的容器,而不是重啟原來的容器。 P86

配置存活探測器的附加屬性 P87

可以使用 kubectl explain pod.spec.containers.livenessProbe 檢視存活探測器能使用的自定義附加引數。

基於 kubia-liveness-probe.yaml 建立一個新的描述檔案 kubia-liveness-probe-initial-delay.yaml ,並新增 pod.spec.containers.livenessProbe.initialDelaySeconds 屬性,值為 15 ,表示在第一次探測器等待 15 秒。

...
spec:
  containers:
    # 建立容器所使用的映象
    - image: idealism/kubia-unhealthy
      ...
      # 開啟一個存活探測器
      livenessProbe:
        ...
        # 第一次探測前等待 15 秒
        initialDelaySeconds: 15

這樣可以在應用程式準備好之後再進行探測,以免應用程式啟動時間過長導致一直探測失敗而無限重啟。

建立有效的存活探測器 P88
  • 存活探測器應該檢查什麼:簡易的存活探測器可以僅檢查伺服器是否響應,但為了更好地進行存活檢查,需要將探測器配置為請求特定的 URL 路徑(如 /health ),並讓應用從內部對內部執行的所有重要元件執行狀態檢查,以確保它們都沒有終止或停止響應。 P88
    • 確保 /health 不需要認證,否則探測會一直失敗,導致容器無限重啟
    • 檢查應用的內部,檢查結果不能受任何外部因素的影響。例如資料庫連不上時,存活探測器不應該返回失敗,如果問題在資料庫,那麼重啟容器不會解決問題。
  • 保持探測器輕量 P89
    • 不應消耗太多計算資源
    • 執行不應花太長時間。預設情況下,探測器執行的頻率相對較高,必須在一秒之內執行完畢
  • 無須在探測器中實現重試:探測器的失敗閾值是可配置的,並且通常在容器被終止之前探測器必須失敗多次 P89
  • 存活探測器小結:存活探測由 pod 上的 Kubelet 執行, Kubernetes 控制皮膚不會參與。因此節點奔潰時,控制皮膚會為所有隨節點停止執行的 pod 建立替代者,但不會為直接建立的 pod 建立替代者,因為這些 pod 只被節點上的 Kubelet 管理。為了避免這種情況發生,我們應該使用控制器或類似機制管理 pod 。 P89

瞭解 Deployment P89

:本節中提到的 pod 受 Deployment 管理等說法為簡化說法,實際上 pod 由受 Deployment 管理建立的 ReplicaSet 進行管理建立。

Deployment 是一種 Kubernetes 資源,可確保它的 pod 始終保持執行狀態。如果 pod 因為任何原因消失(例如節點從叢集中消失或由於該 pod 已從節點中逐出),則 Deployment 會注意到缺少了 pod 並建立替代者。 P89

圖 4.1 節點故障時,只有 Deployment 管理的 pod 被重新建立.png

上圖的節點 1 有兩個節點, Pod A 是被直接建立的,而 Pod B 由 Deployment 管理。節點異常退出後, Deployment 會建立一個新的 Pod B2 來替換減少的 Pod B ,而 Pod A 由於沒有東西負責重建而完全丟失。 P89

Deployment 的操作 P90

Deployment 會持續監控正在執行的 pod 列表,並保證匹配標籤選擇器(03. pod: 執行於 Kubernetes 中的容器 中介紹過標籤選擇器及使用方式)的 pod 數目與期望相符。 P90

介紹控制器的協調流程 P91

Deployment 的工作是確保 pod 數量始終與其標籤選擇器匹配。 P91

圖 4.2 一個 Deployment 的協調流程.png

瞭解 Deployment 的三部分 P91

  • 標籤選擇器 (label selector) :用於確定 Deployment 作用域中有哪些 pod
  • 副本個數 (replica count) :指定應執行的 pod 數量
  • pod 模版 (pod template) :用於建立新的 pod 副本

圖 4.3 Deployment 的三個關鍵部分.png

Deployment 的副本個數、標籤選擇器和 pod 模版都可以隨時修改,但只有副本數目但變更會影響現有的 pod 。 P92

更改控制器的標籤選擇器或 pod 模版的效果 P92

更改標籤選擇器和 pod 模版對現有的 pod 沒有影響。更改標籤選擇器會使現有的 pod 脫離 Deployment 的範圍,因此控制器會停止關注它們。更改模版僅影響由此 Deployment 建立的新 pod 。 P92

使用 Deployment 的好處 P92

  • 確保 pod 持續執行:在現有 pod 丟失時啟動一個新 pod
  • 叢集節點發生故障時,為故障節點上執行的受 Deployment 管理的所有 pod 建立替代副本
  • 輕鬆實現 pod 水平伸縮——手動和自動都可以

注意: pod 例項永遠不會重新安置到另一個節點。 Deployment 會建立一個全新的 pod 例項,它與正在替換的例項無關。 P92

建立一個 Deployment P92

我們可以通過以下描述檔案 kubia-deployment.yaml 建立一個 Deployment ,它確保符合標籤選擇器 app=kubia 的 pod 例項始終是三個。

# 遵循 v1 版本的 Kubernetes API
apiVersion: apps/v1
# 資源型別為 Deployment
kind: Deployment
metadata:
  # Deployment 的名稱
  name: kubia
spec:
  # 指定與標籤選擇器匹配的 pod 數目為 3
  replicas: 3
  # 指定 Deployment 操作物件
  selector:
    # 需要匹配以下指定的標籤
    matchLabels:
      app: kubia
  # 啟動 pod 使用的模版(可以發現模版內容與 kubia-manual.yaml 對應部分一致)
  template:
    metadata:
      # 指定標籤為 app=kubia
      labels:
        app: kubia
    spec:
      containers:
        # 容器的名稱
        - name: kubia
          # 建立容器所使用的映象
          image: idealism/kubia
          # 應用監聽的埠
          ports:
            - containerPort: 8080
              protocol: TCP

模版中的 pod 標籤必須和 Deployment 的標籤選擇器匹配, API 伺服器會校驗 Deployment 的定義,不會接受錯誤配置。 P93

若不指定選擇器,它會自動根據 pod 模版中的標籤自動配置,這樣可以讓描述檔案更簡潔。 P93

使用 Deployment P94

kubectl create -f kubia-deployment.yaml 會建立一個名為 kubia 的 Deployment ,它會根據 pod 模版啟動三個新 pod 。 P94

kubectl get pods 可以檢視當前建立的所有 pod :

NAME                    READY   STATUS             RESTARTS   AGE
kubia-9495d9bf5-5dwj7   1/1     Running            0          3m53s
kubia-9495d9bf5-5j6zr   1/1     Running            0          3m53s
kubia-9495d9bf5-w98f6   1/1     Running            0          3m53s

檢視 Deployment 對已刪除的 pod 的響應 P94

kubectl delete pod kubia-9495d9bf5-5dwj7 會刪除一個 pod ,然後再次檢視當前所有 pod ,可以發現會有 4 個 pod ,因為刪除的 pod 正在終止,並且正在建立一個新的 pod : P94

NAME                    READY   STATUS              RESTARTS   AGE
kubia-9495d9bf5-5dwj7   1/1     Terminating         0          24m
kubia-9495d9bf5-5j6zr   1/1     Running             0          24m
kubia-9495d9bf5-kxcw5   0/1     ContainerCreating   0          9s
kubia-9495d9bf5-w98f6   1/1     Running             0          24m

控制器如何建立新的 pod P95

控制器通過建立一個新的替代 pod 來響應 pod 的刪除操作。但它並沒有對刪除本身作出反應,而是針對由此產生對狀態—— pod 數量不足作出反應。 P95

圖 4.4 如果一個 pod 消失, Deployment 將發現 pod 數目就更少並建立一個新的替代 pod.png

應對節點故障 P96

接下來我們將關閉一個節點的網路介面來模擬節點故障。 P96

  1. minikube ssh --node='m02': 進入節點內部
  2. sudo ifconfig eth0 down: 關閉該節點的網路介面
  3. kubectl get nodes: 發現節點 minikube-m02 的狀態為未就緒 (NotReady)
  4. kubectl get pods: 可能仍然會看到與之前相同的三個 pod ,因為 Kubernetes 在重新排程 pod 之前會等待一段時間(如果節點因臨時網路故障或 Kubelet 重啟而無法訪問)。如果節點在幾分鐘內無法訪問, Deployment 會立即啟動一個新的 pod 。

將 pod 移入/移出 Deployment 的作用域 P97

Deployment 建立的 pod 並不是繫結到 Deployment 。在任何時刻, Deployment 管理與標籤選擇器匹配的 pod 。通過更改 pod 的標籤,可以將它從 Deployment 的作用域中新增或刪除。 P97

儘管一個 pod 沒有繫結到一個 Deployment 擁有的 ReplicaSet ,但該 pod 在 metadata.ownerReferences 中儲存它屬於哪一個 ReplicaSetP98

Deployment 管理的 pod 加標籤 P98

kubectl label pod kubia-9495d9bf5-5mmhb type=special: 給 pod 新增其他標籤不會影響 Deployment 的管理範圍,它只關心該 pod 是否具有標籤選擇器中引用的所有標籤。 P98

更改已託管的 pod 的標籤 P98

kubectl label pod kubia-9495d9bf5-5mmhb app=foo --overwrite: 更改其中一個 pod 的標籤將使其不再與 Deployment 的標籤選擇器相匹配,並不再由 Deployment 管理,只剩下兩個匹配的 pod 。因此, Deployment 會啟動一個新的 pod ,將數目恢復為三。 P98

圖 4.5 通過更改標籤從 Deployment 的作用域中刪除一個 pod.png

更改 Deployment 的標籤選擇器 P100

更改 Deployment 的標籤選擇器會讓所有的 pod 脫離 Deployment 的管理,導致它建立三個新的 pod 。你永遠不會修改控制器的標籤選擇器,但會時不時地更改它的 pod 模版。 P100

修改 pod 模版 P100

Deployment 的 pod 模版可以隨時修改,可以使用 kubectl edit deployment kubia 編輯 Deployment 。更改後會重新建立一個新的 ReplocaSet ,並使原有的 ReplocaSet 的副本數變為 0 。因此,使用 kubectl get pods 將發現有 6 個 pod ,pod 的字首是對應的 ReplocaSet 的名稱。

NAME                    READY   STATUS        RESTARTS   AGE
kubia-9495d9bf5-kxcw5   1/1     Terminating   0          78m
kubia-9495d9bf5-w98f6   1/1     Terminating   0          102m
kubia-9495d9bf5-xn67d   1/1     Terminating   0          29m
kubia-bc974964b-bp4l2   1/1     Running       0          22s
kubia-bc974964b-r29j2   1/1     Running       0          39s
kubia-bc974964b-xl677   1/1     Running       0          14s

若通過 kubectl edit replicaset kubia-bc974964b 直接修改 Deployment 擁有的 ReplicaSet 例項。這樣效果和直接修改 Deployment 類似,也會建立一個新的 ReplicaSet ,並使原有的 ReplocaSet 的副本數變為 0 。這樣修改不會將新的 pod 模版同步回原有的 Deployment ,但刪除 Deployment 時仍然會刪除所有相關的 ReplocaSet 及其管理的 pod 。

水平縮放 pod P101

kubectl scale deployment kubia --replicas=10: 可以修改 Deployment 需要保持的 pod 例項的數量(02. 開始使用 Kubernetes 和 Docker中介紹過使用該命令進行伸縮)。 P101

也可以通過 kubectl edit deployment kubia 修改 spec.replicas 的數量,從而更改需要保持的 pod 例項的數量。 P102

刪除一個 Deployment

當通過 kubectl delete deployment kubia 刪除 Deployment 時,對應的 ReplicaSet 和 pod 都會被刪除。

而通過 kubectl delete replicaset kubia-bc974964b 刪除 ReplicaSet 時,對應的 pod 會被刪除,但由於 Deployment 會重新建立一個 Replicaset ,所以又會自動建立對應數量的 pod 。

圖 4.7 使用 --cascade=false 刪除 ReplicaSet 使 pod 不受管理.png

當通過 kubectl delete deployment kubia --cascade=false 刪除 Deployment 時,會保留對應的 ReplicaSet 和 pod ,這樣ReplicaSet 不再受管理,但是 pod 仍然受 ReplicaSet 管理。當重新建立符合要求的 Deployment 時, ReplicaSet 又會受到管理。

同樣地,通過 kubectl delete replicaset kubia-bc974964b --cascade=false 刪除 ReplicaSet 時,也會保留對應的 pod 。這樣 pod 不再受管理。當建立符合要求的 ReplicaSet 時,這些 pod 又會受到管理。

使用 ReplicaSet P104

:書中原本上一節講得是 ReplicationController ,但我直接使用 Deployment 進行實踐,並依照現在的結果進行了修改。目前推薦使用 Deployment ,並且 ReplicaSet 是受 Deployment 管理的,所以不再詳細實踐本節內容。

使用更富有表達力的標籤選擇器 P106

基於 kubia-deployment.yaml 建立一個新的描述檔案 kubia-deployment-matchexpressions.yaml ,並將 spec.selector.matchLabels 屬性替換為 spec.selector.matchExpressionsP107

...
spec:
  ...
  # 指定 Deployment 操作物件
  selector:
    # 需要匹配滿足以下要求的標籤
    matchExpressions:
      # 標籤名為 app 的值在 ["kubia"] 中
      - app: app
        operator: In
        values:
          - kubia
  ...

matchExpressions 執行給選擇器新增額外的表示式。每個表示式都必須包含一個 key 、一個 operator ,並且可能還有一個 values 的列表(取決於 operator )。共有四個有效的運算子: P107

  • In: 標籤的值必須與其中一個指定的 values 匹配
  • NotIn: 標籤的值與任何指定的 values 都不匹配
  • Exists: pod 必須包含一個指定名稱的標籤(不關心值)。使用此運算子時,不應指定 values 欄位
  • DoesNotExist: pod 不得包含指定名稱的標籤。使用此運算子時,不應指定 values 欄位

如果指定了多個表示式,則所有這些表示式都必須為 true 才能使選擇器與 pod 匹配。如果同時指定 matchLabelsmatchExpressions ,則所有標籤都必須匹配,且所有表示式都必須為 true 才能使選擇器與 pod 匹配。 P107

使用 DaemonSet 在每個節點上執行一個 pod P107

DaemonSet 可以讓 pod 在叢集中的每個節點上執行,並且每個節點正好有一個執行的 pod 例項。 P107

圖 4.8 DaemonSet 在每個節點上只執行一個 pod 副本.png

使用 DaemonSet 在每個節點上執行一個 pod P108

DaemonSet 沒有副本數的概念,它確保建立足夠的 pod ,並在每一個節點上部署一個 pod 。如果節點下線, DaemonSet 不會重新建立 pod ;但新節點新增到叢集中,它會立刻部署一個新的 pod 例項到該節點。 P108

使用 DaemonSet 只在特定的節點上執行 pod P109

DaemonSet 將 pod 部署到叢集的所有節點上,除非通過 pod 模版中的 spec.nodeSelector 屬性指定這些 pod 只在部分節點上執行。 P109

注意:節點可以被設定為不可排程,防止 pod 被部署到節點上。但 DaemonSet 會將 pod 部署到這些節點上,因為無法排程但屬性只會被排程器使用,而 DaemonSet 的目的是執行系統服務,即使在不可排程的節點上,系統服務通常也需要執行。 P109

用一個例子來解釋 DaemonSet P109

假設有一個名為 ssd-monitor 的守護程式,它需要在包含 SSD 的所有節點上執行。包含 SSD 的節點已被新增了 disk=ssd 標籤,所以我們需要建立一個 DaemonSet ,它只在擁有上述標籤的節點上執行守護程式。 P109

圖 4.9 使用含有節點選擇器的 DaemonSet 在特定的節點上部署 pod.png

建立一個 DaemonSet 描述檔案 P110

為了模擬 ssd-monitor 的監控程式,我們將使用以下 Dockerfile 建立一個每 5 秒中列印 SSD OK 的映象。

FROM busybox
ENTRYPOINT while true; do echo 'SSD OK'; sleep 5; done

為了將 ssd-monitor 部署到符合要求的每個節點上,我們還需要使用以下 ssd-monitor-daemonset.yaml 描述檔案進行部署。

# 遵循 apps/v1 版本的 Kubernetes API
apiVersion: apps/v1
# 資源型別為 DaemonSet
kind: DaemonSet
metadata:
  # DaemonSet 的名稱
  name: ssd-monitor
spec:
  # 指定 DaemonSet 操作物件
  selector:
    # 需要匹配以下指定的標籤
    matchLabels:
      app: ssd-monitor
  # 啟動 pod 使用的模版
  template:
    metadata:
      # 指定標籤為 app=ssd-monitor
      labels:
        app: ssd-monitor
    spec:
      # 指定選擇具有 disk=ssd 標籤的節點部署
      nodeSelector:
        disk: ssd
      containers:
        # 容器的名稱
        - name: main
          # 建立容器所使用的映象
          image: idealism/ssd-monitor

實踐 P110

  1. kubectl create -f ssd-monitor-daemonset.yaml: 按照指定的描述檔案建立一個 DaemonSet
  2. kubectl get daemonsets: 可以發現所有的值都是 0 ,因為目前還沒有節點擁有 disk=ssd 標籤
  3. kubectl get pods: 可以發現目前還沒有 pod
  4. kubectl label node minikube-m03 disk=ssd: 給節點 minikube-m03 打上標籤 disk=ssd
  5. kubectl get pods: 可以發現剛剛啟動了一個 pod
    NAME                    READY   STATUS              RESTARTS   AGE
    ssd-monitor-bbqbp       0/1     ContainerCreating   0          2s
    
  6. kubectl label node minikube-m03 disk=hdd --overwrite: 將節點 minikube-m03 的標籤 disk=ssd 修改為 disk=hdd
  7. kubectl get pods: 可以發現剛剛啟動的 pod 正在終止
    NAME                    READY   STATUS        RESTARTS   AGE
    ssd-monitor-bbqbp       1/1     Terminating   0          2m37s
    

執行執行單個任務的 pod P112

介紹 Job 資源 P112

Kubernetes 通過 Job 資源支援執行一種 pod ,該 pod 子啊內部程式成功結束時,不重啟容器。一旦任務完成, pod 就被認為處於完成狀態。 P112

在節點發生故障時,該節點上由 Job 管理的 pod 將被重新安排到其他節點。如果程式本身異常退出(程式返回錯誤退出碼時),可以將 Job 配置為重新啟動容器。 P112

圖 4.10 由 Job 管理的 pod 會一直被重新安排,知道它們成功完成任務.png

定義 Job 資源 P113

為了模擬耗時的任務,我們將使用以下 Dockerfile 建立一個呼叫 sleep 120 命令的映象。

FROM busybox
ENTRYPOINT echo "$(date) Batch job starting"; sleep 120; echo "$(date) Finished succesfully"

為了管理部署 batch-job ,我們還需要使用以下 batch-job.yaml 描述檔案進行部署。

# 遵循 batch/v1 版本的 Kubernetes API
apiVersion: batch/v1
# 資源型別為 Job
kind: Job
metadata:
  # Job 的名稱
  name: batch-job
spec:
  # 啟動 pod 使用的模版
  template:
    metadata:
      # 指定標籤為 app=batch-job
      labels:
        app: batch-job
    spec:
      # Job 不能使用 Always 為預設的重啟策略
      restartPolicy: OnFailure
      containers:
        # 容器的名稱
        - name: main
          # 建立容器所使用的映象
          image: idealism/batch-job

設定 Job 的重啟策略為 OnFailureNever 可以防止容器在完成任務時重新啟動。 P114

Job 執行一個 pod P114
  1. kubectl create -f batch-job.yaml: 根據描述檔案建立指定的 Job
  2. kubectl get jobs: 檢視 job ,可以發現剛剛建立的 Job
    NAME        COMPLETIONS   DURATION   AGE
    batch-job   0/1           5s         5s
    
  3. kubectl get pods: 檢視 pod ,可以發現 Job 建立的 pod 正在執行
    NAME                    READY   STATUS        RESTARTS   AGE
    batch-job-d59js         1/1     Running       0          10s
    
  4. kubectl get pods: 等兩分鐘後再檢視 pod ,可以發現 Job 建立的 pod 狀態已經變為 Completed ,即任務已經完成。 pod 未被刪除,所以我們可以檢視 pod 的日誌
    NAME                    READY   STATUS        RESTARTS   AGE
    batch-job-d59js         0/1     Completed     0          2m56s
    
  5. kubectl logs pod batch-job-d59js: 檢視 pod 的日誌
    Sun Jun  7 22:36:04 UTC 2020 Batch job starting
    Sun Jun  7 22:38:04 UTC 2020 Finished succesfully
    
  6. kubectl get jobs: 再次檢視 job ,可以發現需要執行的 1 個 pod 已經完成
    NAME        COMPLETIONS   DURATION   AGE
    batch-job   1/1           2m45s      6m25s
    
Job 中執行多個 pod 例項 P114

Job 配置中設定 spec.completionsspec.parallelism 可以讓 Job 建立多個 pod 例項,並允許以並行的方式執行它們。 P114

基於 batch-job.yaml 建立一個新的描述檔案 multi-completion-parallel-batch-job.yaml ,並新增 spec.completionsspec.parallelism 屬性,指定需要成功執行完成 5 個 pod ,最多 2 個 pod 並行執行 : P115

...
spec:
  # 必須確保 5 個 pod 執行完成
  completions: 5
  # 最多 2 個 pod 可以並行執行
  parallelism: 2
  ...
  1. kubectl create -f multi-completion-parallel-batch-job.yaml: 根據描述檔案建立指定的 Job

  2. kubectl get pods: 檢視執行的 pod ,可以發現共有兩個 pod 正在執行。只要一個 pod 執行完成, Job 將執行下一個 pod ,直至 5 個 pod 都成功完成

    NAME                                        READY   STATUS        RESTARTS   AGE
    multi-completion-parallel-batch-job-fpwv5   1/1     Running       0          37s
    multi-completion-parallel-batch-job-m4cqw   1/1     Running       0          37s
    
限制 Job pod 完成任務的時間 P116
  • Pod.spec.activeDeadlineSeconds: 可以指定一個 pod 最長存活時間,超時則終止 pod 並標記 Job 失敗,可以用來限制 pod 完成任務的時間
  • Job.spec.backoffLimit: 可以配置一個 Job 在被標記為失敗前最多嘗試的次數,預設為 6 次

安排 Job 定期執行或在將來執行一次 P116

建立一個 CronJob P116

為了每 15 分鐘執行一次前面的任務,我們需要建立以下 cronjob.yaml 描述檔案:

# 遵循 batch/v1beta1 版本的 Kubernetes API
apiVersion: batch/v1beta1
# 資源型別為 CronJob
kind: CronJob
metadata:
  # Job 的名稱
  name: batch-job-every-fifteen-minutes
spec:
  # Cron 表示式表明當前任務在每天每小時的 0, 15, 30, 45 分執行
  schedule: "0,15,30,45 * * * *"
  # 指定最遲必須在預定時間後 15 秒內開始執行,否則就標記為一次失敗的 `Job`
  startingDeadlineSeconds: 15
  # 建立 Job 使用的模版(可以發現和 batch-job.yaml 的 spec 部分基本一致)
  jobTemplate:
    spec:
      # 啟動 pod 使用的模版
      template:
        metadata:
          # 指定標籤為 app=periodic-batch-job
          labels:
            app: periodic-batch-job
        spec:
          # Job 不能使用 Always 為預設的重啟策略
          restartPolicy: OnFailure
          containers:
            # 容器的名稱
            - name: main
              # 建立容器所使用的映象
              image: idealism/batch-job

kubectl get cronjobs: 可以檢視所有的 CronJob

NAME                              SCHEDULE             SUSPEND   ACTIVE   LAST SCHEDULE   AGE
batch-job-every-fifteen-minutes   0,15,30,45 * * * *   False     0        <none>          8s

CronJob 總是為計劃中配置的每個執行建立一個 Job ,但可能會有以下兩種問題:

  • 同時建立兩個 Job :保證任務是冪等的
  • 沒有建立 Job :保證下一個任務能執行完成錯過的任何工作

本文首發於公眾號:滿賦諸機(點選檢視原文) 開源在 GitHub :reading-notes/kubernetes-in-action

相關文章