1. OOM影响因素
- 资源预留:影响的是节点的Allocatable的值
- 驱逐:kubelet对Pod进行驱逐时,只根据--eviction-hard参数(支持的指标参考本文),与system-reserved等参数无关。
- OOM:当某个进程的内存超过自己的限制时,该进程会被docker(cgroup)杀掉。容器发生OOM的情况可能有两种:
- 容器所使用的内存超出了自身的limit限制;
- 所有Pod使用的内存总和超出了
/sys/fs/cgroup/memory/kubepods.slice/memory.limit_in_bytes
;
2. 节点资源预留
2.1 什么要做资源预留?
1.Kubernetes的节点可以按照节点的资源容量进行调度,默认情况下Pod能够使用节点全部可用容量。这样就会造成一个问题,因为节点自己通常运行了不少驱动OS和Kubernetes的系统守护进程。除非为这些系统守护进程留出资源,否则它们将与Pod争夺资源并导致节点资源短缺问题。
2.当我们在线上使用Kubernetes集群的时候,如果没有对节点配置正确的资源预留,我们可以考虑一个场景,由于某个应用无限制的使用节点的CPU资源,导致节点上CPU使用持续100%运行,而且压榨到了kubelet组件的CPU使用,这样就会导致kubelet和 apiserver的心跳出问题,节点就会出现Not Ready状况了。默认情况下节点Not Ready过后,5分钟后会驱逐应用到其他节点,当这个应用跑到其他节点上的时候同样100%的使用CPU,是不是也会把这个节点搞挂掉,同样的情况继续下去,也就导致了整个集群的雪崩,集群内的节点一个一个的Not Ready了,后果是非常严重的,或多或少的人遇到过Kubernetes集群雪崩的情况,这个问题也是面试的时候镜像询问的问题。
3.要解决这个问题就需要为Kubernetes集群配置资源预留,kubelet暴露了一个名为NodeAllocatable的特性,有助于为系统守护进程预留计算资源,Kubernetes也是推荐集群管理员按照每个节点上的工作负载来配置NodeAllocatable。
节点压力驱逐是 kubelet 主动终止 Pod 以回收节点上资源的过程。kubelet 监控集群节点的 CPU、内存、磁盘空间和文件系统的 inode 等资源。 当这些资源中的一个或者多个达到特定的消耗水平, kubelet 可以主动地使节点上一个或者多个 Pod 失效,以回收资源防止资源不足。
2.2 查看节点资源
#查询节点可分配资源
kubectl describe node [NODE_NAME] | grep Allocatable -B 7 -A 6
#预期输出
Capacity:
cpu: 4 #节点的CPU总核数。
ephemeral-storage: 123722704Ki #节点的临时存储总量,单位KiB。
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7925980Ki #节点的内存总量,单位KiB。
pods: 64
#Allocatable 就是节点可被分配的资源,如果没有配置资源预留,默认情况下 Capacity 与 Allocatable 的值基本上是一致的
Allocatable:
cpu: 3900m #节点可分配的CPU核数。
ephemeral-storage: 114022843818 #节点可分配的临时存储,单位Byte。
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 5824732Ki #节点可分配的内存,单位KiB。
pods: 64
#查询节点可分配资源
kubectl describe node [NODE_NAME] | grep Allocatable -B 7 -A 6
#预期输出
Capacity:
cpu: 4 #节点的CPU总核数。
ephemeral-storage: 123722704Ki #节点的临时存储总量,单位KiB。
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7925980Ki #节点的内存总量,单位KiB。
pods: 64
#Allocatable 就是节点可被分配的资源,如果没有配置资源预留,默认情况下 Capacity 与 Allocatable 的值基本上是一致的
Allocatable:
cpu: 3900m #节点可分配的CPU核数。
ephemeral-storage: 114022843818 #节点可分配的临时存储,单位Byte。
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 5824732Ki #节点可分配的内存,单位KiB。
pods: 64
- 查看node
[root@kube-master-01 kubelet]# free -k
total used free shared buff/cache available
Mem: 3976836 1522420 959140 2140 1743812 2454416
Swap: 0 0 0
[root@kube-master-01 kubelet]# free -k
total used free shared buff/cache available
Mem: 3976836 1522420 959140 2140 1743812 2454416
Swap: 0 0 0
在kubernetes 1.6版本后,引入了Node的Allocatable特性,通过该特性我们可以控制每个节点可分配的资源,可分配资源和资源预留之间的关系,如下图
- Kubelet Node Allocatable 用来为 Kube 组件和 System 进程预留资源,从而保证当节点出现满负荷时也能保证 Kube 和 System 进程有足够的资源。
- 目前支持 cpu, memory, ephemeral-storage 三种资源预留。
- Node Capacity 是节点的所有硬件资源,kube-reserved 是给 kube 组件预留的资源,system-reserved 是给系统进程预留的资源,eviction-threshold 是 kubelet 驱逐的阈值设定,allocatable 才是真正调度器调度 Pod 时的参考值(保证节点上所有 Pods 的 request 资源不超过Allocatable)。
节点可分配资源的计算方式为
Node Allocatable Resource = Node Capacity - Kube-reserved - system-reserved - eviction-threshold
Node Allocatable Resource = Node Capacity - Kube-reserved - system-reserved - eviction-threshold
当kubelet启动后,Node的allocatable就是固定的
,不会因为pod的创建与销毁而改变。
2.3 allocatable、 requests 和 limits 三者关系
在pod的yaml文件中,我们可以为pod设置requests与limits。其中limits与allocatable没有什么关系。但requests与allocatable关系紧密。调度到某个节点上的Pod的requests总和不能超过该节点的allocatable。limits的总和没有上限。
比如,某个节点的内存的allocatable为10Gi,有三个Pod(requests.memory=3Gi)已经调度到该节点上,那么第4个Pod就无法调度到该节点上,即使该Node上的空闲内存大于3Gi。
2.4 配置资源预留
系统资源预留分为两种不设cgroup
和 设置cgroup
。
- 不设cgroup:Pod使用的资源上限不会超过allocatable,如超过则被系统oom掉。系统使用资源超过kube-reserved和system-reserved阈值。可以使用allocatable的资源
- 设置cgroup:Pod使用的资源上限还是不会超过allocatable。但是系统使用资源超过kube-reserved和system-reserved阈值的话,会被cgroup杀掉。所以
推荐使用不设置cgroup。
不设cgroup
--enforce-node-allocatable=pods
--kube-reserved=memory=...
--system-reserved=memory=...
--eviction-hard=...
--enforce-node-allocatable=pods
--kube-reserved=memory=...
--system-reserved=memory=...
--eviction-hard=...
- 修改kubelet.config配置
enforceNodeAllocatable:
- pods
kubeReserved: # 配置 kube 资源预留
cpu: 500m
memory: 1Gi
ephemeral-storage: 1Gi
systemReserved: # 配置系统资源预留
memory: 1Gi
evictionHard: #配置硬驱逐阈值
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
enforceNodeAllocatable:
- pods
kubeReserved: # 配置 kube 资源预留
cpu: 500m
memory: 1Gi
ephemeral-storage: 1Gi
systemReserved: # 配置系统资源预留
memory: 1Gi
evictionHard: #配置硬驱逐阈值
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
- 重启kubelet服务
systemctl restart kubelet
systemctl restart kubelet
- 查看
[root@kube-master-01 kubernetes]# kubectl describe node kube-master-01 | grep Allocatable -B 7 -A 6
Capacity:
cpu: 2
ephemeral-storage: 36400Mi
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3976836Ki
pods: 110
Allocatable:
cpu: 1500m
ephemeral-storage: 33277607880
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 1777284Ki
pods: 110
--
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Starting 101s kubelet Starting kubelet.
Warning InvalidDiskCapacity 101s kubelet invalid capacity 0 on image filesystem
Normal NodeHasSufficientMemory 101s kubelet Node kube-master-01 status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 101s kubelet Node kube-master-01 status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 101s kubelet Node kube-master-01 status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 101s kubelet Updated Node Allocatable limit across pods
[root@kube-master-01 kubernetes]# kubectl describe node kube-master-01 | grep Allocatable -B 7 -A 6
Capacity:
cpu: 2
ephemeral-storage: 36400Mi
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3976836Ki
pods: 110
Allocatable:
cpu: 1500m
ephemeral-storage: 33277607880
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 1777284Ki
pods: 110
--
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Starting 101s kubelet Starting kubelet.
Warning InvalidDiskCapacity 101s kubelet invalid capacity 0 on image filesystem
Normal NodeHasSufficientMemory 101s kubelet Node kube-master-01 status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 101s kubelet Node kube-master-01 status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 101s kubelet Node kube-master-01 status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 101s kubelet Updated Node Allocatable limit across pods
其中的 Allocatable
的值恰好是 Capacity
减去上面我们配置的预留资源的值:
allocatale = capacity - kube_reserved - system_reserved - eviction_hard
1777284Ki = 3976836Ki - 1*1024*1024Ki - 1*1024*1024Ki - 100*1024Ki
#或者是百分比
allocatable = capacity - kube_reserved - system_reserved - eviction_threshhold
2510193454/1024Ki = 3861512Ki - 500*1024Ki - 500*1024Ki - 3861512*10%Ki
allocatale = capacity - kube_reserved - system_reserved - eviction_hard
1777284Ki = 3976836Ki - 1*1024*1024Ki - 1*1024*1024Ki - 100*1024Ki
#或者是百分比
allocatable = capacity - kube_reserved - system_reserved - eviction_threshhold
2510193454/1024Ki = 3861512Ki - 500*1024Ki - 500*1024Ki - 3861512*10%Ki
- 查看kubepods控制组
查看 kubepods.slice
(systemd 驱动是以 .slice
结尾)cgroup 中对节点上所有 Pod 内存的限制,该值决定了 Node 上所有的 Pod 能使用的资源上限:
cat /sys/fs/cgroup/memory/kubepods.slice/memory.limit_in_bytes
1924796416
1924796416Bytes = 3976836Ki = Allocatable(1777284Ki) + eviction_hard(100*1024Ki)
#也可根据下面的计算可知,Node上Pod能实际使用的资源上限值为:
kubepods/memory.limit_in_bytes = capacity - kube_reserved - system_reserved
cat /sys/fs/cgroup/memory/kubepods.slice/memory.limit_in_bytes
1924796416
1924796416Bytes = 3976836Ki = Allocatable(1777284Ki) + eviction_hard(100*1024Ki)
#也可根据下面的计算可知,Node上Pod能实际使用的资源上限值为:
kubepods/memory.limit_in_bytes = capacity - kube_reserved - system_reserved
❌ 注意
一个节点上所有Pod能使用的内存总和,与eviction-hard无关
设置cgroup
假设我们现在需要为系统预留一定的资源,那么我们可以配置如下的kubelet参数
--enforce-node-allocatable=pods,kube-reserved,system-reserved
--kube-reserved=memory=...
--kube-reserved-cgroup=...
--system-reserved=memory=...
--system-reserved-cgroup=...
--eviction-hard=..
--enforce-node-allocatable=pods,kube-reserved,system-reserved
--kube-reserved=memory=...
--kube-reserved-cgroup=...
--system-reserved=memory=...
--system-reserved-cgroup=...
--eviction-hard=..
如果还设置了对应的 --system-reserved-cgroup 和 --kube-reserved-cgroup参数,Pod能实际使用的资源上限不会改变(即kubepods.limit_in_bytes不变),但系统进程与kube进程也会受到资源上限的限制。如果系统进程超过了预留资源,那么系统进程会被cgroup杀掉。 但是如果不设这两个参数,那么系统进程可以使用超过预留的资源上限。
- 配置建议
--enforce-node-allocatable=pods
--kube-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--system-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--eviction-hard=memory.available<10%,nodefs.available<10%
--enforce-node-allocatable=pods
--kube-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--system-reserved=cpu=xx,memory=xx,ephemeral-storage=xx
--eviction-hard=memory.available<10%,nodefs.available<10%
总结
- Node的allocatable在kubelet启动后是一个固定的值,不会因为pod的创建与删除而改变
- 当我们为Pod设置了resources.requests时,调度到Node上的Pod的resources.requests的总和不会超过Node的allocatable。但Pod的resources.limits总和可以超过Node的allocatable
- 一个Pod能否成功调度到某个Node,关键看该Pod的resources.request是否小于Node剩下的request,而不是看Node实际的资源空闲量。即使空闲资源小于Pod的requests,Pod也可以调度到该Node上
- 当Pod的内存资源实际使用量超过其limits时,docker(实际是cgroup)会把该Pod内超出限额的进程杀掉(OOM);如果CPU超过,不会杀掉进程,只是进程会一直等待CPU。
- allocatable与kubepods.limit的值不一样,它们之间相差一个 eviction_hard
- 当我们不设置cgroup时,可以达到为系统预留资源的效果,即Pod的资源实际使用量不会超过allocatable的值(因为kubepods控制组中memory.limit_in_bytes的值就为allocatable的值)。即使系统本身没有使用完预留的那部分资源,Pod也无法使用。当系统超出了预留的那部分资源时,系统进程可以抢占allocatable中的资源,即对系统使用的资源没有限制。
3. imagefs与nodefs
kubelet 服务对磁盘检查是有两个参数的,分别是 imagefs
与 nodefs
。其中
- imagefs:监控docker启动参数
data-root 或者 graph
目录所在的分区。默认/var/lib/docker
- nodefs:监控kubelet启动参数
--root-dir
指定的目录所在分区。默认/var/lib/kubelet
nodefs是--root-dir目录所在分区
imagefs是docker安装目录所在的分区
3.1 修改kubelet数据目录
启动时
kubelet --root-dir=/new/data/directory
kubelet --root-dir=/new/data/directory
配置文件
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
rootDir: "/new/data/directory"
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
rootDir: "/new/data/directory"
- 重启服务
systemctl daemon-reload
systemctl restart kubelet
systemctl daemon-reload
systemctl restart kubelet
参考文档,
https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/reserve-compute-resources/
https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/
https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/
https://kubernetes.io/zh/docs/tasks/administer-cluster/out-of-resource/https://kubernetes.io/zh/docs/concepts/scheduling-eviction/node-pressure-eviction/
节点压力驱逐:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressure-eviction/