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

保持 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 //: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