Kubernetes Pod 驅逐詳解


本文轉載自米開朗其揚的博客


1. Kubernetes Pod 驅逐詳解

在 Kubernetes 中,Pod 使用的資源最重要的是 CPU、內存和磁盤 IO,這些資源可以被分為可壓縮資源(CPU)和不可壓縮資源(內存,磁盤 IO)。可壓縮資源不可能導致 Pod 被驅逐,因為當 Pod 的 CPU 使用量很多時,系統可以通過重新分配權重來限制 Pod 的 CPU 使用。而對於不可壓縮資源來說,如果資源不足,也就無法繼續申請資源(內存用完就是用完了),此時 Kubernetes 會從該節點上驅逐一定數量的 Pod,以保證該節點上有充足的資源。

當不可壓縮資源不足時,Kubernetes 是通過 kubelet 來驅逐 Pod 的。kubelet 也不是隨機驅逐的,它有自己的一套驅逐機制,每個計算節點的 kubelet 都會通過抓取 cAdvisor 的指標來監控節點的資源使用量,下面我們來具體分析每種情況。

1.1 存儲資源不足

下面是 kubelet 默認的關於節點存儲的驅逐觸發條件:

  • nodefs.available<10%(容器 volume 使用的文件系統的可用空間,包括文件系統剩余大小和 inode 數量)
  • imagefs.available<15%(容器鏡像使用的文件系統的可用空間,包括文件系統剩余大小和 inode 數量)

當 imagefs 使用量達到閾值時,kubelet 會嘗試刪除不使用的鏡像來清理磁盤空間。

當 nodefs 使用量達到閾值時,kubelet 就會拒絕在該節點上運行新 Pod,並向 API Server 注冊一個 DiskPressure condition。然后 kubelet 會嘗試刪除死亡的 Pod 和容器來回收磁盤空間,如果此時 nodefs 使用量仍然沒有低於閾值,kubelet 就會開始驅逐 Pod。從 Kubernetes 1.9 開始,kubelet 驅逐 Pod 的過程中不會參考 Pod 的 QoS,只是根據 Pod 的 nodefs 使用量來進行排名,並選取使用量最多的 Pod 進行驅逐。所以即使 QoS 等級為 Guaranteed 的 Pod 在這個階段也有可能被驅逐(例如 nodefs 使用量最大)。如果驅逐的是 Daemonset,kubelet 會阻止該 Pod 重啟,直到 nodefs 使用量超過閾值。

如果一個 Pod 中有多個容器,kubelet 會根據 Pod 中所有容器的 nodefs 使用量之和來進行排名。即所有容器的 container_fs_usage_bytes 指標值之和。

舉個栗子,假設某計算節點上運行着一系列已知 QoS 等級和 nodefs 使用量的 Pod:

Pod Name	Pod QoS			nodefs usage
A		Best Effort		800M
B		Guaranteed		1.3G
C		Burstable		1.2G
D		Burstable		700M
E		Best Effort		500M
F		Guaranteed		1G

當 nodefs 的使用量超過閾值時,kubelet 會根據 Pod 的 nodefs 使用量來對 Pod 進行排名,首先驅逐使用量最多的 Pod。排名如下所示:

Pod Name	Pod QoS			nodefs usage
B		Guaranteed		1.3G
C		Burstable		1.2G
F		Guaranteed		1G
A		Best Effort		800M
D		Burstable		700M
E		Best Effort		500M

可以看到在本例中,QoS 等級為 Guaranteed 的 Pod 最先被驅逐。

1.2 內存資源不足

下面是 kubelet 默認的關於節點內存資源的驅逐觸發條件:

  • memory.available<100Mi

當內存使用量超過閾值時,kubelet 就會向 API Server 注冊一個 MemoryPressure condition,此時 kubelet 不會接受新的 QoS 等級為 Best Effort 的 Pod 在該節點上運行,並按照以下順序來驅逐 Pod:

  1. Pod 的內存使用量是否超過了 request 指定的值
  2. 根據 priority 排序,優先級低的 Pod 最先被驅逐
  3. 比較它們的內存使用量與 request 指定的值之差。
  4. 按照這個順序,可以確保 QoS 等級為 Guaranteed 的 Pod 不會在 QoS 等級為 Best Effort 的 Pod 之前被驅逐,但不能保證它不會在 QoS 等級為 Burstable 的 Pod 之前被驅逐。

如果一個 Pod 中有多個容器,kubelet 會根據 Pod 中所有容器相對於 request 的內存使用量與之和來進行排名。即所有容器的 (container_memory_usage_bytes 指標值與 container_resource_requests_memory_bytes 指標值的差)之和。

繼續舉例,假設某計算節點上運行着一系列已知 QoS 等級和內存使用量的 Pod:

Pod Name	Pod QoS		Memory requested	Memory limits	Memory usage
A		Best Effort	0			0		700M
B		Guaranteed	2Gi			2Gi		1.9G
C		Burstable	1Gi			2Gi		1.8G
D		Burstable	1Gi			2Gi		800M
E		Best Effort	0			0		300M
F		Guaranteed	2Gi			2Gi		1G

當節點的內存使用量超過閾值時,kubelet 會根據 Pod 相對於 request 的內存使用量來對 Pod 進行排名。排名如下所示:

Pod Name	Pod QoS		Memory requested	Memory limits	Memory usage	內存相對使用量
C		Burstable		1Gi		2Gi		1.8G		800M
A		Best Effort		0		0		700M		700M
E		Best Effort		0		0		300M		300M
B		Guaranteed		2Gi		2Gi		1.9G		-100M
D		Burstable		1Gi		2Gi		800M		-200M
F		Guaranteed		2Gi		2Gi		1G		-1G

可以看到在本例中,可以看到在本例中,QoS 等級為 Guaranteed 的 Pod 在 QoS 等級為 Burstable 的 Pod 之前被驅逐。

當內存資源不足時,kubelet 在驅逐 Pod 時只會考慮 requests 和 Pod 的內存使用量,不會考慮 limits。

1.3 Node OOM (Out Of Memory)

OOM Killer,是 Liunx 特性下的一個保護機制,用於避免在內存不足的時候不至於出現嚴重問題,把一些無關的進程優先殺掉,即在內存嚴重不足時,系統為了繼續運轉,內核會挑選一個進程,將其殺掉,以釋放內存,緩解內存不足情況。

因為 kubelet 默認每 10 秒抓取一次 cAdvisor 的監控數據,所以有可能在 kubelet 驅逐 Pod 回收內存之前發生內存使用量激增的情況,這時就有可能觸發內核 OOM killer。這時刪除容器的權利就由kubelet 轉交到內核 OOM killer 手里,但 kubelet 仍然會起到一定的決定作用,它會根據 Pod 的 QoS 來設置其 oom_score_adj 值:

QoS					oom_score_adj
Guaranteed				-998
Burstable				min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)
pod-infra-container			-998
kubelet, docker daemon, systemd service	-999

如果該節點在 kubelet 通過驅逐 Pod 回收內存之前觸發了 OOM 事件,OOM killer 就會采取行動來降低系統的壓力,它會根據下面的公式來計算 oom_score 的值:

容器使用的內存占系統內存的百分比 + oom_score_adj = oom_score

OOM killer 會殺掉 oom_score_adj 值最高的容器,如果有多個容器的 oom_score_adj 值相同,就會殺掉內存使用量最多的容器(其實是因為內存使用量最多的容器的 oom_score 值最高)。與 Pod 驅逐不同,如果一個 Pod 的容器被 OOM 殺掉,是可能被 kubelet 根據 RestartPolicy 重啟的。關於 OOM 的更多內容請參考:Kubernetes 內存資源限制實戰

假設某節點運行着 4 個 Pod,且每個 Pod 中只有一個容器。每個 QoS 類型為 Burstable 的 Pod 配置的內存 requests 是 4Gi,節點的內存大小為 30Gi。每個 Pod 的 oom_score_adj 值如下所示:

Pod Name	Pod QoS			oom_score_adj
A		Best Effort		1000
B		Guaranteed		-998
C		Burstable		867(根據上面的公式計算)
D		Best Effort		1000

當調用 OOM killer 時,它首先選擇 oom_score_adj 值最高的容器(1000),這里有兩個容器的 oom_score_adj 值都是 1000,OOM killer 最終會選擇內存使用量最多的容器。

1.4 總結

因為 kubelet 默認每 10 秒抓取一次 cAdvisor 的監控數據,所以可能在資源使用量低於閾值時,kubelet 仍然在驅逐 Pod。
kubelet 將 Pod 從節點上驅逐之后,Kubernetes 會將該 Pod 重新調度到另一個資源充足的節點上。但有時候 Scheduler 會將該 Pod 重新調度到與之前相同的節點上,比如設置了節點親和性,或者該 Pod 以 Daemonset 的形式運行。
現在你應該理解了 kubelet 驅逐 Pod 的原理和過程,如果你在部署應用時設置了恰當的參數,知道了所有的可能性,你就能更好地掌控你的集群。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM