Skip to content

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 查看节点资源

img

bash
#查询节点可分配资源
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
bash
[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特性,通过该特性我们可以控制每个节点可分配的资源,可分配资源和资源预留之间的关系,如下图

image-20241014135037623

  • 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)。

节点可分配资源的计算方式为

bash
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配置
yaml
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服务
bash
systemctl restart kubelet
systemctl restart kubelet
  • 查看
bash
[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 减去上面我们配置的预留资源的值:

bash
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 能使用的资源上限:

bash
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参数

bash
--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%

总结

  1. Node的allocatable在kubelet启动后是一个固定的值,不会因为pod的创建与删除而改变
  2. 当我们为Pod设置了resources.requests时,调度到Node上的Pod的resources.requests的总和不会超过Node的allocatable。但Pod的resources.limits总和可以超过Node的allocatable
  3. 一个Pod能否成功调度到某个Node,关键看该Pod的resources.request是否小于Node剩下的request,而不是看Node实际的资源空闲量。即使空闲资源小于Pod的requests,Pod也可以调度到该Node上
  4. 当Pod的内存资源实际使用量超过其limits时,docker(实际是cgroup)会把该Pod内超出限额的进程杀掉(OOM);如果CPU超过,不会杀掉进程,只是进程会一直等待CPU。
  5. allocatable与kubepods.limit的值不一样,它们之间相差一个 eviction_hard
  6. 当我们不设置cgroup时,可以达到为系统预留资源的效果,即Pod的资源实际使用量不会超过allocatable的值(因为kubepods控制组中memory.limit_in_bytes的值就为allocatable的值)。即使系统本身没有使用完预留的那部分资源,Pod也无法使用。当系统超出了预留的那部分资源时,系统进程可以抢占allocatable中的资源,即对系统使用的资源没有限制。

3. imagefs与nodefs

kubelet 服务对磁盘检查是有两个参数的,分别是 imagefsnodefs。其中

  • imagefs:监控docker启动参数 data-root 或者 graph 目录所在的分区。默认/var/lib/docker
  • nodefs:监控kubelet启动参数 --root-dir 指定的目录所在分区。默认/var/lib/kubelet

nodefs是--root-dir目录所在分区

imagefs是docker安装目录所在的分区

3.1 修改kubelet数据目录

启动时

bash
kubelet --root-dir=/new/data/directory
kubelet --root-dir=/new/data/directory

配置文件

bash
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
rootDir: "/new/data/directory"
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
rootDir: "/new/data/directory"
  • 重启服务
bash
 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/