Kubernetes 教學系列 - 滾動更新就用 Deployment

上次介紹了如何使用 Replica Set 如何對 Pods 來進行管理,但其實官方並不推薦直接使用 Replica Set 來對 Pods 操作,更推薦可以使用 Deployment 來直接操作 Pods,可以理解當我們建造 Deployment 資源後會自動一併建立好 Replica Set 及 Pods,所以我們可以透過 Deployment 直接進行管理。

Deployment 的作用

  • 透過創建 Deployment 來建立 Replica Set,Replica Set 又可以幫助管理 Pods
  • 滾動更新 Deployment 版本,例如,如果當前的 Deployment 上線的 Pod 需要更新新上線的 code 版本,可以做到 zero downtime deployment
  • 也可以 rollback 到先前版本,如果新版本的 code 上線造成不穩定或 bug 的情況。

Deployment 設定檔撰寫

直接上設定檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
spec:
selector:
matchLabels:
app: web-app
replicas: 2
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: kennychenfight/web-app:v1
ports:
- containerPort: 8080
  • kind 為 Deployment
  • spec 這邊定義的 selector 是代表要讓 Deployment 知道如何查找藥管理的 Pods,所以定義的 labels 應該要跟下面的 template 的 labels 一致才行。而這邊也可以用 Replica Set 複雜的 label 比對,例如使用 matchExpressions
  • template 這邊就是描述 Pod 的相關的定義

先幫 Application Image 打上 Version

在我們 create Deployment 前,我們需要先將 web-app image 打上 Version Tag,這樣等等才方便示範滾動更新或回退的操作。

將之前只有 latest 版本打 tag 為 v1 版本:

1
2
docker tag kennychenfight/web-app:latest kennychenfight/we
b-app:v1

之後 push 到 Docker Hub 上:

1
docker push kennychenfight/web-app:v1

建立 Deployment 資源

接著就可以來建立 Deployment 資源:

1
kubectl create -f deployment.yaml

取得 Deployment 相關資源

1
2
3
kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
web-app-deployment 2/2 2 2 44s
1
2
3
kubectl get rs
NAME DESIRED CURRENT READY AGE
web-app-deployment-747dc7c599 2 2 0 35s
1
2
3
4
kubectl get pods
NAME READY STATUS RESTARTS AGE
web-app-deployment-747dc7c599-h9g96 1/1 Running 0 104s
web-app-deployment-747dc7c599-wl448 1/1 Running 0 104s

當建立完之後就會自動建立 Deployment、Replica Set、Pods 這些資源,根據以上的指令就可以知道 Name 都是以 Deployment Metadat 的 name 為 Prefix。

如何滾動更新

滾動更新有好多種指令可以達成。我們先將目前的 web-app 版本 tag 打上 v2 版本再來做示範。

1
2
docker tag kennychenfight/web-app:latest kennychenfigh
t/web-app:v2
1
docker push kennychenfight/web-app:v2

更新 Yaml 檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
spec:
selector:
matchLabels:
app: web-app
replicas: 2
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: kennychenfight/web-app:v2
ports:
- containerPort: 8080

這邊很簡單就將 image 的 tag 更改為 v2 即可。

透過 apply 指令來更新設定:

1
kubectl apply -f deployment-update.yaml 

接著趕快用 kubectl get pods 來看看:

1
2
3
4
5
6
 kubectl get pods
NAME READY STATUS RESTARTS AGE
web-app-deployment-6699fcb49-b7b8p 1/1 Running 0 5s
web-app-deployment-6699fcb49-jbl6p 0/1 ContainerCreating 0 0s
web-app-deployment-b76b756bd-4t75m 1/1 Running 0 71s
web-app-deployment-b76b756bd-5lbvt 1/1 Terminating 0 71s

可以發現 Deployment 會幫我們將 Pods 做 Zero Down Time 更新,會一個一個將舊版本的 Pod 停掉,同時也會起一個新版本的 Pod 來運行,避免有服務完全停機而無法服務的情況。

透過 rollout status 指令可以知道目前升級的狀態:

1
2
kubectl rollout status deploy web-app-deployment
deployment "web-app-deployment" successfully rolled out

使用 set Image 指令

也可以透過 set Image 指令來做 Pod Image 的更新:

1
kubectl set image deploy/web-app-deployment web-app=kennychenfight/web-app:v2

使用 edit 指令

透過 edit deploy 指令可以做 yaml 檔案欄位的更新,此外也會多許多欄位可以做更改:

1
kubectl edit deploy web-app-deployment

這邊除了直接更改 image 的版本之外,還有幾個欄位是關於滾動更新的:

1
2
3
4
5
6
sepc:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
  • maxSurge 可以用百分比也可以用數字設定,這是代表在滾動更新的時候新版本的 Pod 可以比原本 Deployment 定義的 Replicas 多幾個 Pod 出來
  • maxUnavailable 則是在滾動更新的時候可以容忍多少 Pod 無法使用

查看 Deployment Rollout 的紀錄

1
2
3
4
5
kubectl rollout history deploy web-app-deployment
deployment.apps/web-app-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>

透過這個指令可以看到有兩次的歷史紀錄的 rollout,第一次是建立 Deployment 的時候,第二次是我們進行滾動更新的時候。而 CHANGE-CAUSE 怎麼設置的呢?可以透過以下三種方式:

  • kubectl annotate deployment.v1.apps/web-app-deployment kubernetes.io/change-cause="image updated to xxx"
  • 追加 --record 指令來保存正在更改資源的 kubectl 指令

可以來看個示範:

第一種方式可以直接修改當前 Deployment 資源的 CHANGE-CAUSE 紀錄,因此我們設定如下:

1
2
kubectl annotate deployment.v1.apps/web-app-deployment kubernetes.io/change-cause="image updated to version2"
deployment.apps/web-app-deployment annotated

再來看是否有改變:

1
2
3
4
5
kubectl rollout history deploy web-app-deployment
deployment.apps/web-app-deployment
REVISION CHANGE-CAUSE
1 <none>
2 image updated to version2

這邊可以看出有改變了。

而第二種方式的意思是類似這樣:

1
kubectl set image deploy/web-app-deployment web-app-pod=kennychenfight/web-app:v2 --record=true

或是

1
kubectl apply -f deployment-update.yaml --record=true

這樣的話會自動將更新 Deployment 資源的指令紀錄上去。

如何回滾回退

撤銷當前上線版本,回滾到上一個版本:

1
kubectl rollout undo deploy web-app-deployment

透過 rollout undo 指令即可

透過使用 --to-revision 來回滾到特定版本:

1
kubectl rollout undo deploy web-app-deployment --to-revision=1

接著透過 rollout history 指令就會發現又多了一個 REVISION:

1
2
3
4
5
kubectl rollout history deploy web-app-deployment
deployment.apps/web-app-deployment
REVISION CHANGE-CAUSE
2 image updated to version2
3 <none>

這邊會發現會將回滾版本 1 踢掉變成新的版本 3

暫停跟恢復 Deployment

如果說我們要對 Deployment 做很多設定的調整,那另外一種方式是可以選擇先將 Deployment 暫停,這個暫停是指原本的 Deployment 出來 Pods 仍然可以運作,但是我們對 Deployment 的更新會保留著,並且當我們解除暫停之後,Deployment 才會做更新的動作。

以避免當如果不小心更改到甚麼之後,就馬上進行上線的動作,可能會產生多餘的上線操作。

暫停 Deployment

1
kubectl rollout pause deploy/web-app-deployment

版本上到第二個版本

1
kubectl set image deploy/web-app-deployment web-app-pod=kennychenfight/web-app:v2 --record=true

這時來看一下 rollout status:

1
2
kubectl rollout status deploy web-app-deployment
Waiting for deployment "web-app-deployment" rollout to finish: 0 out of 2 new replicas have been updated...

可以看到還沒進行更新。必須 resume deployment 才會更新。

恢復 Deployment

1
kubectl rollout resume deploy/web-app-deployment

這時候恢復後,再看看 Pods:

1
2
3
4
5
6
kubectl get pods
NAME READY STATUS RESTARTS AGE
web-app-deployment-6699fcb49-5qmmj 1/1 Running 0 3s
web-app-deployment-6699fcb49-cxhmt 1/1 Running 0 2s
web-app-deployment-b76b756bd-52kdr 1/1 Terminating 0 14m
web-app-deployment-b76b756bd-5fsqb 0/1 Terminating 0 14m

可以看到在進行更新了~

再來看 status:

1
2
kubectl rollout status deploy web-app-deployment
deployment "web-app-deployment" successfully rolled out

成功 rolled out 了~

總結

這篇文章主要介紹了如何建立 Deployment 資源,以及常用的操作,例如如何滾動更新跟回退,這對於上線新版本是很好用的。下一篇文章會來介紹如何透過外部服務來跟 Pods 來進行連線溝通,也就是 Service 資源的建立。