k8s之Deployment 聲明式地升級應用(五)


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資源介紹完結。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM