1. 一次磁盘告警引发的排查:Non DFS Used 到底是什么
凌晨两点,监控平台弹出一条告警:某集群 DataNode 节点磁盘使用率突破 90% 阈值。按照常规思路,我第一反应是数据写入量激增,于是登录节点执行df -h查看磁盘占用,发现数据盘确实快满了。但接下来的一幕让我有点意外——打开 HDFS 的 Web UI,看到 DFS Used 只占了不到 60%,剩余空间却显示为 "Non DFS Used" 占了大头。
这个 "Non DFS Used" 是 HDFS 运维里一个非常经典但又容易被忽视的概念。简单说,它指的是 DataNode 数据目录所在磁盘上,不属于 HDFS 管理范围的那部分空间占用。HDFS 自己算账的逻辑是这样的:磁盘总容量减去 DFS Used(HDFS 实际存储的数据块),再减去 Reserved(系统保留空间),剩下的就是 Non DFS Used。公式写出来就是:
Non DFS Used = 磁盘总容量 - DFS Used - Reserved Space - 剩余可用空间
换句话说,只要磁盘上存在任何 HDFS 之外的文件,比如日志、临时文件、被删除但句柄未释放的文件、甚至是其他程序写入的数据,都会被归入 Non DFS Used。这个指标本身不是"错误",但它一旦异常膨胀,就意味着 DataNode 可用的写入空间被蚕食,严重时直接导致写入失败、数据块副本丢失,甚至节点被踢出集群。
这篇文章适合所有负责 HDFS 集群运维的工程师、大数据平台开发者,以及正在被"磁盘满了但 HDFS 显示没满"这类问题困扰的同行。我会把这次排查的完整过程拆开讲清楚:Non DFS Used 的计算原理、常见成因、排查手段、清理方案,以及后续如何建立预防机制。内容基于真实运维场景整理,涉及的命令和参数都可以直接拿去用。
2. Non DFS Used 的计算逻辑与常见成因拆解
2.1 HDFS 是如何统计这块空间的
要理解 Non DFS Used,得先搞清楚 DataNode 上报磁盘信息的机制。DataNode 定期向 NameNode 发送心跳时,会附带一份StorageReport,里面包含capacity、dfsUsed、remaining、nonDfsUsed等字段。NameNode 的 Web UI 和dfsadmin report命令展示的数据就来源于此。
关键在于nonDfsUsed的计算方式。在较新的 Hadoop 版本中,DataNode 通过DF命令(或等效的系统调用)获取整个磁盘分区的使用情况,然后用分区总容量减去 HDFS 数据目录实际占用的字节数,得到的就是 Non DFS Used。这里有个容易踩的坑:如果多个数据目录共享同一个磁盘分区,HDFS 会把整个分区的非 HDFS 占用都算进来,而不是只算某个目录。
举个例子,假设/data分区挂载了 1TB 磁盘,HDFS 的dfs.datanode.data.dir配置为/data/dfs/dn,同时这个分区上还跑着 YARN 的本地临时目录/data/yarn/local。那么 YARN 产生的所有临时文件、容器日志、Shuffle 数据,全部会被计入 Non DFS Used。这就是为什么很多集群明明 HDFS 数据不多,Non DFS Used 却高得吓人。
2.2 六种最常见的空间占用来源
结合我这些年处理过的案例,Non DFS Used 异常膨胀的原因基本逃不出下面这几类:
| 成因类别 | 典型表现 | 排查难度 |
|---|---|---|
| 日志文件堆积 | DataNode/NodeManager 日志未轮转,单文件达数十 GB | 低 |
| 被删除但未释放的文件 | lsof显示 deleted 状态,进程仍持有句柄 | 中 |
| YARN 本地临时目录 | 容器工作目录、Shuffle 数据残留 | 低 |
| 其他服务共用磁盘 | 同一分区部署了非 HDFS 组件 | 低 |
| 文件系统保留空间 | ext4 默认 5% 保留给 root | 低 |
| 快照或副本残留 | 存储快照、回收站未清理 | 中 |
其中被删除但未释放的文件是最隐蔽的一类。Linux 的文件删除机制是:只有当所有持有该文件句柄的进程都关闭文件后,磁盘空间才会真正释放。如果某个进程(比如一个卡死的 DataNode 或外部脚本)一直持有大文件的句柄,即使rm删除了文件,df依然显示空间被占用,而du却统计不到。这种"幽灵占用"经常让人抓狂。
2.3 为什么这个指标值得单独关注
很多人觉得 Non DFS Used 只是个参考值,不影响 HDFS 运行。这个认知是危险的。DataNode 在决定是否接受新数据块写入时,会检查可用空间是否低于配置的阈值(dfs.datanode.du.reserved)。如果 Non DFS Used 吃掉了大量空间,实际可用于 HDFS 写入的空间就会缩水,触发DiskOutOfSpaceException,导致写入失败。
更严重的情况是,当 Non DFS Used 持续增长,DataNode 可能因为磁盘满而无法写入新的数据块副本,NameNode 检测到副本数不足后会触发副本复制,但其他节点如果也有同样问题,就会形成连锁反应。我见过一次因为日志把磁盘写满,导致整个机架的数据块副本同时丢失的案例,恢复起来相当痛苦。
3. 实战排查:从告警到定位真凶的完整过程
3.1 第一步:确认 Non DFS Used 的真实数值
登录问题节点后,不要急着删文件。先用几个命令交叉验证,把账算清楚:
# 查看磁盘整体使用情况 df -h /data # 查看 HDFS 数据目录实际占用 du -sh /data/dfs/dn # 通过 hdfs 命令查看集群视角的统计 hdfs dfsadmin -report | grep -A 10 "DataNode"df显示的是文件系统层面的占用,du显示的是目录实际占用。如果两者差距巨大,基本可以确定存在"被删除但未释放"的文件。这时候用lsof找出持有句柄的进程:
# 查找所有 deleted 状态但仍被占用的文件 lsof | grep deleted | grep /data # 按占用大小排序,快速定位大文件 lsof | grep deleted | awk '{print $7, $9}' | sort -rn | head -20这一步的输出会告诉你哪个进程、哪个文件在"占着茅坑不拉屎"。常见的是 DataNode 自身的日志文件被外部工具轮转后,进程没有重新打开新文件,导致旧文件句柄一直挂着。
3.2 第二步:逐层排查目录占用
如果df和du的数值基本吻合,说明空间确实被实际文件占用了,那就需要逐层定位。我的习惯是从分区根目录开始,用du按层级下钻:
# 查看一级目录占用,按大小排序 du -h --max-depth=1 /data | sort -rh # 对占用最大的目录继续下钻 du -h --max-depth=1 /data/yarn | sort -rh这次排查中,我发现/data/yarn/local目录占了将近 200GB。进一步查看发现,里面堆满了usercache下的容器工作目录,很多是几个月前失败的作业残留。YARN 的本地目录清理依赖yarn.nodemanager.localizer.cache.cleanup.interval-ms和yarn.nodemanager.localizer.cache.target-size-mb两个参数,如果配置不当或者 NodeManager 长时间未重启,残留就会越积越多。
3.3 第三步:检查文件系统保留空间
还有一个容易被忽略的点是文件系统的保留块。ext4 默认会保留 5% 的空间给 root 用户,这部分空间在df里算作已使用,但普通进程无法写入。对于一块 4TB 的盘,5% 就是 200GB,相当可观。
# 查看文件系统保留比例 tune2fs -l /dev/sdX | grep -i reserved # 调整保留比例(生产环境谨慎操作,建议保留 1%-2%) tune2fs -m 1 /dev/sdX对于专门用于 HDFS 的数据盘,把保留比例降到 1% 是常见做法,但要注意这会影响系统在磁盘满时的应急能力,需要权衡。
3.4 第四步:确认 HDFS 自身的 Reserved 配置
别忘了 HDFS 自己也有一层保留空间配置dfs.datanode.du.reserved,默认值是 10GB 左右。这个值会从可用空间中扣除,不参与 HDFS 写入。如果这个值设置得过大,也会让 Non DFS Used 看起来偏高。检查hdfs-site.xml中的配置:
<property> <name>dfs.datanode.du.reserved</name> <value>10737418240</value> <!-- 10GB --> </property>这个值的设定要根据磁盘容量和业务写入速度来定。太小了容易写满,太大了浪费空间。我的经验是,对于 4TB 以上的盘,保留 20-30GB 比较稳妥,给系统留出应急缓冲。
4. 清理方案与操作避坑指南
4.1 安全清理 YARN 本地目录
确认是 YARN 残留后,不要直接rm -rf整个目录,那样可能误删正在运行的容器数据。正确做法是先停止 NodeManager,或者通过 YARN 提供的清理机制处理:
# 优雅方式:重启 NodeManager,触发自动清理 yarn-daemon.sh stop nodemanager yarn-daemon.sh start nodemanager # 手动清理时,只删除超过一定天数的目录 find /data/yarn/local/usercache -type d -mtime +7 -exec rm -rf {} \;注意:手动清理前务必确认没有正在运行的作业,否则会导致容器失败。建议在业务低峰期操作,并提前通知相关团队。
4.2 处理被删除但未释放的文件
对于lsof找到的 deleted 文件,处理方式取决于持有句柄的进程:
- 如果是 DataNode 自身持有,最安全的做法是重启 DataNode。重启前先执行
hdfs dfsadmin -refreshNodes确认节点状态,然后优雅停止。 - 如果是外部脚本或工具持有,先尝试正常终止该进程。如果进程卡死无法终止,再考虑强制 kill,但要评估对业务的影响。
- 极端情况下,可以用
> /proc/PID/fd/FD的方式清空文件内容,但这只适用于普通文件,且需要 root 权限。
# 查看进程持有的具体文件描述符 ls -l /proc/PID/fd | grep deleted # 清空文件内容(谨慎使用) : > /proc/PID/fd/FD_NUMBER4.3 日志轮转配置优化
日志堆积是最常见的成因,也是最容易预防的。检查 DataNode 和 NodeManager 的日志配置,确保启用了按大小和时间的轮转:
<!-- log4j.properties 关键配置 --> log4j.appender.RFA.MaxFileSize=256MB log4j.appender.RFA.MaxBackupIndex=10同时配合系统的logrotate,设置合理的保留天数和压缩策略。我一般会把 HDFS 相关日志的保留周期设为 7-14 天,超过的自动压缩归档到冷存储。这样既能满足排查需求,又不会让磁盘被日志撑爆。
4.4 清理操作的检查清单
每次清理前,我都会过一遍这个清单,避免手抖:
- 确认当前没有正在运行的写入作业,或已通知相关方
- 确认要删除的目录不属于 HDFS 数据目录
- 确认删除操作不会影响其他共用该磁盘的服务
- 删除前记录当前磁盘使用率,便于对比效果
- 删除后立即用
df -h和 HDFS UI 确认 Non DFS Used 是否下降 - 观察 24 小时,确认没有异常增长
5. 预防机制:让 Non DFS Used 不再失控
5.1 监控指标的精细化配置
光靠磁盘使用率告警是不够的,需要把 Non DFS Used 单独拎出来监控。在监控系统中添加如下指标采集:
nonDfsUsed绝对值及其占磁盘总容量的比例nonDfsUsed的日环比增长率- DFS Used 与 Non DFS Used 的比值变化趋势
我一般会设置两级阈值:Non DFS Used 占比超过 20% 触发警告,超过 30% 触发严重告警。这样能在问题恶化前就介入处理。
5.2 磁盘规划的最佳实践
从架构层面,最有效的预防手段是磁盘隔离。不要让 HDFS 数据目录和其他服务共用同一个分区。理想情况下:
- HDFS 数据盘单独挂载,只放
dfs.datanode.data.dir - YARN 本地目录放在独立的盘或分区
- 日志目录单独挂载,避免日志写满影响数据盘
如果资源有限无法完全隔离,至少要在dfs.datanode.du.reserved中预留足够的缓冲空间,并严格控制共用服务的写入量。
5.3 定期巡检脚本
我写了一个简单的巡检脚本,每周跑一次,输出各节点的 Non DFS Used 排名和异常目录:
#!/bin/bash # 巡检 Non DFS Used 异常节点 for node in $(cat datanode_list.txt); do echo "=== $node ===" ssh $node "df -h /data | tail -1; du -h --max-depth=1 /data 2>/dev/null | sort -rh | head -5" done这个脚本能快速发现哪些节点的非 HDFS 占用在增长,以及增长来自哪个目录。配合定时任务,可以做到早发现早处理。
5.4 版本升级带来的变化
值得一提的是,Hadoop 3.x 版本对 Non DFS Used 的统计做了一些优化,引入了更精确的nonDfsUsed计算方式,减少了因多目录共享分区导致的误报。如果还在用 2.x 版本,升级到 3.x 能省不少心。不过升级前要做好兼容性测试,特别是涉及存储格式和 API 变更的部分。
6. 几个真实踩坑案例的复盘
6.1 案例一:日志轮转脚本的副作用
某次集群扩容后,新节点的 Non DFS Used 在三天内从 5% 涨到 40%。排查发现是运维团队部署的日志轮转脚本有 bug,轮转后没有通知 DataNode 重新打开日志文件,导致旧日志句柄一直被持有。lsof显示有十几个 deleted 状态的日志文件,加起来超过 300GB。重启 DataNode 后空间立即释放。这个案例的教训是:任何涉及文件句柄的操作,都要考虑对运行中进程的影响。
6.2 案例二:YARN 缓存配置的坑
另一个集群的 Non DFS Used 缓慢增长,每周增加约 50GB。查下来是 YARN 的本地化缓存清理间隔设置得太长(默认 10 分钟,但被改成了 24 小时),加上target-size-mb设置过大,导致缓存文件长期不清理。调整参数后,Non DFS Used 稳定在 10% 以内。这里的关键是理解每个参数的实际含义,不要盲目照搬网上的配置。
6.3 案例三:快照导致的隐性占用
有个节点 Non DFS Used 异常高,但du和lsof都找不到明显的大文件。最后发现是存储层做了快照,快照数据占用了大量空间,而du默认不统计快照。用lvs和vgs查看逻辑卷信息后确认了问题。处理方式是清理过期快照,并调整快照策略。这个案例提醒我们,排查要穿透到存储层,不能只看文件系统层面。
7. 写在最后的一些个人体会
处理 Non DFS Used 这类问题,最忌讳的就是一上来就删文件。我见过太多因为着急清理而误删数据、导致集群故障的案例。正确的姿势永远是:先算清楚账,再定位来源,最后才动手清理。df、du、lsof这三个命令的组合,能解决 90% 以上的磁盘占用排查问题。
另外,Non DFS Used 本身不是敌人,它是 HDFS 给我们的一个信号——提醒我们磁盘上还有其他东西在占用空间。与其把它当成需要消灭的指标,不如把它当成一个健康检查项,定期关注它的变化趋势。只要它稳定在一个合理范围内,就说明集群的磁盘管理是健康的。
最后分享一个小技巧:在 DataNode 的hdfs-site.xml中,可以配置多个数据目录,并把它们分散到不同的物理磁盘上。这样即使某块盘出现 Non DFS Used 异常,也不会影响整个节点的写入能力。这个配置在集群规划阶段就要考虑好,后期调整成本会高很多。