第61节 节点资源管理、异常检测以及常见问题排查
❤️💕💕新时代拥抱云原生,云原生具有环境统一、按需付费、即开即用、稳定性强特点。Myblog:http://nsddd.top
[TOC]
NUMA Node
Non-Uniform Memory Access 是一种内存访问方式,是为多处理器计算机设计的内存架构。
节点资源管理
- 状态汇报
- 资源预留
- 防止节点资源耗尽的防御机制驱逐
- 容器和系统资源的配置
状态上报
kubelet 周期性地向 API Server
进行汇报,并更新节点的相关健康和资源使用信息
- 节点基础信息,包括IP地址、操作系统、内核、运行时、kubelet、 kube-proxy 版本信息。
- 节点资源信息包括CPU、内存、HugePage、临时存储、GPU 等注册设备,以及这些资源中可以分配给容器使用的部分。
- 调度器在为 Pod 选择节点时会将机器的状态信息作为依据。
状态 | 意义 |
---|---|
Ready | 节点是否健康 |
MemoryPressure | 节点是否存在内存压力 |
PIDPressure | 节点是否存在比较多的进程 |
DiskPressure | 节点是否存在磁盘压力 |
NetworkUnavailable | 节点网络配置是否正确 |
Lease
在早期版本 kubele t的状态上报直接更新 node 对象,而上报的信息包含状态信息和资源信息,因此需要 传输的数据包较大,给 APIServer 和 etcd 造成的压力较大。
后引入 Lease 对象用来保存健康信息,在默认 40s 的 nodeLeaseDurationSeconds 周期内,若 Lease 对 象没有被更新,则对应节点可以被判定为不健康。
将变化频率低的资源信息和状态信息分离。
资源预留
计算节点除用户容器外,还存在很多支撑系统运行的基础服务,譬如 systemd、journald、 sshd、dockerd、Containerd、 kubelet 等。
为了使服务进程能够正常运行,要确保它们在任何时候都可以获取足够的系统资源,所以我们要为这些系统进程预留资源。 kubelet 可以通过众多启动参数为系统预留 CPU、内存、PID等资源,比如 SystemReserved、KubeReserved等。
Capacity 和 Allocatable
容量资源(Capacity) 是指 kubelet 获取的计算节点当前的资源信息。
- CPU是从
/proc/cpuinfo
文件中获取的节点CPU核数; - memory 是从/ proc/memoryinfo 中获取的节点内存大小;
- ephemeral-storage 是指节点根分区的大小。
资源可分配额(Allocatable) 是用户 Pod 可用的资源,是资源容量减去分配给系统的资源的剩余部分。
节点资源总量= KubeReserved + SystemReserved + Eviction-threshold + Allocatable
节点磁盘管理
系统分区 nodefs
- 工作目录和容器日志
- kubelet 的数据目录,默认为
/var/lib/kubelet
容器运行时分区 imagefs
- 用户镜像和容器可写层
- 容器运行时分区是可选的,可以合并到系统分区中
- 存放镜像的目录,containerd 在
/var/lib/containerd
,docker 在/var/lib/docker
驱逐管理
- kubelet 会在系统资源不够时中止一些容器进程,以空出系统资源,保证节点的稳定性。
- 但由 kubelet 发起的驱逐只停止 Pod 的所有容器进程,并不会直接删除 Pod,便于管理员发现问题
- Pod 的 status.phase 会被标记为 Failed
- status.reason 会被设置为 Evicted
- status.message 则会记录被驱逐的原因
资源可用额监控
- kubelet 依赖内嵌的开源软件 cAdvisor,周期性检查节点资源使用情况
- CPU 是可压缩资源,根据不同进程分配时间配额和权重,CPU 可被多个进程竞相使用
- 驱逐策略是基于磁盘和内存资源用量进行的,因为两者属于不可压缩的资源,当此类资源使用耗尽时将无法再申请
检查类型 | 说明 |
---|---|
memory.avaliable | 节点当前可用内存 |
nodefs.avaliable | 节点根分区的可用磁盘大小 |
nodefs.inodesFree | 节点根分区的可使用 inode |
imagefs.avaliable | 节点运行时分区的可用磁盘大小 |
imagefs.inodesFree | 节点运行时分区的可使用 inode |
驱逐策略
kubelet 获得节点的可用额信息后,会结合节点的容量信息来判断当前节点运行的 Pod 是否满足驱逐条件。
驱逐条件可以是绝对值或百分比,当监控资源的可使用额少于设定的数值或百分比时,kubelet 就会发起驱逐操作。
kubelet 参数 evictionMinimumReclaim 可以设置每次回收的资源的最小值,以防止小资源的多次回收。
kubelet 参数 | 分类 | 驱逐方式 |
---|---|---|
evictionSoft | 软驱逐 | 当检测到当前资源达到软驱逐的阈值时,并不会立即启动驱逐操作,而是要等待一个宽限期。这个宽限期选取 EvictionSoftGracePeriod 和 Pod 指定的TerminationGracePeriodSeconds 中较小的值 |
evictionHard | 硬驱逐 | 没有宽限期,一旦检测到满足硬驱逐的条件,就直接中止容器来释放紧张资源 |
基于内存压力的驱逐
memory.avaiable 表示当前系统的可用内存情况。
kubelet 默认设置了 memory.avaiable<100Mi 的硬驱逐条件。
当 kubelet 检测到当前节点可用内存资源紧张并满足驱逐条件时,会将节点的 MemoryPressure 状态设置为 True, 调度器会阻止 BestEffort Pod 调度到内存承压的节点。
kubelet 启动对内存不足的驱逐操作时,会依照如下的顺序选取目标 Pod:、
- (1)判断 Pod 所有容器的内存使用量总和是否超出了请求的内存量,超出请求资源的 Pod 会成为备选目标。
- (2)查询 Pod 的调度优先级,低优先级的 Pod 被优先驱逐。
- (3)计算 Pod 所有容器的内存使用量和 Pod 请求的内存量的差值,差值越小,越不容易被驱逐。
- Pod A 请求 100Mi,使用了 120Mi,比例为 1.2
- Pod B 请求 200Mi,使用了 300Mi,比例为1.5
- Pod B 会被优先驱逐
基于磁盘压力的驱逐
以下任何一项满足驱逐条件时,它会将节点的 DiskPressure 状态设置为 True,调度器不会再调度任何 Pod 到该节点上:
- nodefs.available
- nodefs.inodesFree
- imagefs.available
- imagefs.inodesFree
驱逐行为
- 有容器运行时分区
- nodefs 达到驱逐阈值, 那么 kubelet 删除已经退出的容器
- Imagefs 达到驱逐阈值,那么 kubelet 删除所有未使用的镜像
- 无容器运行时分区
- kubelet 同时删除未运行的容器和未使用的镜像。
回收已经退出的容器和未使用的镜像后,如果节点依然满足驱逐条件,kubelet 就会开始驱逐正在运行的 Pod,进一步释放磁盘空间。
- 判断 Pod 的磁盘使用量是否超过请求的大小,超出请求资源的 Pod 会成为备选目标。
- 查询 Pod 的调度优先级,低优先级的 Pod 优先驱逐。
- 根据磁盘使用超过请求的数量进行排序,差值越小,越不容易被驱逐。
OOM Killer 的行为
- 系统的 OOM_Killer 可能会采取 OOM 的方式来中止某些容器的进程,进行必要的内存回收操作
- 而系统根据进程的 oom_score 来进行优先级排序,选择待终止的进程,且进程的 oom_score 越高,越容易被终止。
- 进程的 oom_score 是根据当前进程使用的内存占节点总内存的比例值乘以 10,再加上 oom_score_adj 综合得到的
- 比如占用内存 50%,那么计算得到的这部分占比分就是 500,如果是 60% 就是 600 分
- 而容器进程的 oom_score_adj 正是 kubelet 根据 memory.request 进行设置的
Pod QoS 等级 | oom_score_adj |
---|---|
Guaranteed | -998 |
BestEffort | 1000 |
Burstable | min(max(2,1000-(1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
查看具体 Pod 的 oom_score:
crictl ps|grep nginx
crictl inspect b2e7a8e64253d|grep pid
cat /proc/296290/oom_score
cat /proc/296290/oom_score_adj
查看全部进程的 oom_score
#!/bin/bash
printf 'PID\tOOM Score\tOOM Adj\tCommand\n'
while read -r pid comm; do [ -f /proc/$pid/oom_score ] && [ $(cat /proc/$pid/oom_score) != 0 ] && printf '%d\t%d\t\t%d\t%s\n' "$pid" "$(cat /proc/$pid/oom_score)" "$(cat /proc/$pid/oom_score_adj)" "$comm"; done < <(ps -e -o pid= -o comm=) | sort -k 2nr
测试对 cpu 的校验和准入行为
- 定义一个 Pod,并将该 Pod 中的 nodeName 属性直接写成集群中的节点名
- 将 Pod 的 CPU 的资源设置为超出计算节点的 CPU 的值
- 创建该 Pod
- 观察行为并思考
日志管理
节点上需要通过运行 logrotate 的定时任务对系统服务日志进行rotate清理,以防止系统服务日志占用大量的磁盘空间。
- logrotate 的执行周期不能过长,以防日志短时间内大量增长。
- 同时配置日志的 rotate 条件, 在日志不占用太多空间的情况下,保证有足够的日志可供查看。
- Docker
- 除了基于系统 logrotate 管理日志,还可以依赖 Docker 自带的日志管理功能来设置容器日志的数量和每个日志文件的大小。
- Docker 写入数据之前会对日志大小进行检查和 rotate 操作,确保日志文件不会超过配置的数量和大小。
- Containerd
- 日志的管理是通过 kubelet 定期(默认为10s)执行 du 命令,来检查容器日志的数量和文件的大小的。
- 每个容器日志的大小和可以保留的文件个数,可以通过 kubelet 的配置参数 container-log-max-size和container-log-max-files 进行调整。
Docker 卷管理
- 在构建容器镜像时,可以在 Dockerfile 中通过 VOLUME 指令声明一个存储卷,目前 Kubernetes 并未将其纳入管控范围,不建议使用。
- 如果容器进程在可写层或 emptyDir 卷进行大量读写操作,就会导致磁盘 I/O 过高,从而影响其他容器进程甚至系统进程。
- Docker 和 Containerd 运行时都基于 CGroupv1。对于块设备,只支持对 Direct I/O 限速,而对于Buffer I/O 还不具备有效的支持。因此,针对设备限速的问题,目前还没有完美的解决方案,对于有特殊 I/O 需求的容器,建议使用独立的磁盘空间。
网络资源
由网络插件通过 Linux Traffic Control 为 Pod限制带宽
可利用 CNI社区提供的 bandwidth 插件
进程数
kubelet 默认不限制 Pod 可以创建的子进程数量,但可以通过启动参数 podPidsLimit 开启限制,还可以由reserved 参数为系统进程预留进程数。
- kubelet 通过系统调用周期性地获取当前系统的 PID 的使用量,并读取
/proc/sys/kernel/pid_max
, 获取系统支持的PID 上限。 - 如果当前的可用进程数少于设定阈值,那么 kubelet 会将节点对象的 PIDPressure 标记为 True
- kube-scheduler 在进行调度时, 会从备选节点中对处于 NodeUnderPIDPressure 状态的节点进行过滤。
Kubernetes 日志
与传统的日志收集相比,Kubernetes 日志收集具有以下不同之处:
- 分布式:Kubernetes 集群中运行的应用程序通常由多个容器组成,这些容器可能跨越多个节点。因此,日志收集需要跨节点和容器进行。
- 动态性:Kubernetes 集群中的 Pod 可以随时被创建、删除或重新启动。因此,日志收集需要动态地适应这些变化。
- 多样性:Kubernetes 集群中的应用程序可能使用多种编程语言和日志格式。因此,日志收集需要支持多种格式和语言。
为了解决这些问题,Kubernetes 提供了一些内置的日志收集机制,如 kubectl logs 命令和 Fluentd 插件。此外,还可以使用第三方工具,如 Elasticsearch、Fluentd 和 Kibana 等,来进行日志收集和分析。
前言
Pod 的生命周期很短, Pod 销毁后 日志也会一同被删除, Kubernetes 的日志系统在设计的时候,必须独立于节点和pod 的生命周期,且保证日志数据可以实时采集到服务端。
在 Kubernetes 集群中,需要进行日志收集的地方包括:
- 容器日志:在容器中运行的应用程序输出的日志。
- 节点日志:节点上的系统日志和容器日志。
除了以上两种方式外,还可以使用日志聚合工具,如 Elasticsearch、Fluentd 和 Kibana 等,来进行日志收集和分析。
在进行日志收集时,需要注意以下几点:
- 确保节点和 Pod 的时间同步。
- 将日志存储在持久存储中,避免数据丢失。
- 对于大规模集群,需要考虑日志的压缩和转储,以减少存储空间的占用。
日志轮转
日志轮转是指在日志文件达到一定大小或一定时间后,将其重命名并创建一个新的日志文件,以避免日志文件过大或过旧而导致的性能问题或存储问题。
优点:
- 避免单个日志文件过大,导致读取、写入、传输、存储等方面的性能问题。
- 避免日志文件过旧,导致存储空间占用过多。
缺点:
- 如果日志轮转设置不当,可能会导致日志文件不完整,或者日志文件没有及时轮转,导致存储空间占用过多。
END 链接
✴️版权声明 © :本书所有内容遵循CC-BY-SA 3.0协议(署名-相同方式共享)©