淺析Kubernetes資源管理-資源預留


1. 情況描述

Kubernetes的系統資源分為可壓縮資源(CPU)和不可壓縮資源(memory、storage)。可壓縮資源(比如CPU)在系統滿負荷時會划分時間片分時運行進程,通常情況下系統整體會變慢;不可壓縮資源(如Memory)在系統滿負荷時,嚴重時會導致某些進程被系統OOM killer機制殺掉。

默認情況下,kubelet沒有做資源預留限制,這樣節點上的所有資源都能被Pod使用。若節點上的pod負載較大,這些pod可能會與節點上的系統守護進程和k8s組件爭奪資源,嚴重時甚至會引發系統OOM而殺掉一些進程。若被殺掉的進程是系統進程或K8S組件,可能導致更嚴重的問題,甚至會導致集群的雪崩。比如:集群內某一節點跑的pod沒做resource limit使得占用資源過大,進而導致kubelet缺乏資源掛掉,此時該節點變為NotReady狀態,kubernetes會將這個節點上所有的pod驅逐到其他正常的節點上重建,進而將另外正常的節點壓跨,以此循環下去,直至集群內所有節點都會NotReady。

針對這種問題,kubernetes提供了kubelet的Node Allocatable特性,為系統進程和k8s組件預留資源(官方文檔)

2. 資源預留簡介

2.1 Node Allocatable

kubelet的啟動配置中有一個Node Allocatable特性,來為系統守護進程和k8s組件預留計算資源,使得即使節點滿負載運行時,也不至於出現pod去和系統守護進程以及k8s組件爭搶資源,導致節點掛掉的情況。目前支持對CPU, memory, ephemeral-storage三種資源進行預留。kubernetes官方建議根據各個節點的負載情況來具體配置相關參數。

節點計算資源的分配如下圖所示:

 Node Capacity
---------------------------
|     kube-reserved       |
|-------------------------|
|     system-reserved     |
|-------------------------|
|    eviction-threshold   |
|-------------------------|
|                         |
|      allocatable        |
|   (available for pods)  |
|                         |
---------------------------

其中各個部分的含義如下:

  • Node Capacity:Node的硬件資源總量;
  • kube-reserved:為k8s系統進程預留的資源(包括kubelet、container runtime等,不包括以pod形式的資源);
  • system-reserved:為linux系統守護進程預留的資源;
  • eviction-threshold:通過--eviction-hard參數為節點預留內存;
  • allocatable:可供節點上Pod使用的容量,kube-scheduler調度Pod時的參考此值。

節點可供Pod使用資源總量的計算公式:

allocatable = NodeCapacity - [kube-reserved] - [system-reserved] - [eviction-threshold]

從公式可以看出,默認情況下(不設置kube-reserved、system-reserved、eviction-threshold)節點上提供給Pod使用的資源總量等於節點的總容量。

2.2 參數含義及配置

Kubelet Node Allocatable的代碼比較簡單,主要在pkg/kubelet/cm/node_container_manager.go,感興趣的同學可以看一下。以下是相關配置參數:

  • --enforce-node-allocatable,默認為pods(默認情況下,kubelet會為所有pod的總cgroup做資源限制,限制為公式計算出的allocatable的大小)。要為kube組件和System進程預留資源,則需要設置為pods,kube-reserved,system-reserve,同時還要分別加上--kube-reserved-cgroup和--system-reserved-cgroup以指定分別限制在哪個cgroup里;

  • --cgroups-per-qos,Enabling QoS and Pod level cgroups,默認開啟。開啟后,kubelet會將管理所有workload Pods的cgroups;

  • --cgroup-driver,默認為cgroupfs,另一可選項為systemd。取決於容器運行時使用的cgroup driver,kubelet與其保持一致;

  • --kube-reserved,用於配置為kube組件(kubelet,kube-proxy,dockerd等)預留的資源量,比如—kube-reserved=cpu=2000m,memory=8Gi,ephemeral-storage=16Gi;

  • --kube-reserved-cgroup,如果設置了--kube-reserved,需設置對應的cgroup,且該cgroup目錄要事先創建好,否則kubelet將不會自動創建導致kubelet啟動失敗。比如設置為kube-reserved-cgroup=/kubelet.service;

  • --system-reserved,用於配置為System進程預留的資源量,比如—system-reserved=cpu=2000m,memory=4Gi,ephemeral-storage=8Gi;

  • --system-reserved-cgroup,如果設置了--system-reserved,需設置對應的cgroup,且該cgroup目錄要事先創建好,否則kubelet將不會自動創建導致kubelet啟動失敗。比如設置為system-reserved-cgroup=/system.slice。

  • --eviction-hard,用來配置kubelet的hard eviction條件,只支持memory和ephemeral-storage兩種不可壓縮資源。當出現MemoryPressure時,Scheduler不會調度新的Best-Effort QoS Pods到此節點。當出現DiskPressure時,Scheduler不會調度任何新Pods到此節點。

3. 配置與驗證

針對pod、system、kube均做cgroup級別限制,需要進行以下配置:

  1. 在kubelet的啟動參數中添加:
--enforce-node-allocatable=pods,kube-reserved,system-reserved \
--cgroup-driver=cgroupfs \
--kube-reserved=cpu=1,memory=1Gi,ephemeral-storage=10Gi \
--kube-reserved-cgroup=/system.slice/kubelet.service \
--system-reserved cpu=1,memory=2Gi,ephemeral-storage=10Gi \
--system-reserved-cgroup=/system.slice \

設置cgroup結構可參考官方建議

  1. 為system.slice、kubelet.service創建cpuset子系統:
    未創建前system.slice這個cgroup是沒有cpuset子系統的,而kubelet(1.9)啟動時會去查看這些cgroup子系統是否存在,如果不存在會報相應的cgroup錯誤。
// Exists checks if all subsystem cgroups already exist
func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
	// Get map of all cgroup paths on the system for the particular cgroup
	cgroupPaths := m.buildCgroupPaths(name)

	// the presence of alternative control groups not known to runc confuses
	// the kubelet existence checks.
	// ideally, we would have a mechanism in runc to support Exists() logic
	// scoped to the set control groups it understands.  this is being discussed
	// in https://github.com/opencontainers/runc/issues/1440
	// once resolved, we can remove this code.
	whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")

	// If even one cgroup path doesn't exist, then the cgroup doesn't exist.
	for controller, path := range cgroupPaths {
		// ignore mounts we don't care about
		if !whitelistControllers.Has(controller) {
			continue
		}
		if !libcontainercgroups.PathExists(path) {
			return false
		}
	}

	return true
}

所以需要手工創建相應cpuset子系統:

$ sudo mkdir -p /sys/fs/cgroup/cpuset/system.slice
$ sudo mkdir -p /sys/fs/cgroup/cpuset/system.slice/kubelet.service
  1. 重啟kubelet后,可以驗證(以內存為例):通過公式計算、節點實際capacity及allocatable的值(kubectl describe node xxx)、kubepods控制組中對內存的限制值(/sys/fs/cgroup/memory/kubepods/memory.limit_in_bytes)均與預期相符。

並且,system.slice(/sys/fs/cgroup/memory/system.slice/memory.limit_in_bytes)、kubelet.service(/sys/fs/cgroup/memory/system.slice/kubelet.service/memory.limit_in_bytes)控制組對內存的限制值也與預期相符。

4. 最佳實踐

  1. 生產環境中,建議同時限制pod、k8s系統組件、linux system進程資源,以免任一類資源負載過高影響其他組件,甚至造成雪崩;
  2. 針對daemonset創建出來的系統級別pod,建議為其配置Guaranteed的服務質量等級

相關文章

Reserve Compute Resources for System Daemons
Node Allocatable Resources
Kubernetes資源管理之—資源預留
從一次集群雪崩看Kubelet資源預留的正確姿勢
Kubernetes 服務質量 Qos 解析


免責聲明!

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



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