适用场景
本文适用于 Kubernetes 集群中出现以下问题的场景:
- 新发布的 Pod 长时间处于
Pending状态; kubectl describe pod中看到node(s) had disk pressure;- 节点状态出现
DiskPressure=True; - 容器镜像、日志或临时目录占满节点磁盘,导致调度失败;
- 节点上已经运行的 Pod 被驱逐,事件中出现
Evicted。
这类问题常见于镜像构建频繁、日志量突然变大、没有配置容器日志轮转、节点磁盘容量偏小、CI/CD 高峰期集中拉取大镜像的集群。
现象描述
业务发布后,Deployment 的副本数迟迟没有恢复:
kubectl get deploy -n prod
kubectl get pod -n prod -o wide
可能看到类似结果:
NAME READY STATUS RESTARTS AGE NODE
api-7d8d87d6f4-2qk9s 0/1 Pending 0 8m <none>
api-7d8d87d6f4-hp8mv 1/1 Running 0 2d node-10-0-1-21
查看 Pod 事件:
kubectl describe pod api-7d8d87d6f4-2qk9s -n prod
常见事件如下:
Warning FailedScheduling default-scheduler 0/5 nodes are available:
2 node(s) had disk pressure,
3 node(s) didn't match Pod's node affinity/selector.
此时问题不在应用代码本身,而是调度器认为可用节点不满足磁盘压力条件,新的 Pod 无法落到节点上。
可能原因
DiskPressure 通常由 kubelet 根据节点文件系统可用空间或 inode 情况判断。常见原因包括:
- 容器运行时目录占用过高,例如
/var/lib/containerd或/var/lib/docker; - 镜像缓存长期未清理,大镜像和历史版本累积;
- 容器标准输出日志持续增长,例如
/var/log/containers、/var/log/pods; - emptyDir、临时文件、导出文件写入节点本地磁盘;
- 节点系统盘容量过小,业务盘和容器运行时目录没有分离;
- inode 耗尽,磁盘容量看似没满但无法继续创建文件;
- 日志采集异常,采集进程停滞后日志堆积。
排查思路
1. 确认 Pod Pending 是否由磁盘压力导致
先看 Pod 事件,不要只看 kubectl get pod:
kubectl describe pod <pod-name> -n <namespace>
重点看 Events 中的 FailedScheduling。如果出现 had disk pressure,说明调度阶段已经被节点条件拦截。
也可以直接查看所有 Pending Pod:
kubectl get pod -A --field-selector=status.phase=Pending -o wide
如果多个命名空间同时出现 Pending,优先怀疑节点资源或调度约束,而不是单个业务配置。
2. 查看节点 DiskPressure 状态
kubectl get node
kubectl describe node <node-name>
在 Conditions 中重点看:
Type Status Reason
DiskPressure True KubeletHasDiskPressure
也可以快速筛选异常节点:
kubectl describe node | grep -E "Name:|DiskPressure|OutOfDisk"
字段解释:
DiskPressure=True:kubelet 判断节点磁盘或 inode 已低于驱逐阈值;MemoryPressure=True:内存压力,不是本文重点;PIDPressure=True:进程数压力;Ready=False或Ready=Unknown:节点健康状态异常,需要结合 kubelet 和网络继续排查。
3. 登录节点查看磁盘与 inode
在异常节点执行:
df -h
df -ih
重点看这些挂载点:
/var/lib/containerd
/var/lib/docker
/var/log
/var
/
df -h 用于看容量,df -ih 用于看 inode。若 inode 使用率 100%,即使磁盘还有空间,也会导致文件创建失败。
继续定位大目录:
sudo du -xhd1 /var | sort -h
sudo du -xhd1 /var/lib | sort -h
sudo du -xhd1 /var/log | sort -h
参数说明:
-x:不跨文件系统,避免把挂载盘一起统计进去;-h:人类可读单位;-d1:只看当前目录下一层,适合逐层定位;sort -h:按容量大小排序。
4. 区分镜像、容器日志和业务临时文件
如果使用 containerd:
sudo crictl images
sudo crictl ps -a
sudo du -xhd1 /var/lib/containerd | sort -h
如果使用 Docker:
docker system df
docker ps -a --size
sudo du -xhd1 /var/lib/docker | sort -h
查看容器日志目录:
sudo du -xhd1 /var/log/pods | sort -h | tail
sudo du -xhd1 /var/log/containers | sort -h | tail
定位单个超大日志文件:
sudo find /var/log/pods /var/log/containers -type f -size +500M -printf '%s %p\n' | sort -n
如果是 emptyDir 或应用临时文件,通常会落在 kubelet 管理目录中:
sudo du -xhd1 /var/lib/kubelet | sort -h
sudo find /var/lib/kubelet -xdev -type f -size +500M -printf '%s %p\n' | sort -n | tail
定位示例
一次发布后,业务 Pod 全部 Pending,事件如下:
0/6 nodes are available: 4 node(s) had disk pressure, 2 node(s) didn't match Pod's node affinity/selector.
查看节点:
kubectl describe node node-10-0-1-21 | grep -A5 Conditions
发现:
DiskPressure True KubeletHasDiskPressure
登录节点:
df -h
结果显示 /var 使用率 96%。继续定位:
sudo du -xhd1 /var | sort -h
发现 /var/log 占用 80G。继续查看:
sudo du -xhd1 /var/log/pods | sort -h | tail
定位到某个业务 Pod 的 stdout 日志持续刷屏,单个日志文件超过 30G。根因是应用在异常重试时每秒打印大量完整请求体,日志采集又因为配置错误没有及时上传和轮转。
修复方案
1. 先恢复调度能力
如果确认是无用镜像或已退出容器占用空间,可以清理容器运行时缓存。
containerd 环境优先使用 kubelet 兼容的清理方式:
sudo crictl ps -a
sudo crictl images
sudo crictl rmi --prune
Docker 环境可以执行:
docker system df
docker container prune
docker image prune -a
执行前要确认节点不是依赖本地镜像运行离线业务。生产环境不建议无脑清理所有镜像,避免后续扩容时重新拉取镜像变慢。
如果是超大容器日志,优先修复应用刷日志问题,再按日志文件路径处理。不要直接删除正在写入的文件后就结束排查,因为进程可能仍持有文件句柄。可以先截断:
sudo truncate -s 0 /var/log/pods/<namespace>_<pod>_<uid>/<container>/0.log
截断后重新查看:
df -h
kubectl describe node <node-name> | grep -A5 DiskPressure
kubelet 需要一段时间重新上报节点状态。状态恢复后,Pending Pod 通常会自动被调度。
2. 处理被驱逐的 Pod
如果出现大量 Evicted Pod:
kubectl get pod -A | grep Evicted
可以按命名空间清理:
kubectl get pod -n <namespace> | awk '/Evicted/{print $1}' | xargs -r kubectl delete pod -n <namespace>
这一步只清理 Kubernetes API 中的已驱逐对象,不解决磁盘压力根因。根因仍要回到节点磁盘占用、日志策略和业务写盘行为。
3. 给业务配置 ephemeral-storage 请求和限制
对于会写临时文件的业务,应配置 ephemeral-storage:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
image: registry.example.com/api:2026-07-29
resources:
requests:
cpu: "500m"
memory: "512Mi"
ephemeral-storage: "1Gi"
limits:
cpu: "1"
memory: "1Gi"
ephemeral-storage: "4Gi"
关键点:
requests.ephemeral-storage会参与调度,避免把大量写盘 Pod 调度到空间不足的节点;limits.ephemeral-storage可以限制单个容器无限制写本地盘;- 写入 emptyDir、容器日志、容器可写层都可能计入本地临时存储。
4. 配置容器日志轮转
如果节点使用 Docker,可以在 /etc/docker/daemon.json 中限制 json-file 日志:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
如果使用 containerd,应检查 kubelet 启动参数或配置文件中的日志限制,例如:
containerLogMaxSize: 100Mi
containerLogMaxFiles: 5
修改后需要按集群维护规范重启相关服务,并分批验证节点是否正常:
systemctl status kubelet
systemctl status containerd
预防措施
- 为节点
/var/lib/containerd或/var/lib/docker单独挂载数据盘,避免挤占系统盘; - 为高日志量服务设置日志级别、采样和单条日志大小限制;
- 配置容器日志轮转,避免 stdout 日志无限增长;
- 为会写临时文件的服务配置
ephemeral-storagerequest 和 limit; - 监控节点磁盘容量、inode、containerd/docker 目录、
DiskPressure节点条件; - 发布系统中关注 Pending Pod 数量和调度失败事件;
- 定期清理不用的镜像和已退出容器,但要避开业务高峰;
- 对批处理、导出、压缩任务使用持久卷或对象存储,不要默认写节点系统盘。
Prometheus 中可以关注这些指标:
kube_node_status_condition{condition="DiskPressure",status="true"}
node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.15
node_filesystem_files_free{mountpoint="/"} / node_filesystem_files{mountpoint="/"} < 0.15
告警建议:
DiskPressure=True持续 5 分钟立即告警;- 根分区或容器运行时分区可用空间低于 15% 告警;
- inode 可用比例低于 15% 告警;
- Pending Pod 数量突然升高时联动查看调度事件。
总结
Kubernetes 中 Pod Pending 不一定是资源请求过大,也可能是节点进入 DiskPressure 后被调度器排除。排查时应先看 Pod 事件,再看节点 Conditions,随后登录节点用 df、du、crictl、日志目录逐层定位。临时清理只能恢复现场,长期治理要落到日志轮转、镜像清理、ephemeral-storage 限制、节点磁盘规划和监控告警上。这样才能避免一次日志刷屏或镜像堆积演变成集群级发布失败。
Discussion
评论