這篇文章主要介紹Pod如何通過Deployment的控制器ReplicatSet實(shí)現(xiàn)水平擴(kuò)展與滾動更新。
一. 控制器模式
在kubernetes項(xiàng)目中的設(shè)計(jì)思想是“控制器”模式谆焊,在前面文章k8s(一) 基本概念與組件原理中介紹的controller manager
組件就是一系列控制器的集合航揉,我們可以通過 Kubernetes 項(xiàng)目的 pkg/controller 目錄查看這些控制器:
$ cd kubernetes/pkg/controller/
$ ls -d */
deployment/ job/ podautoscaler/
cloud/ disruption/ namespace/
replicaset/ serviceaccount/ volume/
cronjob/ garbagecollector/ nodelifecycle/ replication/ statefulset/ daemon/
這個目錄下面的每一個控制器驶乾,都以獨(dú)有的方式負(fù)責(zé)某種編排功能邑飒。
以部署Nginx-Deployment為例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
上面Yaml文件定義的spec.replicas
字段為2,即確保攜帶了app=nginx
標(biāo)簽的 Pod 的個數(shù)永遠(yuǎn)等于2個级乐,這就是我們定義的期望狀態(tài)疙咸,意味著如果集群中超過兩個副本就會刪除舊的Pod,反之就會有新的Pod創(chuàng)建风科。實(shí)現(xiàn)方式大概如下:
- Deployment 控制器從 Etcd 中獲取到所有攜帶了
app: nginx
標(biāo)簽的 Pod撒轮,然后統(tǒng)計(jì)它們的數(shù)量,這就是實(shí)際狀態(tài)贼穆; - Deployment 對象的 Replicas 字段的值就是期望狀態(tài)题山,即我們事先定義的副本數(shù);
- Deployment 控制器將兩個狀態(tài)做比較故痊,然后根據(jù)比較結(jié)果顶瞳,確定是創(chuàng)建 Pod,還是刪除已有的 Pod愕秫。
Deployment 這種控制器的設(shè)計(jì)原理慨菱,就是用一種對象管理另一種對象的方式。
類似 Deployment 這樣的一個控制器戴甩,實(shí)際上都是由上半部分的控制器定義(包括期望狀態(tài))符喝,加上下半部分的被控制對象PodTemplate
模板組成。
二. 作業(yè)副本的水平擴(kuò)展/收縮
在上面例子中簡單介紹了Deployment控制器的實(shí)現(xiàn)方式甜孤,實(shí)現(xiàn)了 Kubernetes 項(xiàng)目中一個非常重要的功能:Pod 的“水平擴(kuò)展 / 收縮”(horizontal scaling out/in)协饲。這個功能,是從 PaaS 時代開始课蔬,一個平臺級項(xiàng)目就必須具備的編排能力囱稽。
舉個例子郊尝,如果你更新了 Deployment 的 Pod 模板(比如二跋,修改了容器的鏡像),那么 Deployment 就需要遵循一種叫作“滾動更新”(rolling update)的方式流昏,來升級現(xiàn)有的容器扎即。而這個能力的實(shí)現(xiàn),依賴的是 Kubernetes 項(xiàng)目中的一個非常重要的概念(API 對象):ReplicaSet况凉。
Deployment 實(shí)際控制的是ReplicaSet對象谚鄙,由ReplicaSet來管理Pod,
通過上圖刁绒,我們可以看到定義了 replicas=3 的 Deployment闷营,與它的 ReplicaSet,以及 Pod 的關(guān)系,實(shí)際上是一種“層層控制”的關(guān)系傻盟。Deployment 通過“控制器模式”速蕊,來操作 ReplicaSet 的個數(shù)和屬性,進(jìn)而實(shí)現(xiàn)“水平擴(kuò)展 / 收縮”和“滾動更新”這兩個編排動作娘赴。
實(shí)現(xiàn)水平擴(kuò)展 / 收縮
Deployment Controller 只需要修改它所控制的 ReplicaSet 的 Pod 副本個數(shù)就可以實(shí)現(xiàn)水平擴(kuò)展 / 收縮规哲。我們可以通過修改YAML文件或者kubectl scale來具體實(shí)現(xiàn),比如將上面的nginx-deployment的副本數(shù)擴(kuò)展為4:
$ kubectl scale deployment nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled
三. 滾動更新
1. 滾動更新的實(shí)現(xiàn)原理
我們根據(jù)上面例子中的nginx-deployment來進(jìn)一步的理解滾動更新的實(shí)現(xiàn)過程诽表。
$ kubectl create -f nginx-deployment.yaml --record
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 0 0 0 1s
--record
記錄下你每次操作所執(zhí)行的命令唉锌,以方便后面查看。
返回結(jié)果中四個字段分別對應(yīng)的含義如下:
- DESIRED:用戶期望的 Pod 副本個數(shù)(spec.replicas 的值)竿奏;
- CURRENT:當(dāng)前處于 Running 狀態(tài)的 Pod 的個數(shù)袄简;
- UP-TO-DATE:當(dāng)前處于最新版本的 Pod 的個數(shù),所謂最新版本指的是 Pod 的 Spec 部分與 Deployment 里 Pod 模板里定義的完全一致泛啸;
- AVAILABLE:當(dāng)前已經(jīng)可用的 Pod 的個數(shù)痘番,即:既是 Running 狀態(tài),又是最新版本平痰,并且已經(jīng)處于 Ready(健康檢查正確)狀態(tài)的 Pod 的個數(shù)汞舱。
下面我們通過kubectl rollout status
來實(shí)時查看資源對象的狀態(tài)變化
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
從返回結(jié)果中可以看到已經(jīng)有2個Pod進(jìn)入了UP-TO-DATE狀態(tài),稍微等待一下后查看Deployment的狀態(tài)
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 20s
可以看到3個Pod都已經(jīng)變成了AVAILABLE 狀態(tài)宗雇,然后再查看這個Deployment控制的ReplicaSet
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-3167673210 3 3 3 20s
根據(jù)上面流程我們可以看到昂芜,當(dāng)提交了創(chuàng)建Deployment請求后,Deployment Controller 就會立即創(chuàng)建一個 Pod 副本個數(shù)為 3 的 ReplicaSet赔蒲,其中ReplicaSet 的 DESIRED泌神、CURRENT 和 READY 字段的含義,和 Deployment 中是一致的舞虱。所以欢际,相比之下,Deployment 只是在 ReplicaSet 的基礎(chǔ)上矾兜,添加了 UP-TO-DATE 這個跟版本有關(guān)的狀態(tài)字段损趋。
現(xiàn)在來修改Deployment的Pod模板來觸發(fā)滾動更新
可以直接使用kubectl edit
命令或者通過修改YAML文件更新資源
$ kubectl edit deployment/nginx-deployment
...
spec:
containers:
- name: nginx
image: nginx:1.9.1 # 1.7.9 -> 1.9.1
ports:
- containerPort: 80
...
deployment.extensions/nginx-deployment edited
保存退出,Kubernetes 就會立刻觸發(fā)“滾動更新”的過程椅寺。通過 kubectl rollout status
指令查看 nginx-deployment 的狀態(tài)變化:
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.extensions/nginx-deployment successfully rolled out
可以通過查看Deployment 的 Events事件信息浑槽,看到這個“滾動更新”的流程
$ kubectl describe deployment nginx-deployment
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 1
Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 2
Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 2
Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 3
Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 0
通過事件日志我們可以清楚的看到滾動更新的具體流程即:
- Deployment Controller 使用這個修改后的 Pod 模板,創(chuàng)建一個新的ReplicaSet(hash=1764197365)返帕,這個新的 ReplicaSet 的初始 Pod 副本數(shù)是:0桐玻。
- Deployment Controller 開始將這個新的 ReplicaSet 所控制的 Pod 副本數(shù)從 0 個變成 1 個,即:“水平擴(kuò)展”出一個副本荆萤。
- Deployment Controller 又將舊的 ReplicaSet(hash=3167673210)所控制的舊 Pod 副本數(shù)減少一個镊靴,即:“水平收縮”成兩個副本。
直到舊ReplicaSet的Pod副本數(shù)為0,新ReplicaSet的副本數(shù)為3偏竟,則意味著滾動更新完成算行。完成后我們可以查看RS的狀態(tài):
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1764197365 3 3 3 6s
nginx-deployment-3167673210 0 0 0 30s
可以看到Hash為3167673210的舊ReplicaSet已經(jīng)被水平伸縮為0個副本。
將一個集群中正在運(yùn)行的多個 Pod 版本苫耸,交替地逐一升級的過程州邢,就是“滾動更新”。
2. 滾動更新的配置
為了進(jìn)一步保證服務(wù)的連續(xù)性褪子,Deployment Controller 還會確保量淌,在任何時間窗口內(nèi),只有指定比例的 Pod 處于離線狀態(tài)嫌褪。同時呀枢,它也會確保柠横,在任何時間窗口內(nèi)乔宿,只有指定比例的新 Pod 被創(chuàng)建出來。這兩個比例的值都是可以配置的梁呈,默認(rèn)都是 DESIRED 值的 25%缨伊。
所以摘刑,在上面這個 Deployment 例子中,它有 3 個 Pod 副本刻坊,那么控制器在“滾動更新”的過程中永遠(yuǎn)都會確保至少有 2 個 Pod 處于可用狀態(tài)枷恕,至多只有 4 個 Pod 同時存在于集群中。這個策略谭胚,是 Deployment 對象的一個字段徐块,名叫 RollingUpdateStrategy
,如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
...
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
在上面這個RollingUpdateStrategy
的配置中灾而,maxSurge
指定的是除了 DESIRED 數(shù)量之外胡控,在一次“滾動”中,Deployment 控制器還可以創(chuàng)建多少個新 Pod旁趟;而 maxUnavailable
指的是昼激,在一次“滾動”中,Deployment 控制器可以刪除多少個舊 Pod轻庆。這兩個配置還可以百分比形式來表示癣猾,比如:maxUnavailable=50%,指的是我們最多可以一次刪除“50%*DESIRED 數(shù)量”個 Pod余爆。
結(jié)合以上講述,現(xiàn)在我們可以擴(kuò)展一下 Deployment夸盟、ReplicaSet 和 Pod 的關(guān)系圖
如上所示蛾方,Deployment 的控制器,實(shí)際上控制的是 ReplicaSet 的數(shù)目,以及每個 ReplicaSet 的屬性桩砰。而一個應(yīng)用的版本拓春,對應(yīng)的正是一個 ReplicaSet;這個版本應(yīng)用的 Pod 數(shù)量亚隅,則由 ReplicaSet 通過它自己的控制器(ReplicaSet Controller)來保證硼莽。通過這樣的多個 ReplicaSet 對象,Kubernetes 項(xiàng)目就實(shí)現(xiàn)了對多個“應(yīng)用版本”的描述煮纵。
3. 對應(yīng)用進(jìn)行版本控制
在日常的代碼更新中懂鸵,我們都是通過版本來控制代碼的上線,但上線過程中難免會有升級失敗的情況行疏,下面我們來實(shí)踐操作下Deployment對應(yīng)用進(jìn)行版本控制的具體流程匆光,方便我們了接具體的實(shí)現(xiàn)原理。
首先我們通過kubectl set image
的指令來修改nginx-depolyment使用的鏡像酿联,把原先的鏡像替換為錯誤的鏡像终息,來模擬升級失敗。
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment.extensions/nginx-deployment image updated
由于這個 nginx:1.91 鏡像在 Docker Hub 中并不存在贞让,所以這個 Deployment 的“滾動更新”被觸發(fā)后周崭,會立刻報錯并停止。這時喳张,我們來檢查一下 ReplicaSet 的狀態(tài)休傍,如下所示:
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1764197365 2 2 2 24s
nginx-deployment-3167673210 0 0 0 35s
nginx-deployment-2156724341 2 2 0 7s
現(xiàn)在新版本的 ReplicaSet(hash=2156724341)已經(jīng)創(chuàng)建了兩個 Pod,但是它們都沒有進(jìn)入 READY 狀態(tài)蹲姐。這當(dāng)然是因?yàn)檫@兩個 Pod 都拉取不到有效的鏡像磨取。與此同時,舊版本的 ReplicaSet(hash=1764197365)的“水平收縮”柴墩,也自動停止了忙厌。此時,已經(jīng)有一個舊 Pod 被刪除江咳,還剩下兩個舊 Pod逢净。
現(xiàn)在我們可以通過kubectl rollout undo
命令將Deployment回滾到上一個版本。
$ kubectl rollout undo deployment/nginx-deployment
deployment.extensions/nginx-deployment
如果我們需要回滾到指定版本的話歼指,可以通過kubectl rollout history
來查看歷史變更的版本爹土,這是因?yàn)榍懊嫖覀儎?chuàng)建的時候添加--record
參數(shù),所以都會被記錄下來踩身。
$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl create -f nginx-deployment.yaml --record
2 kubectl edit deployment/nginx-deployment
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.91
可以看到變更次數(shù)為三次胀茵,失敗的則為版本3,現(xiàn)在我們通過--to-revision=2
指定回滾到版本2挟阻。
$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment
當(dāng)發(fā)布的版本次數(shù)過多時琼娘,Deployment同樣會創(chuàng)建很多的ReplicaSet峭弟,我們可以通過修改 spec.revisionHistoryLimit
參數(shù),來控制 Kubernetes 為 Deployment 保留的“歷史版本”個數(shù)脱拼。
總結(jié):現(xiàn)在我們通過實(shí)踐知道了kubernetes設(shè)計(jì)思想實(shí)際是通過一個個控制器來具體實(shí)現(xiàn)的瞒瘸,也了解了Deployment 這個 Kubernetes 項(xiàng)目中最基本的編排控制器的實(shí)現(xiàn)原理和使用方法。Deployment 實(shí)際上是一個兩層控制器熄浓。首先情臭,它通過 ReplicaSet 的個數(shù)來描述應(yīng)用的版本;然后赌蔑,它再通過 ReplicaSet 的屬性(比如 replicas 的值)俯在,來保證 Pod 的副本數(shù)量。
上篇文章:k8s四 | 深入理解Pod資源對象
系列文章:深入理解Kuerneters
參考資料:深入剖析Kubernetes-張磊