系统环境

  • Docker 版本:19.03.8
  • Kubernetes 版本:1.20.1
  • 操作系统版本:CentOS 7.6

问题描述

在日常操作维护 Kubernetes 过程中我们会经常遇到很多问题,其中经常收到的告警信息就是节点磁盘压力,即 DiskPressure 警告,当出现该警告不久后 Pod 会被驱逐,甚至节点机器的 Docker 镜像也被清理。在生产中这些情况是不能忍受的,探究一下是什么原因导致的这些以及如何提前预防并解决相关问题

问题分析

Kubernetes依靠什么组件对节点磁盘进行监控

根据 Kubernetes 官方文档描述,在 Kubernetes 集群中,节点磁盘使用量的监控是交给 Kubelet 组件完成的,它会对节点资源使用情况进行分析,等达到一定条件后会执行发出内存、磁盘、pid 资源压力警告

Kubelet监控节点的哪些文件系统

在 Kubernetes 节点组件 Kubelet 目前只支持监控两种文件系统分区,分别为:

  • nodefs: kubelet 相关的文件系统,里面存储了 Pod 挂载的卷、守护程序日志等
  • imagefs: 容器文件系统,里面存储了容器运行时用于保存镜像和容器可写层

Kubelet 监控文件系统是通过 Google 的 cAdvisor 组件完成的,该组件专用于监控容器运行环境,当监控到上面列出的文件系统达到一定量后将会发出 DiskPressure 告警,等达到指定阈值,将会对容器及无用镜像相关资源进行清理

Kubelet根据什么指标对Pod执行驱逐的

这个信息在 Kubernetes 官方文档中,Kubelet 会根据以下指标判断是否对 Pod 驱逐进行驱逐:

  • nodefs.available: kubelet 相关的文件系统剩余可用存储空间比例
  • nodefs.inodesFree: kubelet 相关文件节点表 inodes 剩余可用存储空间比例
  • imagefs.available: 容器运行时文件系统可用存储空间比例
  • imagefs.inodesFree: 容器运行时 inodes 剩余可用存储空间比例

Kubelet根据指标达到什么条件才会执行Pod驱逐

经过 Kubernetes 文档的介绍,Kubelet 会对下面指标进行判断,当到达设置的条件后,会执行强制驱逐 Pod:

  • memory.available<100Mi: 当内存下降到 100Mi 时 kubelet 开始驱逐 Pod
  • nodefs.available<10%: 当 kubelet 相关存储可用的存储不足 10% 时开始驱逐 Pod 及其容器来释放磁盘空间
  • imagefs.available<15%: 当容器运行时文件系统可用存储空间不足 15% 时开始驱逐 Pod,并且删除没有被使用的镜像来释放空间
  • nodefs.inodesFree<5%: (仅 Linux 系统): 当容器运行时 inodes 可用存储空间不足 5% 时开始驱逐 Pod 及其容器来释放磁盘空间

Kubernetes触发DiskPressure告警时造成的影响

当触发 DiskPressure 告警时,会产生如下影响:

  • 驱逐 Pod
  • 删除未使用的容器
  • 阻止新创建的 Pod 调度到该节点中

什么原因导致经常出现DiskPressure警告

推测经常出现这个警告的原因可能是:

  • 默认情况下docker会将自己的持久化文件存储到/var/lib/docker目录下,这个目录占用空间比较大,导致/var目录挂载的磁盘容易被占满
  • 默认情况下kubelet会将自身的Pod运行时相关数据存储在/var/lib/kubelet 目录下,这个目录空间比较大,导致/var目录挂载的磁盘容易被沾满
  • 默认情况下 Docker、Kubernetes 和系统等相关日志默认都会存储到 /var/log 目录下,日志相关文件占用空间非常大,导致 /var 目录挂载的磁盘容易被沾满

上面三个默认配置,一个是 kubelet,一个是 docker 的,还有就是相关日志配置的,并且发现 var 目录一般分配的空间不是很大,所以 /var 目录挂载的磁盘空间非常容易被日志文件占满
除此之外,还有一种可能,那就是当发生 DiskPressure 告警时,此时磁盘其实可用存储空间还很充足,例如 1TB 存储空间,使用率 80% 提示 DiskPressure 告警,这时候其实还有 200GB 可用存储空间。所以,这个磁盘告警比例需要根据节点磁盘的真实情况进行调整

解决方案

注意: 下面的操作最好在集群搭建初期修改,如果已经在现网环境文档运行,修改请谨慎!

更改docker存储目录

既然 Docker 目录默认在 /var/lib/docker 目录下,且这个目录所挂载的磁盘容易被占满,所以我们修改 Docker 配置,将存储的数据单独存放到挂载其它大磁盘的目录中

查看Docker状态,根据显示的信息定位Docker存储配置文件的位置

root@VM-32-194-centos ~]# systemctl status -l docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: active (running) since Tue 2021-08-03 14:45:06 CST; 6 months 7 days ago
     Docs: https://docs.docker.com
 Main PID: 1315 (dockerd)
......

可以看到配置文件所在目录是 /usr/lib/systemd/system/,配置文件是 docker.service

设置Docker数据存储目录

找到 ExecStartExecStart=/usr/bin/docker 这行,在后台添加 --graph <Docker数据存储目录>

[root@VM-32-194-centos ~]# vim /usr/lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --graph /data/docker --containerd=/run/containerd/containerd.sock
...

重启docker

[root@VM-32-194-centos ~]# systemctl restart docker

更改kubelet存储目录与告警阈值

既然 Kubelet 目录默认在 /var/lib/kubelet 目录下,且这个目录所挂载的磁盘容易被占满,所以我们修改 Kubelet 配置,将存储的数据单独存放到挂载其它大磁盘的目录中,并且根据磁盘真实大小,调整 Kubelet 触发 DisPress 告警的比例

查看kubelet状态,根据显示的信息定位kubelet存储配置文件的位置

[root@k8s01 ~]# systemctl status -l kubelet
● kubelet.service - Kubernetes Kubelet
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2021-07-26 14:10:48 CST; 6 months 15 days ago
 Main PID: 12631 (kubelet)
...

可以观察到配置文件目录所在位置是 /usr/lib/systemd/system/,配置文件是kubelet.service

查看 EnvironmentFile 配置文件所在位置

打开 kubelet 配置,查看 kubelet 配置文件

[root@k8s01 ~]# cat /usr/lib/systemd/system/kubelet.service 
[Unit]
Description=Kubernetes Kubelet
After=docker.service
 
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf
ExecStart=/opt/kubernetes/bin/kubelet $KUBELET_OPTS
Restart=on-failure
LimitNOFILE=65536
 
[Install]
WantedBy=multi-user.target

可以看到 EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ,这个文件则是供我们用户添加自定义参数的,所以我们需要在这个配置文件中添加我们自定义配置参数

编辑/opt/kubernetes/cfg/kubelet.conf配置文件

添加参数 -root-dir=<新的kubelet存储目录>

[root@k8s01 ~]# vim /opt/kubernetes/cfg/kubelet.conf
KUBELET_OPTS="--logtostderr=false \
...
--root-dir=/data/kubelet \
--eviction-hard=nodefs.available<5% \
--eviction-hard=imagefs.available<5% \
...
  • --root-dir=/apps/data/kubelet: 设置 kubelet 数据存储在 /apps/data/kubelet 目录下
  • --eviction-hard=nodefs.available<5%: 设置当 kubelet 相关存储可用的存储不足 5% 时开始驱逐 Pod
  • --eviction-hard=imagefs.available<5%: 当容器运行时文件系统可用存储空间不足 5% 时开始驱逐 Pod

上面的大小限制也可以改成具体的数值,例如 --eviction-hard=imagefs.available<10Gi,则表示容器运行时存储小于 10Gi 时进行驱逐

重启 daemon-reload 和 kubelet

[root@k8s01 ~]# systemctl daemon-reload && systemctl restart kubelet
文章作者: 鲜花的主人
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 爱吃可爱多
Kubernetes Kubernetes
喜欢就支持一下吧
打赏
微信 微信
支付宝 支付宝