更新應用程序
用戶期望應用程序始終可用,並且希望開發人員每天部署新版本。在 Kubernetes 上通過滾動更新(Rolling updates)達成。Rolling updates允許通過使用新的 Pods 實例逐個更新來實現零停機的部署更新。新的 Pods 會被调度到可用資源的 Node 節點上。
我們將應用程序擴展為運行多個實例。這也是執行更新但不影響應用可用性所需的條件。默認情况下,更新期間最大數量的不可用 Pod 以及最大數量的新 Pod 是一。 這兩個選項可以配置為數字或百分比(Pods)。 在 Kubernetes 中,更新已版本化,任何部署更新都可以恢復到以前的 (穩定) 版本。
DEPLOYMENT
在新版的 Kubernetes 官方推薦使用Deployment
來取代Replication Controller(rc)
,兩者間主要相同點包括確保處在服務狀態的 Pod 數量(replicas)能滿足先前所設定的值以及支援滾動升級(Rolling update),前者額外支援回滾(Roll back)的機制,因此接下來會介紹如何利用Deployment
來進行滾動升級。
PODS、REPLICA SETS、DEPLOYMENT 三者關係圖
從圖中可以看到一個Deployment
掌管一或多個Replica Set
,而一個Replica Set
掌管一或多個Pod
。
讓 Deployment 控管多個 Replica Set 的主要原因在於支援回滾機制 (Rollback)
。每當部署新的 Deployment 設定時,Kubernetes 會依據新設定重新生成一個 Replica Set 並保留舊設定,未來有需要的話就能直接利用舊的 Replica Set 回滾至先前狀態。必須注意的是同時間只會有一個 Replica Set 生效 (DESIRED > 0)。
HANDS-ON
首先我們利用下面的 deployment yaml 檔nginx.yaml
來建立一個需要 3 個 pods 的 deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
template:
metadata:
labels:
service: http-server
spec:
containers:
- name: nginx
image: nginx:1.10.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
你可以使用kubectl create
或kubectl apply
去建立 nginx deployment
$ kubectl create -f nginx.yaml
deployment "nginx" created
透過 kubectl 可以看到目前該 nginx deployment 的部署情況
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx 3 3 3 3 8m
從 Deployment、Replica Set、Pod 的命名可以看出之前所說的這三者間的階層關係
└─ Deployment: <name>
└─ Replica Set: <name>-<rs>
└─ Pod: <name>-<rs>-<randomString>
Kubernetes 自動產生的 replica set
$ kubectl get rs
NAME DESIRED CURRENT AGE
nginx-3322722759 3 3 8m
由 replica set 產生的 pod
$ kubectl get pod -l "service in (http-server)"
NAME READY STATUS RESTARTS AGE
nginx-3322722759-7vp34 1/1 Running 0 14m
nginx-3322722759-ip5w2 1/1 Running 0 14m
nginx-3322722759-q97b7 1/1 Running 0 14m
滾動升級 (ROLLING UPDATE)
為了讓 Kubernetes 能夠按照我們所想的方式來進行滾動升級,首先我們必須在剛剛的 yaml 檔內的spec
加入相關升級策略設定
minReadySeconds: 5
strategy:
# indicate which strategy we want for rolling update
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
- minReadySeconds:
- 容器內應用程式的啟動時間,Kubernetes 會等待設定的時間後才繼續進行升級流程
- 如果沒有此欄位的話,Kubernetes 會假設該容器一開完後即可進行服務
- 若未設定此欄位,在某些極端情況下可能會造成服務無法正常運作(新誕生的 pod 尚未進入可服務階段)
- maxSurge:
- 升級過程中最多可以比原先設定所多出的 pod 數量
- 此欄位可以為固定值或是比例(%)
- ex. maxSurge: 1、replicas: 5,代表 Kubernetes 會先開好 1 個新 pod 後才刪掉一個舊的 pod,整個升級過程中最多會有 5+1 個 pod
- maxUnavailable:
- 最多可以有幾個 pod 處在無法服務的狀態
- 當
maxSurge
不為零時,此欄位亦不可為零 - ex. maxUnavailable: 1,代表 Kubernetes 整個升級過程中最多會有 1 個 pod 處在無法服務的狀態
加完相關設定後nginx.yaml
會長下面這個樣子
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-test
spec:
replicas: 10
selector:
matchLabels:
service: http-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
service: http-server
spec:
containers:
- name: nginx
image: nginx:1.10.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
apply 新的 nginx.yaml
$ kubectl apply -f nginx.yaml --record
接下來介紹三種方式來進行滾動升級 (以升級 docker image 為例)
set image
# format
$ kubectl set image deployment <deployment> <container>=<image> --record
# example
$ kubectl set image deployment nginx nginx=nginx:1.11.5 --record
replace
修改 nginx.yaml 內的 image 版本
spec:
containers:
- name: nginx
# newer image version
image: nginx:1.11.5
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
利用replace
來進行升級而非 apply
# format
$ kubectl replace -f <yaml> --record
# example
$ kubectl replace -f new-nginx.yaml --record
edit
# format
$ kubectl edit deployment <deployment> --record
# example
$ kubectl edit deployment nginx --record
這指令會直接打開編輯器的視窗,讓我們來修改 deployment 內的設定值
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
kubectl.kubernetes.io/last-applied-configuration: '{"kind":"Deployment","apiVersion":"extensions/v1beta1","metadata":{"name":"nginx","creationTimestamp":null},"spec":{"replicas":10,"template":{"metadata":{"creationTimestam
...
spec:
containers:
- image: nginx:1.10.2
imagePullPolicy: IfNotPresent
name: nginx
...
查詢升級狀況
$ kubectl rollout status deployment nginx
暫停滾動升級
$ kubectl rollout pause deployment <deployment>
繼續滾動升級
$ kubectl rollout resume deployment <deployment>
回滾
當升級完後同事發現此次的升級造成服務發生不穩定的狀況,但他們不見得清楚先前的設定檔的樣子。在這種狀況下,我們可以利用 rollback 來回復到先前的狀態。
在先前的操作過程中,大家可能會注意到指令後面多一個--record
的參數,這參數主要是告知 Kubernetes 紀錄此次下達的指令,如此一來我們更能清楚不同的版本(revision)間做了什麼操作。
$ kubectl apply -f nginx.yaml --record
deployment "nginx" configured
$ kubectl set image deployment nginx nginx=nginx:1.11.5 --record
deployment "nginx" image updated
$ kubectl rollout history deployment ngin
deployments "nginx":
REVISION CHANGE-CAUSE
1 kubectl apply -f nginx.yaml --record
2 kubectl set image deployment nginx nginx=nginx:1.11.5 --record
假設我們現在要回滾到 revision 1
# to previous revision
$ kubectl rollout undo deployment <deployment>
# to specific revision
$ kubectl rollout undo deployment <deployment> --to-revision=<revision>
# exmaple
$ kubectl rollout undo deployment nginx --to-revision=1
由於每次修改的設定皆會儲存在 replica set 中,因此我們可以利用.spec.revisionHistoryLimit
來決定我們需要保留多久以前的紀錄 (此欄位需要在第一次建立 deployment 時就設定)
...
spec:
replicas: 10
selector:
matchLabels:
service: http-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
revisionHistoryLimit: 10
...
$ kubectl rollout history deployment/nginx
deployments "nginx":
REVISION CHANGE-CAUSE
2 kubectl set image deployment nginx nginx=nginx:1.11 --record
3 kubectl set image deployment nginx nginx=nginx:1.11.5 --record
4 kubectl set image deployment nginx nginx=nginx:1.10 --record
5 kubectl set image deployment nginx nginx=nginx:1.10.2 --record