Kubernetes 使用RollingUpdate佈署策略, 以nginx為例

Posted by Kubeguts on 2020-10-19

Step 1 運行基本環境

準備用來部署的yaml檔案如下,為設置 4個持續運行nginx的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: my-nginx
spec:
replicas: 4
minReadySeconds: 1 # Default 0
progressDeadlineSeconds: 60 # Default 600s
revisionHistoryLimit: 5 #Default 10
strategy:
type: RollingUpdate # This is the default
rollingUpdate:
maxSurge: 1 #Default 25%
maxUnavailable: 1 # Default 25%
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: my-nginx
image: nginx:1.16.1-alpine #nginx:1.17.8-alpine
ports:
- containerPort: 80
resources:
limits:
memory: "128Mi" #128 MB
cpu: "200m" #200 millicpu (.2 cpu or 20% of the cpu)

定義nginx的service

nginx.service.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
kind: Service
apiVersion: v1
metadata:
name: nginx-service
labels:
app: my-nginx
spec:
type: LoadBalancer
selector:
app: my-nginx
ports:
- port: 80
targetPort: 80

接著執行運行指令

1
2
3
4
5
6
// 透過deployment創建具備4個container的nginx服務
$ kubectl create -f nginx.deployment.yml --save-config --record

// 定義負載平衡與流量管理
$ kubectl create -f nginx.service.yml --save-config --record
service/nginx-service created

--record 可以紀錄rollout的過程紀錄,方便後續執行rolling back動作

可以看到4個nginx的容器正在同一個pod運行中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-nginx-75bbd45cb9-2k7gg 1/1 Running 0 15m
pod/my-nginx-75bbd45cb9-4j5zn 1/1 Running 0 15m
pod/my-nginx-75bbd45cb9-5glbj 1/1 Running 0 15m
pod/my-nginx-75bbd45cb9-nhxpx 1/1 Running 0 15m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d11h
service/nginx-service LoadBalancer 10.104.214.7 localhost 80:31533/TCP 4m25s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-nginx 4/4 4 4 15m

NAME DESIRED CURRENT READY AGE
replicaset.apps/my-nginx-75bbd45cb9 4 4 4 15m

這時準備一個測試是否是可執行 zero downtime的腳本

確認nginx服務是否能夠回傳首頁

1
2
3
4
5
for ((i=1;i<=100;i++)); 
do
curl -s "http://localhost" | grep "<title>.*</title>"
sleep 2s
done

這時可看到nginx服務正在運作中

1
2
3
4
5
$ bash curl-loop.sh 
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>

Step 2. 更換版本並移轉

若要更換 nginx版本 從 nginx:1.16.1-alpinenginx:1.17.8-alpine

更改nginx.deployment.yml檔案,並使用 kubectl apply -f nginx.deployment.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: my-nginx
spec:
replicas: 4
minReadySeconds: 1 # Default 0
progressDeadlineSeconds: 60 # Default 600s
revisionHistoryLimit: 5 #Default 10
strategy:
type: RollingUpdate # This is the default
rollingUpdate:
maxSurge: 1 #Default 25%
maxUnavailable: 1 # Default 25%
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: my-nginx
image: nginx:1.17.8-alpine (更換成新版的nginx)

...

接著快速的執行 kubectl get all 查看目前Rolling Update狀況

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-nginx-6b8f7cd7cc-2vk7g 0/1 ContainerCreating 0 6s
pod/my-nginx-6b8f7cd7cc-596cv 0/1 ContainerCreating 0 5s
pod/my-nginx-75bbd45cb9-2k7gg 1/1 Running 0 36h
pod/my-nginx-75bbd45cb9-4j5zn 0/1 Terminating 0 36h
pod/my-nginx-75bbd45cb9-5glbj 1/1 Running 0 36h
pod/my-nginx-75bbd45cb9-nhxpx 1/1 Running 0 36h

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d22h
service/nginx-service LoadBalancer 10.104.214.7 localhost 80:31533/TCP 35h

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-nginx 3/4 2 3 36h

NAME DESIRED CURRENT READY AGE
replicaset.apps/my-nginx-6b8f7cd7cc 2 2 0 6s
replicaset.apps/my-nginx-75bbd45cb9 3 3 3 36h

再過一會兒,可看到目前的Pod一樣維持4個nginx服務

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-nginx-6b8f7cd7cc-2vk7g 1/1 Running 0 56s
pod/my-nginx-6b8f7cd7cc-596cv 1/1 Running 0 55s
pod/my-nginx-6b8f7cd7cc-5wn5d 1/1 Running 0 41s
pod/my-nginx-6b8f7cd7cc-j9zzk 1/1 Running 0 36s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d22h
service/nginx-service LoadBalancer 10.104.214.7 localhost 80:31533/TCP 35h

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-nginx 4/4 4 4 36h

NAME DESIRED CURRENT READY AGE
replicaset.apps/my-nginx-6b8f7cd7cc 4 4 4 56s
replicaset.apps/my-nginx-75bbd45cb9 0 0 0 36h

可看到版本移轉完畢後有兩個replicaSets

1
2
3
NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/my-nginx-6b8f7cd7cc 4 4 4 56s
replicaset.apps/my-nginx-75bbd45cb9 0 0 0 36h

Step 3:查看移轉狀態

使用 kubectl rollout status deployment <name> 查看rollout狀態

1
2
$ kubectl rollout status deployment my-nginx
deployment "my-nginx" successfully rolled out

再次查看 curl-loop.sh 腳本的運作,可看到移轉過程中,都沒有中斷!

1
2
3
4
5
6
7
8
9
10
$ bash curl-loop.sh

<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
...
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>
<title>Welcome to nginx!</title>

Step 4:執行回滾 (rollback)

使用 kubectl rollout history deployment [name] 查看移轉歷史紀錄

查看一下移轉後的歷史紀錄revision=2的內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kubectl rollout history deployment my-nginx --revision=2

deployment.apps/my-nginx with revision #2
Pod Template:
Labels: app=my-nginx
pod-template-hash=6b8f7cd7cc
Annotations: kubernetes.io/change-cause: kubectl create --filename=nginx.deployment.yml --save-config=true --record=true
Containers:
my-nginx:
Image: nginx:1.17.8-alpine # 移轉後版本
Port: 80/TCP
Host Port: 0/TCP
Limits:
cpu: 200m
memory: 128Mi
Environment: <none>
Mounts: <none>
Volumes: <none>

若要回滾到先前的版本,使用 kubectl rollout undo deployment my-nginx --to-revision=1

1
2
$ kubectl rollout undo deployment my-nginx --to-revision=1
deployment.apps/my-nginx rolled back

再次查看history,可看到revision為3的回滾紀錄

1
2
3
4
5
6
$ kubectl rollout history deployment my-nginx

deployment.apps/my-nginx
REVISION CHANGE-CAUSE
2 kubectl create --filename=nginx.deployment.yml --save-config=true --record=true
3 kubectl create --filename=nginx.deployment.yml --save-config=true --record=true

回滾完畢後,使用 kubectl describe deploy [name] 查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ kubectl describe deploy my-nginx

Name: my-nginx
Namespace: default
CreationTimestamp: Thu, 29 Oct 2020 22:10:47 +0800
Labels: app=my-nginx
Annotations: deployment.kubernetes.io/revision: 3
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"my-nginx"},"name":"my-nginx","namespace":"defaul...
kubernetes.io/change-cause: kubectl create --filename=nginx.deployment.yml --save-config=true --record=true
Selector: app=my-nginx
Replicas: 4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 1
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: app=my-nginx
Containers:
my-nginx:
Image: nginx:1.16.1-alpine # 可看到已經回滾為原本的版本!
Port: 80/TCP
Host Port: 0/TCP
Limits:
cpu: 200m
memory: 128Mi
Environment: <none>
Mounts: <none>
Volumes: <none>

...

參考

Pluralsight: Kubernetes for Developers: Deploying Your Code
https://app.pluralsight.com/library/courses/kubernetes-developers-deploying-code/table-of-contents