浅析 Kubernetes 资源管理

阅读数:52 2019 年 11 月 14 日 16:42

浅析Kubernetes资源管理

Kubernetes 的系统资源分为可压缩资源(CPU)和不可压缩资源(memory、storage)。默认情况下,kubelet 没有做资源预留限制,这样节点上的所有资源都能被 Pod 使用。若节点上的 pod 负载较大,会引发一系列问题。本文介绍了针对该问题 Kubernetes 提供的 kubelet 的 Node Allocatable 特性。

背景简介

Kubernetes 的系统资源分为可压缩资源(CPU)和不可压缩资源(memory、storage)。可压缩资源 (比如 CPU) 在系统满负荷时会划分时间片分时运行进程,通常情况下系统整体会变慢;不可压缩资源 (如 Memory) 在系统满负荷时,严重时会导致某些进程被系统 OOM killer 机制杀掉。

默认情况下,kubelet 没有做资源预留限制,这样节点上的所有资源都能被 Pod 使用。若节点上的 pod 负载较大,这些 pod 可能会与节点上的系统守护进程和 k8s 组件争夺资源,严重时甚至会引发系统 OOM 而杀掉一些进程。若被杀掉的进程是系统进程或 K8S 组件,可能导致更严重的问题,甚至会导致集群的雪崩。

针对这种问题,kubernetes 提供了 kubelet 的 Node Allocatable 特性,为系统进程和 k8s 组件预留资源。

资源预留

1 Node Allocatable

kubelet 的启动配置中有一个 Node Allocatable 特性,来为系统守护进程和 k8s 组件预留计算资源,使得即使节点满负载运行时,也不至于出现 pod 去和系统守护进程以及 k8s 组件争抢资源,导致节点挂掉的情况。目前支持对 CPU, memory, ephemeral-storage 三种资源进行预留。kubernetes 官方建议根据各个节点的负载情况来具体配置相关参数。

节点计算资源的分配如下图所示:

浅析Kubernetes资源管理

其中各个部分的含义如下:

  • 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 参数含义及配置

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 到此节点。

配置与验证

针对 pod、system、kube 均做 cgroup 级别限制,需要进行以下配置:

复制代码
# 在 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 结构可参考官方建议。

为 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

重启 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) 控制组对内存的限制值也与预期相符。

最佳实践

  1. 生产环境中,建议同时限制 pod、k8s 系统组件、linux system 进程资源,以免任一类资源负载过高影响其他组件,甚至造成雪崩;

  2. 针对 daemonset 创建出来的系统级别 pod,建议为其配置 Guaranteed 的服务质量等级。

本文转载自公众号 360 云计算(ID:hulktalk)。

原文链接:

https://mp.weixin.qq.com/s/CmHok-9gKVrHfiXpYUQ4nw

评论

发布