介绍

概念功能说明

kubelet主动监测和防止Kubernetes集群的资源使用情况,在资源短缺时,如何判断资源短缺根据系统设置的eviction保留策略,当节点资源使用触发到该阈值,开始执行驱逐(主动的结束一个或者多个Pod以回收资源,Pod的状态变为Failed),如果被驱逐的Pod被deployment控制器接管,那么将会被调度到其它的节点

"evictionHard": {
      "imagefs.available": "15%",
      "memory.available": "100Mi",
      "nodefs.available": "10%",
      "nodefs.inodesFree": "5%"
    },

驱逐处理

驱逐信号

具体信号,参考下表

驱逐信号 解释  
memory.available  memory.available := node.status.capacity[memory] – node.stats.memory.workingSet 代表节点可用的内存
nodefs.available nodefs.available := node.stats.fs.available 可用的文件系统,一般指/,官方说明是指kubelet所使用的卷及日志所在文件系统
nodefs.inodesFree nodefs.inodesFree := node.stats.fs.inodesFree 同上,inode的使用
imagefs.available imagefs.available := node.stats.runtime.imagefs.available Container runtimes所在的逻辑卷,用于存储镜像及可写入的镜像层
imagefs.inodesFree imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree 同上,inode的使用
pid.available pid.available := node.stats.rlimit.maxpid – node.stats.rlimit.curproc  

驱逐信号的值,可以用具体数字及百分比代表

memory.available 的值从 cgroupfs 获取,而不是通过类似 free -m 的工具。因为 free -m 不能在容器中工作,并且如果用户使用了 节点可分配资源 特性,资源不足的判定将同时在本地 cgroup 层次结构的终端用户 Pod 部分和根节点做出。 这个脚本 复现了与 kubelet 计算 memory.available 相同的步骤。 kubelet 将 inactive_file(意即活动 LRU 列表上基于文件后端的内存字节数)从计算中排除, 因为它假设内存在出现压力时将被回收

官方文档总是那么的认真,一句话,就是节点的available内存,还给出一个具体计算方式

计算方式

#!/bin/bash
#!/usr/bin/env bash

# This script reproduces what the kubelet does
# to calculate memory.available relative to root cgroup.

# current memory usage
memory_capacity_in_kb=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}')
memory_capacity_in_bytes=$((memory_capacity_in_kb * 1024))
memory_usage_in_bytes=$(cat /sys/fs/cgroup/memory/memory.usage_in_bytes)
memory_total_inactive_file=$(cat /sys/fs/cgroup/memory/memory.stat | grep total_inactive_file | awk '{print $2}')

memory_working_set=${memory_usage_in_bytes}
if [ "$memory_working_set" -lt "$memory_total_inactive_file" ];
then
    memory_working_set=0
else
    memory_working_set=$((memory_usage_in_bytes - memory_total_inactive_file))
fi

memory_available_in_bytes=$((memory_capacity_in_bytes - memory_working_set))
memory_available_in_kb=$((memory_available_in_bytes / 1024))
memory_available_in_mb=$((memory_available_in_kb / 1024))

echo "memory.capacity_in_bytes $memory_capacity_in_bytes"
echo "memory.usage_in_bytes $memory_usage_in_bytes"
echo "memory.total_inactive_file $memory_total_inactive_file"
echo "memory.working_set $memory_working_set"
echo "memory.available_in_bytes $memory_available_in_bytes"
echo "memory.available_in_kb $memory_available_in_kb"
echo "memory.available_in_mb $memory_available_in_mb"

文件系统驱逐信号

目前kubelet只支持二种文件系统,如下

nodefs (节点kubelet所在文件系统使用率)
imagefs(docker的Root Dir,docker info查看)

驱逐阈值

表示语法

[eviction-signal][operator][quantity]

合法的 eviction-signal 标志如上所示。
operator 是所需的关系运算符,例如 <
quantity 是驱逐阈值值标志,例如 1Gi。合法的标志必须匹配 Kubernetes 使用的数量表示。 驱逐阈值也可以使用 % 标记表示百分比

举例说明,如果一个节点有 10Gi 内存,希望在可用内存下降到 1Gi 以下时引起驱逐操作, 则驱逐阈值可以使用下面任意一种方式指定(但不是两者同时)
memory.available<10%
memory.available<1Gi

驱逐方式

硬驱逐:硬驱逐阈值没有宽限期,触发阈值,kubelet将立即采取行动回收关联的资源(被触发的资源)。 如果满足硬驱逐阈值,kubelet将立即结束 (kill的方式)Pod 而不是优雅的(stop的方式)它们
软驱逐:当触发阈值时需要等待指定的宽限期组合使用(阈值&宽限期),在超出宽限期前,kubelet不会采取任何动作回收和驱逐信号关联的资源。如果没有提供宽限期,kubelet启动时将报错;此外,如果达到了软驱逐阈值,可以指定从节点驱逐 pod 时,在宽限期内允许结束的 pod 的最大数量。 如果指定了 pod.Spec.TerminationGracePeriodSeconds值, kubelet取二者(TerminationGracePeriodSeconds & eviction-soft-grace-period)的最小值执行。 如果没有指定,kubelet将立即终止 pod,而不会优雅结束它们

eviction-soft 描述了驱逐阈值的集合(例如 memory.available<1.5Gi),如果在宽限期之外满足条件将触发 pod 驱逐。
eviction-soft-grace-period 描述了驱逐宽限期的集合(例如 memory.available=1m30s),对应于在驱逐 pod 前软驱逐阈值应该被控制的时长。
eviction-max-pod-grace-period 描述了当满足软驱逐阈值并终止 pod 时允许的最大宽限期值(秒数)

驱逐时间检查间隔

housekeeping-interval 是容器管理时间间隔

驱逐节点状态

当集群中的某个节点,触发到驱逐阈值时,节点会呈现出以下状态

MemoryPressure memory.available 节点上可用内存量达到逐出阈值
DiskPressure nodefs.available, nodefs.inodesFree, imagefs.available, 或 imagefs.inodesFree 节点或者节点的根文件系统或镜像文件系统上可用磁盘空间和 inode个数达到逐出阈值
PIDPressure pid.available 在(Linux)节点上的可用进程标识符已降至驱逐阈值以下

驱逐时节点资源波动

kubelet在配置了软驱逐时,如果触发了阈值并在上下波动,时true时flase,针对此种情况会使用以下参数规避

eviction-pressure-transition-period (默认时间5m0s)是 kubelet从压力状态中退出之前必须等待的时长

回收节点资源

当时达到eviction阈值时,kubelet在驱逐Pod释放系统资源,比如硬盘资源缺乏时,如下规则

imagefs

如果 nodefs文件系统满足驱逐阈值,kubelet通过删除dead pod & Contaienr以释放磁盘空间
如果 imagefs文件系统满足驱逐阈值,kubelet通过删除所有未使用的镜像来释放磁盘空间

no imagefs

删除所有Dead Pod & Container
删除所有未使用镜像

驱逐Pod

如果kubele在节点上无法回收足够的资源,则需要驱逐Pod,具体顺序如下

首先对BestEffort或者Burstable此类Qos级别Pod的紧缺资源进行判断,判断的条件是实际使用大于请求分配,然后进行排序,高则驱逐

Guaranteed pod 和 Burstable pod实际使用低于请求分配,最后被驱逐,高则先被驱逐

如果在节点遇到系统守护进程如(kubelet docker journald)实际使用的资源消耗大于systemReserved & kubeReserved预留的资源,则按优先级驱逐此类Pod以保持节点的稳定性
具体个人总结为(BestEffort > Burstable > Guaranteed )同级情况比较资源的消耗量,高则先被驱逐

imagesfs

如果是 nodefs触发驱逐,kubelet是按基于nodefs用量进行排序,判断条件(本地卷 + Pod所有容器日志的总和)
如果 imagefs 触发了驱逐,Kubelet 按Pod中所有容器的写入层进行排序,从高至低,高则驱逐

no imagefs

如果是nodefs触发了驱逐阈值,kubelet基于Pod的所有容器的(Local volumes + 日志用量 + 可写入层)的和进行排序

 最小回收值

有时会遇到在触发驱逐阈值后,驱逐Pod会回收少量的资源会反反复复进行驱逐这样会引起平台的稳定性,对此可以设置最小回收值解决此类现象,如下

–eviction-hard=memory.available<500Mi,nodefs.available<1Gi,imagefs.available<100Gi

–eviction-minimum-reclaim=”memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi”

以上案例表示如果遇到触发了内存驱逐条件时,那么最终回收的内存资源值是(500Mi+0Mi),默认 eviction-minimum-reclaim 值为 0

节点OOM

如果kubelet在回收内存前先执行了节点OOM,那么下表是依据Pod的Qos而定义的oom_score_adj的值

sshd/dmevented / systemd-udevd -1000
kubelet/docker/journalctl -999
Guaranteed -998
BestEffort 1000
Burstable min(max(2, 1000 – (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

如果节点OS在内存紧缺时,并且在节点OOM之前无法进行内存回收,那么oom_killer会计算所有container基于内存使用的百分比得出oom_score,再与oom_score_adj,最终算出容器真正的oom_score,然后kill得分最高的容器,具体公式如下

init oom_score(基于容器对内存使用的百分比计算) + oom_score_adj = oom_score_adj,计算的结果等于Pod的Qos等级最低及请求内存最高的容器首先被第一个结束

与Pod驱逐不同的是,如果一个Pod被OOM,会被所属的资源管理器所接管,如deployment接管,kubelet执行对应Deployment中指定的restartPolicy策略

驱逐建议

参考场景:

节点内存容量:10Gi

系统守护进程保留 5% 内存量(kernel sshd udevf)系统守护进程而预留

kubeRserved预留 5% 内存量(kubelet  docekr proxy)Kubernetes组件而预留

在内存用量达到 90% 时驱逐 pod,以减少对系统的冲击并防止系统 OOM 的发生

具体配置,如下

    "evictionHard": {
      "imagefs.available": "15%",
      "memory.available": "10%",
      "nodefs.available": "10%",
      "nodefs.inodesFree": "5%"
    },
    "evictionPressureTransitionPeriod": "4m0s",
    "evictionMinimumReclaim": {
      "imagefs.available": "2Gi",
      "memory.available": "300Mi",
      "nodefs.available": "500Mi"
    },
...
    "systemReserved": {
      "cpu": "500m",
      "memory": "500Mi"
    },
    "kubeReserved": {
      "cpu": "500m",
      "memory": "500Mi"
    }

以上配置总结如下

systemReserved+kubeReserved ≥ eviction > node OOM(/proc/sys/vm/min_free_kbytes)

如果systemReserved + kubeReserved < eviction 的值代表OOM的概率增加,因为Pod的Allocatable的值接近Capacity的值

如果systemReserved + kubeReserved > eviction 的值代表OOM的概率减少,因为Pod的Allocatable的值小于Capacity的值

最终目的是优先执行eviction,防止应用丢失数据

DaemonSet

在配置DaemonSet时资源副本控制器,Qos的等级务必为Guaranteed(requests & limits的值相同),因为如果Qos设置过低的话,会被优先驱逐,由于DaemonSet的特性是每个节点的守护Pod,如果kill或者驱逐则调度器会在相同的节点重新调度

https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#active-file-memory-is-not-considered-as-available-memory