前幾天看了形形色色的干擾類型,今天來實做看看 pdb 並且理解用法吧
https://kubernetes.io/docs/tasks/run-application/configure-pdb/
PodDisruptionBudget
(PDB) 是用來確保 pods 同時存在一定數量,以確保 app 可以持續提供服務不停機
PDB 可以配置於以下 k8s 內建的 controller:
在配置 pdb 時,確保上述 controller 的 .spec.selector
是與 pdb 的 .spec.selector
一致
在配置 PDB 之前,需要依據 app 的型態考慮情況:
minAvailable
or maxUnavailable
這兩個參數可以被配置為整數或是百分比
根據以上內容,PDB 主要的三個參數為:
.spec.selector
:指定對應的 controller / pods.spec.minAvailable
:確保最少可用的數量。若設定為百分比,則會向上取整.spec.maxUnavailable
:最多不可用的數量。若設定為百分比,則會向上取整
maxUnavailable
與 minAvailable
只能配置其中一種配置上的幾種例子:
通常一個 PDB 就對應到一個 pods controller,不太會混用(混用管理也蠻困難的)。
另外一個特殊情況是 minAvailable=100%
或 maxUnavailable=0%
時,表示此 pods 永遠無法被驅逐,kubectl drain node 指令也會卡死,這在 PDB 的語法上是合法的
補充:cluster admin 在執行 drain node 的動作時,最好先檢查過所有 PDB 的配置,並且在 kubectl drain 指令加上 —timeout 的參數,以避免指令永遠卡死 (尤其在配置自動化腳本時 e.g. ansible-playbook,可能會直接導致 play 卡住然後又看不到輸出訊息無法 debug)
minAvailable 範例如下:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: zookeeper
maxUnavailable 範例如下:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: zookeeper
當 app=zookeeper 的 controller replicas 配置為 3 時,此兩個配置是等價的。
可以透過 kubectl get
指令確認 PDB 的狀態:
kubectl get poddisruptionbudgets
# 當 replicas <3
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
zk-pdb 2 N/A 0 7s
# 當 replicas = 3
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
zk-pdb 2 N/A 1 7s
主要就看 ALLOWED DISRUPTIONS
欄位,就可以知道目前還能夠允許幾個 pods 被驅逐了。
快速建立一個 nginx app, 並且配置 PDB 之後,drain node 看看行為
建立一個 replicas 為 7 的 deployment:
# 建立的 deployment 會有 app: pdb-deployment 的 labels
❯ kubectl create deployment --replicas=7 pdb-deployment --image=nginx
根據上述 maxUnavailable 的範例,建立一個 PDB:
# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: pdb-deployment
spec:
maxUnavailable: 70%
selector:
matchLabels:
app: pdb-deployment
❯ k apply -f pdb.yaml
❯ k get -f pdb.yaml
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
pdb-deployment N/A 70% 5 44s
可以發現在 replicas:7 且設定為 max=70% 時,算法為 7*70%=4.9,並向上取整為 5 了
測試看看 min 的配置,改為 80%:
...
spec:
minAvailable: 80%
...
#
❯ k apply -f pdb.yaml
❯ k get -f pdb.yaml
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
pdb-deployment 80% N/A 1 7m22s
7 * 80% = 5.6,向上取整為 6,7-6 = 1 ,因此最多可以 1 個 pods 不健康
接下來我們測試看看,若是直接使用 kubectl delete
刪除所有的 pods 是否會觸發 PDB?
❯ k delete pods -l app=pdb-deployment
pod "pdb-deployment-845f8bdc8f-2xsq7" deleted
pod "pdb-deployment-845f8bdc8f-8xllz" deleted
pod "pdb-deployment-845f8bdc8f-d9wrk" deleted
pod "pdb-deployment-845f8bdc8f-ldwzt" deleted
pod "pdb-deployment-845f8bdc8f-ndsdx" deleted
pod "pdb-deployment-845f8bdc8f-q57ws" deleted
pod "pdb-deployment-845f8bdc8f-wh9ll" deleted
可以看到 PDB 並不會阻擋 user 下的 delete 命令,這在前幾天有討論到,delete 並不是自願干擾的一種,透過 eviction API 的才是自願干擾!
接著讓我們試試看 drain node 的行爲:
# 找到有兩個 pods 以上的 node
❯ k get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pdb-deployment-845f8bdc8f-c72jq 1/1 Running 0 2m7s 10.0.0.226 k8s-master1 <none> <none>
pdb-deployment-845f8bdc8f-gpmjw 1/1 Running 0 2m7s 10.0.1.6 k8s-master2 <none> <none>
pdb-deployment-845f8bdc8f-j25dx 1/1 Running 0 2m7s 10.0.2.66 k8s-master3 <none> <none>
pdb-deployment-845f8bdc8f-j9z9s 1/1 Running 0 2m8s 10.0.1.5 k8s-master2 <none> <none>
pdb-deployment-845f8bdc8f-k4k2b 1/1 Running 0 2m8s 10.0.1.168 k8s-master2 <none> <none>
pdb-deployment-845f8bdc8f-xjrl4 1/1 Running 0 2m8s 10.0.0.107 k8s-master1 <none> <none>
pdb-deployment-845f8bdc8f-xzfvc 1/1 Running 0 2m8s 10.0.2.63 k8s-master3 <none> <none>
# 嘗試 drain k8s-msater3
❯ k drain k8s-master3 --ignore-daemonsets --timeout=60s
執行後會發現有兩個 pods 觸發了 eviction API,但因為最多同時只能一個關閉,因此一個成功一個失敗,接著等到新的 pods 健康後,另一個驅逐才會成功:
Warning: ignoring DaemonSet-managed Pods: kube-system/cilium-envoy-vvq2g, kube-system/cilium-g9mvk, kube-system/kube-proxy-xjk4h
evicting pod default/pdb-deployment-845f8bdc8f-xzfvc
evicting pod default/pdb-deployment-845f8bdc8f-j25dx
error when evicting pods/"pdb-deployment-845f8bdc8f-j25dx" -n "default" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.
pod/pdb-deployment-845f8bdc8f-xzfvc evicted
evicting pod default/pdb-deployment-845f8bdc8f-j25dx
pod/pdb-deployment-845f8bdc8f-j25dx evicted
node/k8s-master3 drained
在 PDB 的 status 欄位,可以看到 currentHealthy 等等資訊:
❯ k get pdb pdb-deployment -o yaml
...
status:
conditions:
- lastTransitionTime: "2024-09-11T10:25:55Z"
message: ""
observedGeneration: 5
reason: SufficientPods
status: "True"
type: DisruptionAllowed
currentHealthy: 7
desiredHealthy: 6
disruptionsAllowed: 1
expectedPods: 7
observedGeneration: 5
...
kubectl describe pdb
顯示的資訊也差不多:
❯ k describe pdb pdb-deployment
Name: pdb-deployment
Namespace: default
Min available: 80%
Selector: app=pdb-deployment
Status:
Allowed disruptions: 1
Current: 7
Desired: 6
Total: 7
Events: <none>
而 PDB 在認定 pods 是否為健康則是透過 pods 的 type="Ready"
且 status="True"
的欄位來判定。
當要設定 PDB 於非 k8s 內建的 controller 時,有以下限制:
.spec.minAvailable
,不能用 .spec.maxUnavailable
筆者目前還不太確定這樣的使用情境,不過基本上可以想像透過這樣配置,可以避免受 PDB 管理的 posd 少於一定數量,且若 kubectl drain node
觸發該 PDB 時,將會永遠卡住 (因爲沒有 pods controller 會建立新的副本出來)
透過配置 PDB,可以確保部署在 k8s cluster 內的 APP 持續提供服務,但若是配置不當 (e.g. 在 replicas:1 的 controller 上配置 PDB),可能會導致 kubectl drain node
指令永遠無法正常被執行,Cluster admin 在撰寫一些自動化腳本用來管理 cluster 時,也要多多注意 PDB 的配置唷 ~
https://kubernetes.io/docs/tasks/run-application/configure-pdb/