今天要介紹的是 Kubernetes 三兄弟的 Deployment
,這個資源對象為 Pod
和 ReplicaSet
兩者提供了一個聲明式(declarative)定義的方法來達到使用者所期望的容器執行狀態,並且官方建議透過 Deployment 來佈署 Pod
和 ReplicaSet
,典型的應用場景包括:
Deployment
Pod
的介紹相信大家已經都不陌生了,但這邊怎麼又冒出一個 ReplicaSet
呢? ReplicaSet
是用來確保在資源允許的前提下,指定的 pod 的數量會跟使用者期望的一致,也就是所謂的 desired status
,而官方建議 ReplicaSet
要搭配 Deployment
一起來使用是因為 Deployment
是個更上層的抽象概念,也支援了更多好用的功能,因此官方才會建議不要單獨使用 ReplicaSet
,而是使用 Deployment
並且將其相關資訊設定在裡面。
從下圖可以看出三者在 Kubernetes 中的對應關係:
官方貼心的為我們提供了幾個經典的 Deployment
使用案例:
接下來我們將用以上情境來實戰演練一下~
// deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-deployment
labels:
type: demo
spec:
replicas: 1
selector:
matchLabels:
type: demo
template:
metadata:
labels:
type: demo
spec:
containers:
- name: foo
image: mikehsu0618/foo
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar-deployment
labels:
type: demo
spec:
replicas: 1
selector:
matchLabels:
type: demo
template:
metadata:
labels:
type: demo
spec:
containers:
- name: bar
image: mikehsu0618/bar
ports:
- containerPort: 8080
kind
: kind 選擇為 Deployment
spec.replicas
: 被選擇套用的 Container 需要產生多少個 Pod,也是我們實現水平擴展的關鍵。spec.selector.matchLabels
: 這裡就是寫入需要套用此 Deployment
的 Template Labels
,所以兩者必須相同。spec.template.metadata.labels
: 設定 template.spec
的 Lables
。spec.template.spec.containers
: 這裡就是我們熟悉的 Pod 相關設定。接著讓我們運行設定(設定檔沒有錯誤則可以如預期中的建立):
kubectl apply -f ./deployment.yaml
--------------------
deployment.apps/foo-deployment created
deployment.apps/bar-deployment created
使用指令確認一下:
kubectl get all
--------------------
NAME READY STATUS RESTARTS AGE
pod/bar-deployment-75bcfbd655-g5gwm 1/1 Running 0 5m59s
pod/foo-deployment-6bbf665b47-kfvxr 1/1 Running 0 5m59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/bar-deployment 1/1 1 1 5m59s
deployment.apps/foo-deployment 1/1 1 1 5m59s
NAME DESIRED CURRENT READY AGE
replicaset.apps/bar-deployment-75bcfbd655 1 1 1 5m59s
replicaset.apps/foo-deployment-6bbf665b47 1 1 1 5m59s
看到我們成功的運行起了 foo
bar
兩個 Pod,並且建立了各自的 Deployment
ReplicaSet
。
接下來我們使用來使用不同的方法更新已經運行起來的 Deployment
。
直接修改原有的設定檔:
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-deployment
labels:
type: demo
spec:
// 這裡我們將 Pod 擴展成兩個!
===================
replicas: 2
===================
selector:
matchLabels:
type: demo
template:
metadata:
labels:
type: demo
spec:
containers:
- name: foo
image: mikehsu0618/foo
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar-deployment
labels:
type: demo
spec:
replicas: 1
selector:
matchLabels:
type: demo
template:
metadata:
labels:
type: demo
spec:
containers:
- name: bar
image: mikehsu0618/bar
ports:
- containerPort: 8080
修改完後再執行一次 apply
指令,kubectl 會檢查指定設定檔是否有更新:
kubectl apply -f ./deployment.yaml --record // --record 可以紀錄 rollout 歷史變更指令
-------------------------
deployment.apps/foo-deployment configured // 有更新
deployment.apps/bar-deployment unchanged // 未檢查到更新
接著可以 kubectl rolloout status
查看我們對 foo-deployment
的資源管理狀態:
kubectl rollout status deployment foo-deployment
-------------------------
deployment "foo-deployment" successfully rolled out
當指令顯示成功,即代表剛剛的更新已經正式生效~,但只要遇到設定錯誤或者是無法實現的請求時, rollout status
將會持續等待至 timeout。
我們也可以使用第二個方法「指令更新」來調整 Deployment
:
kubectl scale deployment bar-deployment --replicas 3
而第三個方法為直接編輯在 Kubernetes 運行中的 Deployment
設定:
// 打開 commmand 編輯面板,直接修改設定
kubectl edit deploy bar-deployment
來使用 get all
確認看看吧。
kubectl get all
-------------------------
NAME READY STATUS RESTARTS AGE
pod/bar-deployment-75bcfbd655-75qcd 1/1 Running 0 31s
pod/bar-deployment-75bcfbd655-c5h9w 1/1 Running 0 31s
pod/bar-deployment-75bcfbd655-g5gwm 1/1 Running 0 5h52m
pod/foo-deployment-6bbf665b47-45c2k 1/1 Running 0 4h7m
pod/foo-deployment-6bbf665b47-kfvxr 1/1 Running 0 5h52m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/bar-deployment 3/3 3 3 5h52m
deployment.apps/foo-deployment 2/2 2 2 5h52m
NAME DESIRED CURRENT READY AGE
replicaset.apps/bar-deployment-75bcfbd655 3 3 3 5h52m
replicaset.apps/foo-deployment-6bbf665b47 2 2 2 5h52m
我們在返回結果中可以看到 pod/bar-deployment
已經預期的啟動三個,並且 RepolicaSet
和 Deployment
也更新了對應狀態。
在我們更新 Deployment
時,Kubernetes 會產生一個 Deployment Revision
,可以很簡單的理解為是更新歷史版本,但要注意的是 不是每一次的更新都會產生 Revision
,只有在 Deployment created
以及 spec.template
範圍下的設定有更新才會產生,所以我們上面更新的 replicas=3
並不會出現在歷史中。
讓我們改動 spec.template
來實驗看看:
// deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-deployment
labels:
type: demo
spec:
replicas: 2
selector:
matchLabels:
type: demo
template:
metadata:
labels:
type: demo
spec:
containers:
- name: foo
image: mikehsu0618/foo
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar-deployment
labels:
type: demo
spec:
replicas: 3
selector:
matchLabels:
type: demo
// 只有在 spec.template 下的改定才會紀錄在 rollout history 中
template:
metadata:
labels:
type: demo
spec:
containers:
- name: bar
// 將我們的 image tag 版號改成不存在 `v1`
===================================
image: mikehsu0618/bar:v1
===================================
ports:
- containerPort: 8080
更新 Deployment
設定檔並使用 --record
來紀錄指令:
kubectl apply -f deployment.yaml --record
-------------------------
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/foo-deployment configured
deployment.apps/bar-deployment configured
原本的 spec.template
雖然會被紀錄在 rollout history 中,但不會有額外資訊,--record 可以讓 Kubernetes 幫我們記下我們當下改變設定的那個指令。
這時我們就能在 rollout history
查看產生出來的 revision
:
kubectl rollout history deployment bar-deployment
--------------------------
REVISION CHANGE-CAUSE
1 <none>
2 kubectl apply --filename=deployment.yaml --record=true
第一個版本為先前 Deployment
被建立時且沒有輸入 --record
的版本,第二個版本為我們調整 bar image=mikehsu0618/bar:v1
且有 --record
的版本。
指定 revision
並查看詳細資訊:
kubectl rollout history deployment bar-deployment --revision=2
--------------------------
deployment.apps/bar-deployment with revision #2ment --revision=2
Pod Template:
Labels: pod-template-hash=864b65d8b6
type=demo
Annotations: kubernetes.io/change-cause: kubectl apply --filename=deployment.yaml --record=true
Containers:
bar:
Image: mikehsu0618/bar:v1
Port: 8080/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
接下來一樣是使用 get all
指令查看容器狀況:
kubectl get all
--------------------------
NAME READY STATUS RESTARTS AGE
pod/bar-deployment-75bcfbd655-5b9z5 1/1 Running 0 7m5s
pod/bar-deployment-75bcfbd655-6whzr 1/1 Running 0 7m5s
pod/bar-deployment-75bcfbd655-zk88d 1/1 Running 0 7m5s
pod/bar-deployment-864b65d8b6-wdhz9 0/1 ImagePullBackOff 0 6m34s
pod/foo-deployment-6bbf665b47-dhndq 1/1 Running 0 40m
pod/foo-deployment-6bbf665b47-pnjfs 1/1 Running 0 40m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/bar-deployment 3/3 1 3 7m6s
deployment.apps/foo-deployment 2/2 2 2 40m
NAME DESIRED CURRENT READY AGE
replicaset.apps/bar-deployment-75bcfbd655 3 3 3 7m6s
replicaset.apps/bar-deployment-864b65d8b6 1 1 0 6m34s
replicaset.apps/foo-deployment-6bbf665b47 2 2 2 40m
這時我們會發現我們的 pod/bar-deployment
發生了 ImagePullBackOff
,原因是我們並沒有建立
mikehsu0618/bar:v1
的 image ,這種情況很好的提供我們一個因為 推進到一個不穩定的版本
而需要使用 版本回滾
先復原服務到上一個正常的版本。
使用 rollout
的回滾指令復原先前版本設定:
// 回滾至上個版本
kubectl rollout undo deployment bar-deployment --record
// 回滾至指定版本
kubectl rollout undo deployment bar-deployment --to-revision=1 --record
----------------------------
deployment.apps/bar-deployment rolled back
這時 Deployment 已經回到了,沒有出問題的 revision=1
版本了
kubectl get all
----------------------------
NAME READY STATUS RESTARTS AGE
pod/bar-deployment-75bcfbd655-5b9z5 1/1 Running 0 17m
pod/bar-deployment-75bcfbd655-6whzr 1/1 Running 0 17m
pod/bar-deployment-75bcfbd655-zk88d 1/1 Running 0 17m
pod/foo-deployment-6bbf665b47-dhndq 1/1 Running 0 50m
pod/foo-deployment-6bbf665b47-pnjfs 1/1 Running 0 50m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23d
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/bar-deployment 3/3 3 3 17m
deployment.apps/foo-deployment 2/2 2 2 50m
NAME DESIRED CURRENT READY AGE
replicaset.apps/bar-deployment-75bcfbd655 3 3 3 17m
replicaset.apps/bar-deployment-864b65d8b6 0 0 0 16m
replicaset.apps/foo-deployment-6bbf665b47 2 2 2 50m
我們上面大致練習了幾個比較實用的方式,可以發現 Deployment
的設計非常的彈性以及簡潔,並且讓我們能將 Pod
設定在一起,大大的減少設定檔的數量。而 Deployment
因為可以簡單的設定 水平擴展
資源限制與請求
等操作,使得許多進階觀念 藍綠佈署
金絲雀佈署
得以更有可能的被一般的後端工程師實現(真是謝天謝地 wwww)。
千呼萬喚始出來!鐵人賽系列「從異世界歸來發現只剩自己不會 Kubernetes」同名改編作品出版了!
感謝所有交流指教的各路英雄,也感謝願意點閱文章的各位,如果能幫助到任何人都將會是我的榮幸。
本書內容改編自第 14 屆 iThome 鐵人賽 DevOps 組的優選系列文章《從異世界歸來發現只剩自己不會 Kubernetes》。此書是一本綜合性的指南,針對想要探索認識 Kubernetes 的技術人員而生。無論是初涉此領域的新手,還是已有深厚經驗的資深工程師,本書都能提供你所需的知識和技能。
「這本書不僅提供了豐富的範例程式碼和操作指南,讓身為工程師的我們能實際操作來加深認知;更重要的是,它教會我如何從後端工程師的角度去思考和應用 Kubernetes。從容器的生命週期、資源管理到部署管理,每一章都與我們的日常開發工作息息相關。」
──── 雷N │ 後端工程師 / iThome 鐵人賽戰友
天瓏連結: 從異世界歸來發現只剩自己不會 Kubernetes:初心者進入雲端世界的實戰攻略!
相關文章:
相關程式碼同時收錄在:
https://github.com/MikeHsu0618/2022-ithelp/tree/master/Day8
Reference
Kubernetes 教學系列 - 滾動更新就用 Deployment
Kubernetes Documentation-Deployment
[Kubernetes] Deployment Overview
Kubernetes 基礎教學(二)實作範例:Pod、Service、Deployment、Ingress
請問在創建Deployment的deployment.yaml檔案裏,沒有輸入replicaset,為什麼它會產生replicaset.apps/bar-deployment-75bcfbd655 和 replicaset.apps/foo-deployment-6bbf665b47,是否在deployment.yaml檔案裏輸入了spec.replicas: 1,它就會自動生成replicaset.apps/bar-deployment-75bcfbd655 和 replicaset.apps/foo-deployment-6bbf665b47?
沒錯 使用 deployment 代表 Kubernetes 已經幫你對 replicaset 做出抽象管理了,目前正常是不太需要設定到 replicaset 這個 workload 的