ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

HDFS故障排查实战:从架构原理到高频问题定位与修复

HDFS故障排查实战:从架构原理到高频问题定位与修复 1. 排查前先把概念盘明白HDFS架构与故障类型地图HDFS 在大数据体系里的位置用一句大实话概括它是整片数据森林的地基。你可以把 Hive 当查数据的门面把 Spark 当跑批量的引擎把 Flume 当采数据的管道但这些组件最后全都压在同一套存储底座上——HDFS。底座一歪楼上的 MapReduce、Spark、Hive 作业一起遭殃轻则作业超时重跑重则数据目录直接不可用。更麻烦的是HDFS 的故障往往不是“啪一下全没了”而是由几十个细节叠加而成某个 DataNode 的磁盘快满了、某个副本悄悄丢了、NameNode 的 edits 文件越积越厚。等你发现的时候它已经忍了你很久。所以我在处理 HDFS 问题时第一步从来不是急着敲命令而是先盘一盘当前集群的架构状态。有多少个 NameNode、是否开了 HA、DataNode 的磁盘挂载方式是什么、副本因子设的是几、block 大小是多少。这些信息决定了后面你会遇到哪一类故障以及排查的方向。比如没有 HA 的单 NameNode 集群元数据一坏就是全局事故而副本因子设为 1 的集群磁盘一坏就是永久丢数据。这些配置不光写在 hdfs-site.xml 里也直接画出了你整个故障排查的地图。1.1 从 HDFS 读写流程反推故障点很多人对 HDFS 的认知停留在“能存文件”这一层但真正做故障排查你必须对读写流程的每一步烂熟于心。因为每一个故障现象都能在流程的某一环上找到对应的“病根”。先说写入流程。客户端发起写请求时先联系 NameNode申请在哪个路径下创建文件、拿到哪些 DataNode 可以写入。NameNode 确认了目录权限和配额之后客户端才会把数据拆成 block按 pipeline 的顺序推给第一个 DataNode再由它转给第二个、第三个。每写一个 blockDataNode 会向 NameNode 回传 block 报告同时向客户端返回 ack。这中间任何一环出问题现象完全不一样客户端连不上 NameNode报的是 RPC 连接超时NameNode 拒绝创建文件报的是权限或配额不足数据推给 DataNode 没人接收报的是 pipeline 建立失败或写入超时。读取流程相对短客户端先找 NameNode 要文件对应的 block 列表和位置NameNode 返回 block 所在 DataNode 的列表客户端再直奔 DataNode 去拉数据。读流程最常见的故障是“拿到位置但读不到数据”——这往往意味着这份 block 的副本已经全部丢失或者 DataNode 实际已经挂了但 NameNode 还没把它剔除。类比一下生活中最常见的场景NameNode 像图书馆的检索系统DataNode 是一排一排的书架block 就是单本书。你去借书先查检索系统NameNode知道书在哪个书架DataNode走过去抽出来读数据。如果检索系统显示书在 3 号书架但 3 号书架已经塌了那你只能白跑一趟。HDFS 里大量“读超时”“报错找不到 block”的问题本质都是这种“书架塌了但检索系统还在说书在那”。有了这条流程线你能干一件很关键的事把故障症状翻译成具体环节。客户端报“Could not obtain block” 读流程的 DataNode 环节出问题报“No lease on file” 写流程的租约管理出问题报“NameNode is in safe mode” NameNode 自身的元数据状态异常。先把症状翻译准了再去看日志和指标效率能差出好几倍。1.2 故障排查的通用方法论我在指导团队做 HDFS 排障时常年坚持一套五步法确认现象、查日志、看指标、验证假设、执行方案。这套方法听起来简单但真到了线上告警响成一片的时候能保持冷静按顺序走的没几个人。第一步“确认现象”非常关键。告警信息经常是笼统的比如“HDFS 容量使用率超过 90%”但等你真去看发现其实是个误报——有人临时传了个大文件。所以先用几条命令快速确认现状再判断问题真伪。第二步“查日志”是排障的重头戏NameNode 和 DataNode 都有自己的日志目录常见路径是 /var/log/hadoop-hdfs/里面以 hadoop-hdfs-namenode-xxx.log 和 hadoop-hdfs-datanode-xxx.log 命名的文件就是核心。第三步“看指标”包括磁盘使用率、块报告、RPC 延迟这些用 jmx 接口或者 hdfs dfsadmin -report 都能拿到。第四步“验证假设”是很多人容易跳过的环节我见过太多人看到日志报错就急着重启进程结果问题复现三次才意识到方向错了。第五步“执行方案”一定是基于前四步的结论做出的不是拍脑袋。下面这张表是我日常排查时最常用的命令清单建议你保存下来命令作用使用场景hdfs dfsadmin -report查看节点状态、存储容量、副本情况快速了解集群整体健康度hdfs fsck / -files -blocks -locations检查文件块完整性、副本分布定位数据丢失、副本不足hdfs dfs -du -h /path查看目录和文件实际占用排查空间使用率异常hdfs dfs -ls /查看目录列表确认文件可见性hdfs dfsadmin -safemode get查看安全模式状态写入失败、只读状态时jps查看 Java 进程是否存活确认进程挂没挂df -h / mount查看磁盘空间和挂载情况排查磁盘写满、坏盘在开始任何具体场景之前先养成一个习惯每条命令跑完把输出保存成一个文件标注时间点。这能让你在复盘时看到问题的演变过程而不是只看到最后那一屏报错。2. 高频故障场景与定位实操HDFS 的故障虽然五花八门但归纳起来不会跳出这么几类NameNode 自身的元数据故障、DataNode 的节点与磁盘故障、容量与配额问题、块副本异常、读写性能劣化。下面逐个说透每个场景我会把“最可能的根因是什么、用什么命令确认、应该怎么处理”一条龙讲清楚。2.1 NameNode 相关故障安全模式、元数据损坏、内存压力NameNode 是 HDFS 的“大脑”但它同时也是整个系统里最脆弱的单点在没有 HA 的情况下。它只负责管理元数据不管数据本身所以它出问题时的表现非常集中要么是拒绝写入、要么是进程起不来。先讲安全模式Safe Mode。这是 HDFS 启动阶段的正常状态NameNode 在启动时要加载 fsimage 和 edits 日志把整个文件系统的目录树重建到内存里同时等待 DataNode 上报 block 报告确认当前副本数满足配置要求。如果启动时 DataNode 还没就绪NameNode 就会停留在安全模式对外表现为“只读”可以读文件但不能创建、删除、修改文件。客户端会报“Name node is in safe mode. The reported blocks 0.000 has reached the threshold 0.9990 of total blocks”。这种情况下先不要急着强制退出安全模式你要判断它是正常启动的过渡态还是异常状态。正常启动时等 DataNode 全部连上、block 报告齐了安全模式会自动退出如果卡了很久还在里面大概率是某个 DataNode 没起来或者 block 报告的阈值设得太高。可以用 hdfs dfsadmin -safemode get 查看状态hdfs dfsadmin -report 看有多少节点在线。如果节点数量没问题再尝试 hdfs dfsadmin -safemode leave 手动退出。但注意如果副本比例真的没达到阈值强制退出会造成数据写入和读取的不一致性属于“治标不治本”。再讲元数据损坏。这种情况我会描述成“最惨烈也最不常见”的场景。NameNode 把元数据持久化到本地磁盘的 dfs.name.dir 目录里面一个是 fsimage文件系统的完整快照一个是 edits快照之后的所有操作日志。如果服务器意外断电、磁盘坏道或者误删了文件NameNode 进程会直接起不来日志里出现 “Failed to load image from FSImageFile” 或 “EditLogFileInputStream error” 这类信息。处理元数据损坏我的建议是“先备份再修复不到万不得已别动手”。如果 fsimage 是好的只有 edits 坏可以尝试 hdfs namenode -recover 进入交互式恢复模式它会引导你选择丢弃哪些损坏的 edits 段。如果 fsimage 本身也坏了而你还有 HA 的镜像节点那就切换过去如果单 NameNode 且 fsimage 也坏了手里唯一的救命稻草就是定期做的元数据备份。很多公司有凌晨的元数据导出任务就是 hdfs dfsadmin -fetchImage 或者直接对 dfs.name.dir 做快照这在大数据集群部署策略里应该是一个必选项但现实中经常被忽略。NameNode 还有一个容易被忽略的故障——内存压力。NameNode 的元数据全放内存文件数量越多、block 越多堆内存占用越大。当集群小文件泛滥后面单讲或者 handler 数量配置不当NameNode 会频繁 Full GC表现为 RPC 延迟超高、客户端写入超时、Active NameNode 探活失败自动切换。排查方法是用 jstat 或 GC 日志看 Full GC 频率调优思路是增大 -Xmx、优化 dfs.namenode.handler.count、控制小文件数量。2.2 DataNode 相关故障进程失联、坏盘、节点下线DataNode 是真正存数据的地方它出问题的频率远高于 NameNode。毕竟一台服务器上挂了十几块盘每块盘都有自己的寿命故障是常态。DataNode 进程失联最直接的现象是 hdfs dfsadmin -report 里某个节点不见了或者 NameNode 日志里反复出现 “Lost tracker” 的记录。这种故障多数是磁盘空间写满、网络抖动、进程 OOM 被杀。我在排查时第一板斧是先看节点本身df -h 看磁盘jps 看进程top 看资源占用。如果磁盘没满、进程在但心跳老丢接下来看网络层ping 一下对端看是否有丢包。如果进程已经不在了去看 /var/log/hadoop-hdfs/hadoop-hdfs-datanode-xxx.log 的尾部日志一般会直接告诉你原因OOM、磁盘 IO 错误或者被系统 kill。坏盘是 DataNode 场景里最经典的一种。磁盘出现坏道之后读写这块盘的 block 会持续报错DataNode 会尝试把这块盘标记为 failed如果 dfs.datanode.failed.volumes.tolerated 配的默认值是 0那只要有一块盘坏整个 DataNode 进程就会退出。很多运维被这个问题折磨过DataNode 反复重启、磁盘明明有空间却总提示 volume failures。处理的关键不是反复重启而是把坏盘从 DataNode 的存储目录里剔除干净再重启进程。具体怎么做我会在下一章用一个完整的案例演示。这里先强调一个原则DataNode 的存储目录 dfs.datanode.data.dir 可以配置多个路径多块磁盘分散挂载。当你发现某块盘有问题正确做法是在这个配置里去掉故障盘对应的绝对路径重启 DataNode让剩余的盘继续服务而不是抱着侥幸心理让故障盘继续写入。2.3 磁盘与容量类故障写满、小文件、Quota容量问题是 HDFS 运维里最高频的一类告警但很多人分不清“磁盘真的满了”和“NameNode 认为满了”的区别。真实磁盘写满时DataNode 心跳上报的剩余空间已经是负值客户端写数据时 NameNode 会找不到可用的 DataNode报 “All datanodes are bad” 或 “There are no nodes available to place replicas”。这属于物理容量耗尽处理思路就是清数据或者加节点。清理时要注意先删那些可以重新生成的外部数据缓存或者临时跑批结果别一上来就把核心事实数据的副本删了。另一种“容量满”是指 NameNode 侧的状态block 总数达到了阈值或目录配额Quota耗尽。HDFS 支持对目录设置 nsquota文件数配额和 dsquota空间配额配额设低了就会导致明明磁盘还有几百个 G但写入时却报 “NameNode is in SAFE mode” 之外的 “The DiskSpace quota of /xxx is exceeded”。还有一个和大容量紧密相关的隐患——小文件问题这个必须单独拎出来讲。HDFS 的每个文件、目录、block 都要占用 NameNode 内存大约在 150 字节到 1KB 不等一个 block 无论大小都算一个元数据项。如果你的集群里有几十万个小文件每个文件只有几 KBNameNode 的内存会迅速膨胀而 DataNode 的磁盘空间其实没怎么用。这是 HDFS 设计上的一个痼疾也是大数据学习路线里绕不开的知识点。解决思路只有三条合并小文件SequenceFile、小文件合并成批量包、减少文件总数Hive 的归档分区、Spark 的 coalesce、如果业务允许加大 block size 并让文件数量更少。2.4 块副本异常与数据一致性块副本异常不像磁盘写满那么直观但它才是真正的“内伤”。你用 hdfs fsck / 扫描整个文件系统会看到三类状态healthy、under-replicated、missing。Under-replicated 表示副本数低于设定的 replication factor比如配了 3 副本但当前只有 2 个。这通常是因为某个 DataNode 宕机、磁盘故障、网络隔离导致的副本批次丢失。只要集群还有别的节点有空间NameNode 会在副本调度机制下自动补充副本这个过程叫 replication不需要人工干预。但如果长期处于 under-replicated 状态说明可用节点不足或者空间不够甚至是因为机架感知rack awareness配置有问题导致副本分配规则冲突。Missing 则是副本全丢这表示某个 block 的所有副本都不在了文件处于不可完整读取的状态。fsck 对这种情况会报 “MISSING!”数据不可恢复。这是最被动的故障状态它的根源通常不是运维失误而是早先的副本配置太低比如一开始就配了 1 副本、DataNode 坏盘没及时处理、或者副本丢失后没有足够的可用节点代偿。处理这类问题我建议的顺序是先 hdfs fsck / 知道整体情况→再用 hdfs fsck / -files -blocks -locations 去定位具体是哪些文件、哪些 block 出了问题→然后根据情况选择手动修复。修复手段有几种对被删或误操作的文件检查 trash 目录有没有残留对 under-replicated 但仍有副本的 block可以临时调高或调低 replication factor 触发一次副本策略重计算对 metadata 和保护提示的隐患顺手设置 dfs.replication 的期望值。最后如果出现大量 missing block优先关注是不是某个 DataNode 的整块磁盘报废导致尽快把节点的数据目录迁移到可用盘上。3. 一次典型故障的完整处理实录前面讲了一堆理论可能你会觉得抽象。这回我搬一个真实案例出来用“事故回放”的方式带你完整走一遍排查流程。那是某个周六晚上快十一点我值班时收到一条监控告警——集群的 NameNode 从 Active 状态切了一遍过了两分钟又切回来了。当时我用 hdfs fsck / 扫了一眼马上看到整片文件系统里冒出来一堆 under-replicated blocks数量在持续增长。直觉告诉我这不是一次规定动作的 HA 切换而是底层 DataNode 出了问题。3.1 故障现象与初始判断告警平台上一共出现了三条消息第一条是“NameNode failover event”第二条是“HDFS under-replicated blocks 5000”第三条是“DataNode process down on node-datanode-03”。前两条都可能是第三条的“果”所以我直接先看 node-datanode-03 的状态。通过跳板机登录上去jps 一看 DataNode 进程不在了再跑了一下 uptime 和查看系统日志确认机器本身没宕机是单纯的服务进程退出。此时屏幕上的第一反应是重启 DataNode。但我告诉自己先别急因为单纯重启一个 DataNode最多解决“进程不在”的问题如果根因是磁盘坏了或者配置错了重启之后过几分钟它还会继续挂。所以我把进程异常退出前的日志翻了出来看的是 /var/log/hadoop-hdfs/hadoop-hdfs-datanode-xxx.log 的最后 200 行。日志尾部直接给了答案出现了大量 “DiskOutOfSpaceException”、随后跟着 “java.io.IOException: No space left on device” 这样的报错。这基本上指向两块一是数据盘写满二是坏盘导致的文件系统异常。我先用 df -h 检查所有挂载点发现有一块 4TB 的数据盘使用率竟然是 100%但同时用 hdfs dfsadmin -report 查到的 HDFS 视角里它显示这一块盘的可用空间还有约 1.2TB。这里出现了一个割裂操作系统层面文件系统满了但 HDFS 元数据层面认为没满。这说明文件系统里堆积了 HDFS 看不见的东西典型的是本地磁盘上残留的临时文件、校验文件、或者文件系统垃圾文件也可能是文件系统 block 需要回收但被异常打断了。更大的嫌疑是磁盘坏块导致文件系统元数据错乱读取失败且空间被错误统计。3.2 日志证据链与根因定位我回过头去把日志时间轴梳理了一遍发现一个特别典型的时间序列。先是当天傍晚开始DataNode 日志里隔三差五出现 “slow block receiver” 和 “Failed to read from block” 的警告随后间隔收窄逐渐演变成 “Uncaught exception in thread Thread-XX” 和 “BP-xxxxxxxx is shutting down”。这个过程很清晰地展示了一个磁盘劣化的演变读写性能先劣化然后出现 IO 错误最终文件系统层报错导致进程崩溃。进一步坐实坏盘判断的方法是直接看系统日志执行 dmesg -T | grep -i error|fail | tail -50看到了 SCSI 层的 “medium error” 和中断卡死的记录。这个证据已经足够指向磁盘物理坏道或者控制器故障了。我并没有急着拔盘而是又做了一步验证检查 HDFS 的 block 存储目录分布df -h /data01 与 hdfs dfsadmin -report 中对应路径的容量互相核对确认 /data01 这块盘确实不在健康状态。此时整个证据链就是磁盘物理坏道 → 文件系统 IO 异常 → DataNode 频繁读写失败 → 心跳中断 → 副本调度滞后 → 大量 under-replicated blocks。3.3 修复动作与结果验证根因清楚了修复动作就顺理成章。这个节点的 DataNode 配置的 dfs.datanode.data.dir 有两块盘其中 /data01 出了问题。我的操作顺序是第一步在不对整台节点做下线的情况下先修改 hdfs-site.xml 中 dfs.datanode.data.dir 配置把 /data01 从列表里去掉指定只保留健康盘 /data02。这个操作的意图很明确让 DataNode 彻底放弃故障盘不再尝试读写它。第二步重新启动 DataNode 进程。启动完成后用 hdfs dfsadmin -report 确认该节点已回到在线状态剩余存储空间从故障盘的 1.2TB 变成了健康盘的 3.8TB因为健康盘确实还有那么多可用。第三步处理已经产生的 under-replicated blocks。由于副本恢复需要时间我不会指望它瞬间完成。先观察在 5 分钟内fsck 显示的 under-replicated 数量是否从 5000 开始下降。这是因为 NameNode 会对缺失的副本自动调度复制前提是有可用的节点和空间。这一环节里我没有做任何人工触发只是在旁边守着看它自愈。第四步在下一波数据写完之后我用 hdfs fsck / -files -blocks -locations 抽查了几个高风险目录确认所有 block 的副本数回到了 3 这个设定值。之后再把这台故障盘从服务器里安全移除换上一块新盘重新把路径加回 dfs.datanode.data.dir让 DataNode 自动把部分数据均衡到新盘上。整个处理过程持续了大概两个小时。事后我复盘时最感慨的一点是如果当初图省事直接重启 DataNode 不去翻日志那我大概率会在重启三次失败之后才意识到问题期间的每一次重启都在加剧集群的副本缺口。这也是为什么我一直强调“日志是故障现场的第一证人操作前先让日志说话”。4. 常见问题速查表与避坑指南理论讲完了案例也复盘了接下来整理一些我在各种环境里反复踩过的坑。这些经验可能不会出现在任何官方文档里但它们是用真实故障换来的。4.1 高频问题速查表我汇总了一份 HDFS 故障排查速查表按“现象→可能原因→排查动作→解决方案”四段式整理你遇到类似问题时可以直接对着找答案。现象可能原因首选排查命令解决方案客户端报 Safe modeNameNode 启动中 / 副本阈值未达hdfs dfsadmin -safemode get等待或手动 safemode leave写入报 All datanodes are bad磁盘写满 / DataNode 全部离线hdfs dfsadmin -report清理空间恢复节点读取报 Could not obtain blockblock 丢失 / DataNode 离线hdfs fsck / -files -blocks -locations检查副本补充副本DataNode 反复重启单块盘故障触发 volume failuredmesg、df -h、日志剔除坏盘路径重启磁盘有空间但写不进nsquota / dsquota 配额耗尽hdfs dfs -count -q /path调大 quota 或清理文件NameNode 频繁 Full GC元数据量过大 / 小文件太多jstat 查看 GC 日志清理小文件、调大堆内存HA 集群频繁切换RPC 超时 / 网络抖动 / NameNode 内存压力查 NameNode 日志、ping 节点优化 handler 参数、隔离网络元数据损坏起不来断电损坏 fsimage / edits查看启动日志从备份恢复、namenode -recover大量 under-replicatedDataNode 宕机 / 空间不足hdfs fsck / 统计数量扩容、等待自动复制、手动调复制因子balancer 卡住不动带宽限制 / 数据量过大hdfs dfsadmin -setBalancerBandwidth调高带宽分批执行这张表的核心价值在于帮你“快速分诊”。表里的命令大多是只读的哪怕你判断错了也不会造成二次伤害所以放心大胆去跑。4.2 日常巡检与预防性配置故障排查做多了之后你会发现真正高效的运维不是“救火”而是“防火”。我在生产环境里长期保持的巡检习惯分三个层面第一层是“每天看一眼”。早上到公司先跑一条 hdfs dfsadmin -report扫一遍有没有节点掉线、容量有没有异常增长。再跑一条 hdfs fsck / 的统计结果比较今天的 missing 和 under-replicated 数量跟昨天有没有偏差。这一步可以在crontab里定时输出到一个文本文件方便对比趋势。第二层是“每周做一次健康体检”。检查项包括NameNode 的日志里有没有频繁的 “Slow Operation” 记录各 DataNode 磁盘使用率的方差是不是过大如果一台 80% 而其他都是 40%说明数据分布有问题跑一遍 hdfs balancer 的 dry-run 模式看看 imbalance 程度。这些信息会直接暴露潜在的容量热点和数据倾斜。第三层是“关键参数提前设好”。我在部署 HDFS 集群时几乎每次都会强调这几个参数的调优dfs.replication 建议至少 3除非是临时测试环境dfs.namenode.handler.count 根据客户端并发量调比如 100 左右起步高了反而消耗锁资源dfs.datanode.handler.count 对应 DataNode 的读写并发磁盘多可以调大dfs.blocksize 一般生产环境我设 128MB默认值就是这个但如果你存取大文件可以考虑 256MB 减少元数据开销还有 dfs.namenode.safemode.threshold-pct 默认 0.999 可以稍微调低一点避免启动等待太久。另外一个容易被忽略的配置是 dfs.datanode.failed.volumes.tolerated。把它从默认的 0 调到 1 或者更高这样当一台机器只有一块盘故障时DataNode 进程不会整个退出而是继续服务健康盘。这个配置在实际运维中能帮你少收到很多“DataNode down”告警。5. 最后分享一点我的实操心得做 HDFS 运维这几年一个很深的感触是很多事故本来是可以避免的问题往往不是“能力不够”而是“流程上少了预防的那道防线”。比如坏盘问题如果你有定期的磁盘健康巡检有 failed volumes tolerated 的配置兜底有副本因子的合理设定绝大多数情况下你根本不会走到“数据丢失”那一步。按照我自己在真实环境里的体会最值得养成的一个小习惯是每次处理完故障当天就把这次问题的排查链路、根因和修复动作写进一页简单的复盘记录哪怕只是几行字也好。下次再遇到类似情况你翻一眼就知道该往哪个方向查而不是又从看第一行日志开始。这个习惯帮我省下过好几轮加班的功夫也希望你能用上。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进