前幾天中午正吃着火鍋唱着歌,手機上忽然就彈出了大量的告警信息,彙報着某個 K8s 集羣的 api-server 沒法鏈接。通過一番檢查,發現是集羣中存在大量重複的 Pod 對象,這些對象使得 etcd 和 apiserver 佔用了很是多的內存,且陷入了不可用的狀態。爲何會這樣呢?接下來和小編一塊兒來看看吧。node
衆所周知,Kubernetes 是一個容器編排系統,它會將集羣中各種應用的狀態維持到聲明中所要求的狀態。爲了可以更加精確的將集羣維護到維護者想象中的狀態,在 Kubernetes 中有着大量的機制來控制集羣。而致使本次故障的,就是如下三個特性的不正確使用所致使的。api
其中,Replicaset
是一種用來維護工做負載中 Pod 數量的控制器。當你在無狀態工做負載中聲明瞭spec.replicas
字段,Kubernetes 就會將不斷建立 Pod,直到可用 Pod 的數量知足聲明後纔會中止調度。code
在建立 Pod 時,Kubernetes 須要將這個 Pod 的容器部署到某一個可用的節點上,這時候調度器就會選擇出一個可用的節點,並將這個 Pod 部署到該節點上。server
若是咱們但願某些節點上面不會調度 Pod 上去,咱們能夠爲該節點設置污點。這樣一來,該節點就會拒絕將來新調度過來的 Pod(NoSchedule),甚至驅散當前已有的 Pod(NoExecute)。只有配置了相應容忍度的 Pod 才能夠繼續被調度和運行到帶有污點的節點上。對象
而咱們又能夠經過聲明(nodeName
)來將一些 Pod 調度到指定的節點上。這些 Pod 將不會部署在其餘名稱的節點上。內存
如今,讓咱們想象一下,當一個帶有複製集的工做負載設置了節點選擇器後,他所選擇的節點忽然被打上了NoExecute
污點。可是工做負載中並無設置相應的容忍度。這時候會發生什麼狀況呢?部署
unavalible
的狀態終止部署很顯然,答案是最不顯然的 D。污點的實現是在調度時去判斷當前節點是否帶有污點,及是否符合要求,若是沒有符合需求的節點,這個 Pod 的部署就會阻塞住。而聲明瞭nodeName
,Pod 的部署會跳過節點的選擇,直接部署到目標節點,而後該 Pod 就會被由於沒有對應容忍度被拒絕,並標註其狀態爲Evicted
。get
因爲此次失敗並不是是容器運行的失敗,Kubernetes 的控制器會嘗試繼續建立 Pod,使得可用的 Pod 數量與複製集中聲明的數量一致。而這個過程很是的迅速,一瞬間就能建立出無數個 Pod,而且隨着時間的不斷的擴充着 Pod 的規模。直至集羣中的 master 節點崩潰。it
很幸運的是,咱們在集羣尚未徹底崩潰以前發現了這個問題。不過雖然集羣還沒崩潰,可是 apiserver 的響應時間已經變得很是的長了,隨便一個 get 請求均可能須要等 5 秒以上。隨即咱們經過 Kubectl 刪除了所有相應的 Pod 和工做負載後,故障獲得瞭解決。io
咱們能夠經過如下命令來刪除所有被驅散的 Pod
kubectl -n kube-system get Pods | grep Evicted |awk '{print$1}'|xargs kubectl -n kube-system delete Pods
若是真的是集羣完全崩潰,那麼可能就會須要先中止 Kubernetes 相關組件的運行,並在 etcd 中手動刪除相應的對象。而後再從新啓用 k8s 組件。
雖然表面上看,這種錯誤屬於人爲的操做失誤,可是在實際狀況中,k8s也會自動的爲某些節點加上污點。如此一來,類似的狀況就可能還會發生。
從源頭上,咱們能夠儘量的用節點選擇器(nodeSelector)和節點親和性(nodeAffinity)來調度Pod。經過這種方式部署的Pod會在調度以前進行一次驗證,若是不存在可用的節點,調度就會中止。
或者能夠經過PDB來阻止建立過多的Pod。PDB能夠控制應用在短期由於非自願干擾而關閉的Pod數量,進而能夠避免無數個Pod被建立又驅散致使的集羣崩潰。