Deployment 聲明式地升級應用
如今你已經知道如何將應用程序組件打包進容器,將他們分組到pod中,併爲它們提供臨時或者持久存儲,將密鑰或配置文件注入,並可使pod之間互相通訊。這就是微服務化:如何將一個大規模系統拆分紅各個獨立運行的組件。以後,你將會須要升級你的應用程序。如何升級再kubernetes集羣中運行的程序,以及kubernetes如何幫助你實現真正的零停機升級過程。升級操做能夠經過使用replicationController 或者 replicaSet實現,但Kubernetes提供了另外一種基於ReplicaSet的資源Deployment,並支持聲明式地更新應用程序。
看一個簡單的例子:
現有一個應用 ,版本爲V1 ,運行在Kubernetes的pod中,如今應用的鏡像有更新,標記爲v2,那麼如何將新版本運行的pod替換掉V1版本的pod.
有如下兩種方法更新pod:
1. 直接刪除全部現有的pod,而後建立新pod
2. 新建立一組pod,等待運行後刪除舊pod. 能夠一次性建立,一次性刪除,也能夠一部分一部分操做。
這兩種各有優缺點:第一種方法將會致使應用程序在必定的時間內不可用。第二種方法會致使新舊版本同時在線,若是對統一個數據的處理方式不同,將會給系統帶來數據損壞。
暫且無論優缺點,先來看看如何實現。
咱們用replicationController來託管pod v1,能夠直接經過將pod模版修改成v2,而後再刪除舊pod,這時候rc就會按照新模版建立pod.
若是你能夠接受短暫的服務不可用,那這是最簡單更新pod的方法。
接下來咱們用第二種方法。首先要保持兩個版本同時在線,因此要雙倍的硬件資源。
前提咱們是用service來暴露pod的。保持rc v1不變,而後建立 rc v2 ,rc v2的pod模版選擇新鏡像標籤V2. 建立rc v2 ,rc v2將運行pod v2 。等待pod v2運行正常後,咱們使用 kubectl set selector 命令來修改service的pod選擇器。
這種一次性從v1切到v2的方法即爲藍綠部署,主要問題就是硬件資源要兩倍。若是咱們不是一次切換,而是一點點切,資源將不須要兩倍。好比你建立rc v2,成功後,縮容 rc v1 ,更改service的pod選擇器。而後再擴容rc v2,在縮容 v1 這就是滾動更新。那麼這個就要求你的應用容許存在兩個不一樣版本,具體要根據實際需求來操做了。
以上第二種方法的兩種狀況,咱們大概知道是怎麼回事了。但咱們在實際操做中,要建立兩個rc,而且要修改service的pod selector. 這要是用滾動更新,pod副本越多,咱們手動操做的次數就越多,手動操做越多越容易出現問題。那麼問題來了,難道kubernetes沒有提供自動完成這些操做的方法嗎?答案是提供了。k8s中使用kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
咱們來經過一個例子瞭解下:
用rc建立一個應用v1 ,並使用service的 loadbalancer將服務對外暴露。
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia-v1
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
---
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
type: LoadBalancer
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
查看loaderbalancer的IP
#kubectl get svc kubia
訪問測試
# while true; do curl http://IP; done
this is v1 running in pod 主機名
接下來用v2版本升級,this is v2 running in pod 主機名
kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
使用 kubia v2版本應用替換運行着kubia-v1 的replicationController,將新的複製控制器命名爲kubia-v2,並使用luksa/kubia:v2做爲容器鏡像。
kubectl 經過複製kubia-v1 的replicationController並在其pod模版中改變鏡像版本。若是仔細觀察控制器的標籤選擇器,會阿賢它也被作了修改。它不只包含一個簡單的app=kubia標籤,並且還包含一個額外的deployment標籤,爲了由這個ReplicationController管理,pod必須具有這個標籤。這也是爲了不使用新的和舊的RC來管理同一組Pod.可是即便新建立的pod添加了deployment標籤,pod中仍是有app=kubia標籤,因此不只新的RC要加deployment標籤,舊的RC一樣也要加上deployment標籤,標籤的值爲 一個鏡像hash(先這樣理解)。要保證舊的RC添加deployment標籤後依然能夠管理以前建立的pod,所以也要修改舊pod,進行添加標籤,而實際上正是在修改舊RC以前,kubectl修改了舊pod的標籤:
kubectl get po --show-labels 進行查看標籤
設置完成後,就開始執行更新操做了,過程就是咱們上面描述的滾動更新過程。
爲何 kubectl rolling-update已通過時
咱們能夠在使用rolling-update命令的時候添加 --v 6 來提供日誌級別,使得全部kubectl 發起的到API服務器的請求都會被輸出。
你會看到一個put請求:
/api/v1/namespace/default/replicationcontrollers/kubia-v1
它是表示kubia-v1 ReplicationController資源的RESTful URL.這些請求減小了RC的副本數,這代表伸縮的請求是由kubectl 客戶端執行的,而不是kubernetes master執行的。那麼當kubectl執行升級時失去了網絡鏈接,升級過程就會中斷。對於中斷後的結果處理起來將很麻煩。因此咱們想盡可能把這個過程讓master負責。這就引出了DeployMent資源。
Deployment是一個高階資源,replicationController和replicaSet都被認爲是底層的概念。當建立一個Deployment時,ReplicaSet資源就會被建立,實際的pod是由Deployment的Replicaset建立和管理的,而不是由Deployment直接建立和管理的。
接下來咱們建立一個簡單的deployment,將上面的replicationController稍做修改:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
由於以前RC只維護和管理了一個特定的版本的pod,並須要命名爲kubia-v1,而一個deployment資源高於版本自己。能夠同時管理多個版本的pod,因此在命名時不須要指定應用的版本號。
kubectl create -f kubia-deployment-v1.yaml --record
使用 --record選項會記錄歷史版本號,在以後操做中很是有用。
kubectl get deployment kubia
kubectl describe deployment kubia
還有一個命令,專門用於查看部署狀態:
kubectl rollout status deployment kubia
查看pod,會發現使用deployment資源部署的pod名稱有必定的規律,
deployment的名字 + replicaset pod模版的hash + 隨機數,名稱我曾懷疑是template模版的名字 + 模版的hash+隨機數。通過驗證發現不是,甚至能夠不給模版定義name.
如:
kubia-15098375-otnsd
kubia-15098375-djc6s
而rc建立出來的名稱是 : rc名稱 + 隨機數
kubia-v1-kgysg
能夠經過查看replicaSet來確認pod模版hash
kubectl get replicasets
kubia-15098375 ...
deployment正是根據pod模版的hash值,對給定版本的pod模版建立相同的(或使用已有的)ReplicaSet.
Deployment的高明之處
有不一樣的更新策略
Recreate 策略在刪除舊的Pod以後纔開始建立新的Pod。若是不容許多個版本共存,使用這個,但會有短暫的不可用。
RollingUpdate 策略會漸進地刪除舊的pod,於此同時建立新的pod.這是deployment默認使用的策略。若是支持多個版本共存,推薦使用這個。
咱們來演示下deployment滾動升級的過程。
在演示以前咱們先減慢滾動升級的速度,以方便咱們觀察
kubectl path deployment kubia -p '{"spec": {"minReadySeconds": 10} }'
使用path對於修改單個或少許資源屬性很是有用,不須要在經過編輯器編輯,使用minReadySeconds 配置正常檢查後多少時間才屬於正常。
注:使用patch命令更改Deployment的自有屬性,並不會致使pod的任何更新,由於pod模版並無被修改。更改其餘Deployment的屬性,好比所須要的副本數或部署策略,也不會觸發滾動升級,現有運行中的pod也不會受影響。
觸發滾動升級
kubectl set image deployment kubia nodejs=luksa/kubia:v2
執行完成後pod模版的鏡像會被更改成luksa/kubia:v2
deployment的優勢
使用控制器接管整個升級過程,不用擔憂網絡中斷。
僅僅更改pod模版便可。
注:若是Deployment中的pod模版引用了一個ConfigMap(或secret),那麼更改ConfigMap字眼自己將不會觸發升級操做。若是真的須要修改應用程序的配置並想觸發更新的話,能夠經過建立一個新的ConfigMap並修改pod模版引用新的ConfigMap.
Deployment背後完成的升級過程和kubectl rolling-update命令類似。
一個新的ReplicaSet會被建立而後慢慢擴容,同時以前版本的Replicaset會慢慢縮容至0
deployment的另一個好處是能夠回滾
kubectl rollout undo deployment kubia
deployment會被回滾到上一個版本
undo命令也能夠在滾動升級過程當中運行,並直接中止滾動升級。在升級過程當中一建立的pod會被刪除並被老版本的pod替代。
顯示deployment的滾動升級歷史
kubectl rollout history deployment kubia
reversion change-cause
2 kubectl set image deployment kubia nodejs=luksa/kubia:v2
3 kubectl set image deployment kubia nodejs=luksa/kubia:v3
還記得建立Deployment的時候--record 參數嗎?若是不給這個參數,版本歷史中的Change-cause這一欄會空。這也會使用戶很難辨別每次的版本作了哪些修改。
回滾到一個特定的Deployment版本
kubectl rollout undo deployment kubia --to-reversion=1
這些歷史版本的replicaset都用特定的版本號保存Deployment的完整的信息,因此不該該手動刪除ReplicaSet。若是刪除會丟失Deployment的歷史版本記錄而致使沒法回滾。
ReplicaSet默認保留最近的兩個版本,能夠經過制定Deployment的reversionHistoryLimit屬性來限制歷史版本數量。apps/v1beta2 版本的Deployment默認值爲10.
控制滾動更新的速率
maxSurge 最多超過時望副本數多少個pod,默認爲25%,能夠設置爲整數
maxUanavailable 最多容許指望副本數內多少個pod爲不可用狀態。
看圖比較容易理解
暫停滾動更新:
kubectl rollout pause deployment kubia
恢復滾動更新:
kubectl rollout resume deployment kubia
阻止出錯版本的滾動升級
使用minReadySeconds屬性指定至少要成功運行多久以後,才能將其視爲可用。在pod可用以前,滾動升級的過程不會繼續。是由maxSurge屬性和maxUanavailable屬性決定。
例子:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas:3
minReadySeconds: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavilable: 0
type: RollingUpdate
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v3
name: nodejs
readinessProbe:
periodSeconds: 1 1秒探測一次
httpGet:
path: /
port: 8080
kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
kubectl rollout status deployment kubia
就緒探針是如何阻止出錯版本的滾動升級的
首先luksa/kubia:v3鏡像應用前5個應用是正常反饋,後面會500狀態返回。所以pod會從service的endpoint中移除。當執行curl時,pod已經被標記爲未就緒。這就解釋了爲何請求不會到新的pod上了。
使用kubectl rollout status deployment kubia查看狀體,發現只顯示一個新副本啓動,以後滾動升級便沒有進行下去,由於新的pod一直處於不可用狀態。即便變爲就緒狀態後,也至少須要保持10秒,纔是真正可用。在這以前滾動升級過程將再也不建立任何新的pod,由於當前maxUnavailable屬性設置爲0,因此不會刪除任何原始的pod。若是沒有定義minReadySeconds,一旦有一次就緒探針調用成功,便會認爲新的pod已經處於可用狀態。所以最好適當的設置minReadySeconds.另外就緒探針默認間隔爲1秒。
deployment資源介紹完結。