🏷 概述

本文介绍公有云容器服务(ACK,EKS, …)或自建K8S集群多场景下可能发生的磁盘高负载问题。并且给出相应的排查思路,解决措施,以及事后的规避策略

📋 问题现

现象 状态返回值
节点状态显示node.kubernetes.io/disk-pressure NoSchedule
创建Pod失败 ContainerCreating
删除Pod失败 Terminating
无法通过kubectl exec操作进入容器 UnexpectedAdmissionError

📋 可能原因

Kubelet 支持 gc 和驱逐机制,每五分钟对未使用的镜像执行一次垃圾收集, 每分钟对未使用的容器执行一次垃圾收集。当(自建kubelet)配置不正确,或者节点上有其它非 K8S 管理的进程在不断写数据到磁盘,将会占用大量空间导致磁盘满载。对于kubelet参数的详细配置见官方文档

磁盘满载将影响 K8S 运行,主要涉及 kubelet 和容器运行时两个关键组件

📋 问题排查

判断容器路径是否被写满:

公有云容器服务(如ACK)创建节点时一般会推荐容器数据单独挂载数据盘。

具体路径可通过docker info确认:

  1. $ docker info|grep Dir
  2. ...
  3. Docker Root Dir: /var/lib/docker
  4. ...

如果没有单独挂数据盘,则会使用系统盘存储。判断是否被写满:

  1. $ df -h
  2. Filesystem Size Used Avail Use% Mounted on
  3. ...
  4. /dev/vda1 200G 200G 0 100% /
  5. ...
  6. overlay 200G 200G 0 100% /var/lib/docker/overlay2/...

🔨 解决方法

处理步骤

  1. 当docker作为容器运行时,所属的CLI(dockerd)也会因此丢失响应无法正常重启来释放空间。因此需要先手动清理一些docker的日志或可写层文件。

通常会优先删除日志文件,再重启dockerd

  1. cd /var/lib/docker/containers
  2. # 找到比较大的容器目录
  3. du -sh *
  4. cd 506605a176678f87cbc4f61185eb72f68441836844eb56d5080f1c306282f412
  5. # 删除log文件
  6. cat /dev/null > 506605a176678f87cbc4f61185eb72f68441836844eb56d5080f1c306282f412-json.log
  1. 将该 node 标记不可调度,并将其已有的 pod 驱逐到其它节点,这样重启 dockerd 就会让该节点的 pod 对应的容器删掉,容器相关的日志(标准输出)与容器内产生的数据文件(可写层)也会被清理
  1. #驱逐异常node,同时不可调度
  2. kubectl drain {Node_Name}
  3. #重启docker
  4. systemctl restart dockerd
  5. #节点恢复
  6. kubectl uncordon {Node_Name}

🔨 如何规避此类事故

  • 通过对节点的监控(列如使用观测云,Prometheus等)做磁盘使用率的自动化报警
  • 根据监控检查节点上非容器的进程是否存在占用空间过多的现象
  • 根据业务实际情况,优化限制Pod的**<font style="color:rgb(51, 51, 51);">ephemeral-storage</font>**
  • 建议避免使用HostPath,选择公有云的存储产品(如阿里云NAS)或自建的NFS等
  • 节点挂载容器的数据磁盘扩容
  • 日志尽量不落地存在Pod内