Pod 工作負載,亦稱 Pod 控制器。在 Kubernetes 平臺上,我們很少會直接建立一個 Pod,因為直接管理單個 Pod 的工作量將會非常繁瑣。我們可以使用 Kubernetes API 建立工作負載物件, 這些物件所表達的是比 Pod 更高階別的抽象概念,Kubernetes 控制平面根據我們定義的工作負載物件規約自動管理 Pod 物件。
一、ReplicationController、ReplicaSet 與 Deployment
在 Kubernetes 中,ReplicationController、ReplicaSet 與 Deployment 都是用於管理無狀態應用的工作負載 API 物件,都是透過指定 Pod 標籤選擇器管理對應的 Pod 集合,確保在任何時候都有指定數量的完全相同的 Pod 副本處於執行狀態。
在最早的 Kubernetes 版本中,最先出現的是 ReplicationController。隨著 Kubernetes 的發展,後來才出現了 ReplicaSet。ReplicaSet 與 ReplicationController 目的相同且行為類似,但 ReplicaSet 進一步增強了 Pod 標籤選擇器的靈活性,ReplicationController 的 Pod 標籤選擇器只能選擇一個 Pod 標籤,而 ReplicaSet 擁有集合式的標籤選擇器,可以選擇多個 Pod 標籤。也就是說,如果使用 ReplicationController 來管理 pod 副本集,同一副本集內的 Pod 都需要設定相同的標籤,而使用 ReplicaSet 來管理 pod 副本集的話,同一副本集內的 Pod 可設定不同的標籤。所以,ReplicaSet 相當於 ReplicationController 的繼承者,相比於 ReplicationController,應優先考慮使用 ReplicaSet。
Deployment 則是一個更高階的概念,它擁有並管理其 ReplicaSet,建立 Deployment 的時候即會自動建立對應的 ReplicaSet。使用 Deployment 時,ReplicaSet 為 Deployment 提供編排 Pod 建立、刪除和更新的機制,而 Deployment 則額外支援了滾動更新等更多有用的功能。所以,建議使用 Deployment 而不是直接使用 ReplicaSet, 除非你需要自定義更新業務流程或根本不需要更新。
1. ReplicationController 和 ReplicaSet
ReplicationController 和 ReplicaSet 都是透過 Pod 標籤選擇器 .spec.selector 確保在任何時候都有特定數量的 Pod 副本處於執行狀態,不同的是,前者只支援選擇一個標籤,而後者則可支援多個標籤。
二者皆不侷限於擁有並管理在其模板中設定的 Pod,只要與其 Pod 標籤選擇器匹配,就算不是當前 ReplicationController 或 ReplicaSet 建立的 Pod,也會將它們納入管理集合。同時,可以透過改變 Pod 的標籤,從 ReplicationController 或 ReplicaSet 中移除 Pod,這種技術可以用來從服務中去除 Pod,以便進行排錯、資料恢復等。
使用 kubectl delete 刪除 ReplicationController 或 ReplicaSet 時,會自動刪除所有依賴的 Pod。若只想刪除 ReplicationController 或 ReplicaSet 而不影響各個 Pod,只需為 kubectl delete 指定 --cascade=orphan 選項即可,此後還能建立一個 .spec.selector 相同的新的 ReplicationController 或 ReplicaSet 來接管這些 Pod。
2. Deployment
(1)建立 Deployment
<1>Deployment yaml 配置
<2>建立 Deployment
<3>檢視 Deployment 上線狀態
<4>檢視 ReplicaSet
<5>檢視 Pod
可看到 Pod 的標籤多了一個 pod-template-hash:
Deployment 控制器將 pod-template-hash 標籤新增到 Deployment 所建立或收留的每個 ReplicaSet 。
此標籤可確保 Deployment 的子 ReplicaSets 不重疊。 標籤是透過對 ReplicaSet 的 PodTemplate 進行雜湊處理。 所生成的雜湊值被新增到 ReplicaSet 選擇算符、Pod 模板標籤,並存在於在 ReplicaSet 可能擁有的任何現有 Pod 中。
(2)更新 Deployment
僅當 Deployment Pod 模板(即 .spec.template)發生改變時,例如模板的標籤或容器映象被更新, 才會觸發 Deployment 上線。其他更新(如對 Deployment 執行擴縮容的操作)不會觸發上線動作。
下面實踐透過更新容器映象來觸發 Deployment 的更新:
<1>更新容器映象
更新容器映象可使用命令 kubectl set image deployment/wjt-deployment wjt-pod-container=... 或 kubectl edit deployment/wjt-deployment,這裡使用前者進行演示:
<2>檢視 Deployment 與 ReplicaSet
可以看到 Deployment 建立了新的 ReplicaSet 並將其擴容至 3 節點,而將舊的 ReplicaSet 縮容至 0 節點。
<3>檢視 Pod
從 pod-template-hash 可以看得到,3 個 Pod 副本都已更新。
<4>檢視 deployment 詳情
從 NewReplicaSet 欄位可以看到 Deployment 管理的 ReplicaSet 已切換至新建的 ReplicaSet。
從 Events 欄位記錄的資訊可以看到,新的 ReplicaSet 逐步擴容,舊的 ReplicaSet 逐步縮容,此即所謂的滾動更新。如 RollingUpdateStrategy 欄位所示,Deployment 更新時,會確保 Pod 副本最大不可用比例為 25%,而且啟動的 Pod 個數最多比期望值多 25%。
當 Deployment 正在上線時被更新,Deployment 會針對更新建立一個新的 ReplicaSet 並開始對其擴容,之前正在被擴容的 ReplicaSet (控制標籤匹配 .spec.selector 但模板不匹配 .spec.template)會被翻轉,新增到舊 ReplicaSets 列表並開始縮容。
(3)回滾 Deployment
回滾 Deployment 時只有 Pod 模板部分會被回滾。
下面先模擬更新容器映象名稱錯誤,再進行回滾操作:
<1>更新容器映象名稱錯誤導致更新過程停止
可以看到,新的 ReplicaSet 建立了 1 個 Pod 以後,在拉取容器映象報錯了,在 RollingUpdateStrategy 配置的滾動更新策略下,Deployment 自動停止了有問題的上線過程。
接下來需要先檢視當前 Deployment 上線的歷史版本,並決定將其回滾到其中一個沒有問題的版本。
<2>檢查 Deployment 上線歷史
<3>回滾 Deployment 到之前的版本
可以看到,Deployment 已將其管理的 ReplicaSet 切回原來的 ReplicaSet,並將新的 ReplicaSet 縮容至節點為 0。
(4)縮放 Deployment
在 Kubernetes 中,可以透過執行命令對 Deployment 進行縮放操作,例如,將 Deployment 從 3 個節點擴充套件為 10 個節點:
如果叢集啟動了 Pod 的水平自動縮放,還可以為 Deployment 設定自動縮放器,並基於現有 Pod 的 CPU 利用率選擇要執行的 Pod 的下限和上限:
kubectl autoscale deployment/wjt-deployment --min=10 --max=15 --cpu-percent=80
Deployiment 在滾動更新的過程中,其管理的 Pod 叢集存在同時執行多個版本應用程式的情況,若此時自動縮放器剛好需要執行縮放操作,則其縮放的副本會分配到存在 Pod 執行的多個 ReplicaSet 中,分配比例則是各個 RaplicaSet 中執行的 Pod 的數量之比。
(5)暫停、恢復 Deployment 的上線過程
在 Kubernetes 中,對 Deployment 的 Pod 模板的每次更新都會觸發 Deployment 的上線。如果想對 Pod 做多次更新後再上線,則可以先暫停 Deployment 的上線過程:
kubectl rollout pause deployment/wjt-deployment
執行完多次更新操作以後,再恢復 Deployment 的上線過程:
kubectl rollout resume deployment/wjt-deployment
這樣就可以將針對 Pod 模板的多次更新一次性上線,避免觸發不必要的上線操作。
注意:不能回滾處於暫停狀態的 Deployment,除非先恢復其執行狀態。
(6)關鍵配置項
<1>清理策略(.spec.revisionHistoryLimit):指定保留當前 Deployment 的歷史版本 ReplicaSet 的數量,多餘的 ReplicaSet 將在後臺被垃圾回收,預設為 10。
<2>副本數量(.spec.replicas):指定當前 Deployment 的 Pod 副本數量。
<3>更新策略(.spec.strategy)
Deployment 的更新策略型別(.spec.strategy.type)可配置以下兩種:
Recreate:重新建立。先成功終止所有舊版本的 Pod 以後,再建立新版本的 Pod。
RollingUpdate:滾動更新。先終止一部分舊版本的 Pod,再建立一部分新版本的 Pod,以此類推,滾動更新。
採用滾動更新策略時,還需配置更新過程中不可用 Pod 的個數上限(.spec.strategy.rollingUpdate.maxUnavailable)和可以建立的超出期望 Pod 個數的 Pod 數量(.spec.strategy.rollingUpdate.maxSurge),二者都可以配置為絕對數字或百分比。
二、StatefulSet
1. 基本作用
StatefulSet 是用來管理有狀態應用的工作負載 API 物件。
StatefulSet 用來管理某 Pod 集合的部署和擴縮, 併為這些 Pod 提供持久儲存和持久識別符號。StatefulSet 為它們的每個 Pod 維護了一個有粘性的 ID,這些 Pod 不能相互替換,無論怎麼排程,每個 Pod 都有一個永久不變的 ID。
2. 簡單使用
StatefulSet 當前需要無頭服務來負責 Pod 的網路標識,所以建立 StatefulSet 之前需要先建立一個無頭服務。
(1)一個簡單的 StatefulSet yaml 配置
(2)建立 statefulSet
(3)檢視 StatefulSet 詳情
3. 部署、擴縮與更新
(1)部署和擴縮保證
<1>對於包含 N 個 副本的 StatefulSet,當部署 Pod 時,它們是依次建立的,順序為 0..N-1。
<2>當刪除 Pod 時,它們是逆序終止的,順序為 N-1..0。
<3>在將擴縮操作應用到 Pod 之前,它前面的所有 Pod 必須是 Running 和 Ready 狀態。
<4>在一個 Pod 終止之前,它後面的所有 Pod 必須完全關閉。
(2)更新策略
StatefulSet 的 .spec.updateStrategy.type 支援兩種更新策略的配置:
<1>OnDelete:StatefulSet 的控制器將不會自動更新 StatefulSet 中的 Pod, 使用者必須手動刪除 Pod 以便讓控制器建立新的 Pod,以此來對 StatefulSet 的 .spec.template 的變動作出反應。
<2>RollingUpdate:對 StatefulSet 中的 Pod 執行自動的滾動更新。這是預設的更新策略。
三、DaemonSet
1. 基本作用
DaemonSet 確保全部(或者某些)節點上執行一個 Pod 的副本。 當有節點加入叢集時, 也會為他們新增一個 Pod 。 當有節點從叢集移除時,這些 Pod 也會被回收。刪除 DaemonSet 將會刪除它建立的所有 Pod。
DaemonSet 的一些典型用法:
<1>在每個節點上執行叢集守護程序
<2>在每個節點上執行日誌收集守護程序
<3>在每個節點上執行監控守護程序
2. 簡單使用
(1)DaemonSet yaml
(2)建立 DaemonSet
(3)檢視 DaemonSet 詳情
3. DeamonSet Pods 排程原理
DaemonSet 可用於確保所有符合條件的節點都執行該 Pod 的一個副本。 DaemonSet 控制器為每個符合條件的節點建立一個 Pod,並新增 Pod 的 spec.affinity.nodeAffinity 欄位以匹配目標主機。Pod 被建立之後,預設的排程程式通常透過設定 .spec.nodeName 欄位來接管 Pod 並將 Pod 繫結到目標主機。如果新的 Pod 無法放在節點上,則預設的排程程式可能會根據新 Pod 的優先順序搶佔 (驅逐)某些現存的 Pod。
檢視 DaemonSet Pod 欄位:
四、Job 與 CronJob
在 Kubernetes 中,支援批處理、離線任務的工作負載 API 物件有 Job 和 CronJob,其中 Job 是一次性任務,只要指定數量的 Pod 成功終止,任務即結束,而 CronJob 是定時任務,它會在指定時間點建立 Job,從而間接建立 Pod,執行任務。
1. Job
(1)基本作用
Job 會建立一個或者多個 Pod,並將繼續重試 Pod 的執行,直到指定數量的 Pod 成功終止。 隨著 Pod 成功結束,Job 跟蹤記錄成功完成的 Pod 個數。 當數量達到指定的成功個數閾值時,任務(即 Job)結束。
(2)簡單使用
<1> Job yaml
spec.completions 指定需成功終止的 Pod 的個數,.spec.parallelism 指定併發執行的 Pod 的個數。
<2>建立 Job
<3>檢視 Job 詳情
該 Job 已經執行結束,一共有 10 個 Pod 執行完成,併發數為 4(從 Events 記錄的每個 Pod 的建立時間可大致看出)。
(3)Job 型別
<1>非並行 Job
通常只啟動一個 Pod,除非該 Pod 失敗。當 Pod 成功終止,立即視 Job 為完成狀態。
可以不設定 spec.completions 和 spec.parallelism。 這兩個屬性都不設定時,均取預設值 1。
<2>具有確定完成計數的並行 Job
透過設定 .spec.completions 為非 0 的正整數來指定需成功終止的 Pod 的個數,透過設定 .spec.parallelism 來指定併發執行的 Pod 的個數,也可以不設定,預設為 1。
Job 用來代表整個任務,當成功的 Pod 個數達到 .spec.completions 時,Job 被視為完成。
<3>帶工作佇列的並行 Job
多個 Pod 之間必須相互協調,或者藉助外部服務確定每個 Pod 要處理哪個工作條目。 例如,任一 Pod 都可以從工作佇列中取走最多 N 個工作條目。
每個 Pod 都可以獨立確定是否其它 Pod 都已完成,進而確定 Job 是否完成。
當 Job 中任何 Pod 成功終止,不再建立新 Pod。
一旦至少 1 個 Pod 成功完成,並且所有 Pod 都已終止,即可宣告 Job 成功完成。
一旦任何 Pod 成功退出,任何其它 Pod 都不應再對此任務執行任何操作或生成任何輸出。 所有 Pod 都應啟動退出過程。
需要將.spec.parallelism 設定為一個非負整數,不能設定 .spec.completions,預設值為 .spec.parallelism。
2. CronJob
(1)基本作用
CronJob 用於執行排期操作,例如備份、生成報告等。 一個 CronJob 物件就像 Unix 系統上的 crontab(cron table)檔案中的一行。 它用 Cron 格式進行編寫, 並週期性地在給定的排程時間執行 Job。
(2)簡單使用
<1>CronJob yaml
<2>建立 CronJob
該 CronJob 每兩分鐘就會建立一個 Job,再由該 Job 建立一個 Pod 去執行任務。
<3>檢視 CronJob 詳情
(3)相關配置
<1>任務延遲開始的最後期限
.spec.startingDeadlineSeconds:表示任務如果由於某種原因錯過了排程時間,開始該任務的截止時間的秒數。CronJob 控制器將會計算從預期建立 Job 到當前時間的時間差。 如果時間差大於該限制,則跳過此次執行。
<2>併發性規則
.spec.concurrencyPolicy:宣告瞭 CronJob 建立的任務執行時發生重疊如何處理。 spec 僅能宣告下列規則中的一種:
Allow(預設):CronJob 允許併發任務執行。
Forbid: CronJob 不允許併發任務執行;如果新任務的執行時間到了而老任務沒有執行完,CronJob 會忽略新任務的執行。
Replace:如果新任務的執行時間到了而老任務沒有執行完,CronJob 會用新任務替換當前正在執行的任務。
<3>任務歷史限制
.spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit:應保留多少已完成和失敗的任務。 預設設定分別為 3 和 1。將限制設定為 0 代表相應型別的任務完成後不會保留。
參考:
《Kubernetes 權威指南第 5 版》