magic_kid_2010头像

HDFS 故障复盘:千万级 block 导致 FBR 超过 128 MiB,DataNode 被判定为 dead

本文记录一次 Hadoop 3.4.2 集群的实际故障。业务首先表现为 WebHDFS 写入异常,随后部分 DataNode 被 NameNode 标记为 dead,但对应物理机上的 Flink/YARN 任务仍然运行。最终定位到:DataNode 块数量过多,单次全量块汇报的 RPC 请求超过 ipc.maximum.data.length 的 128 MiB 限制。将上限调整为 256 MiB,并重启异常 DataNode 服务后,节点重新加入,块汇报恢复。

文中的主机名和业务路径已作示例化处理。日志时间保留原始时钟,未做时区换算。

一、先说结论

这次故障不能简单理解成“某台机器宕机”或“发现了坏块”。现场确认的根因是 FBR 请求超出 RPC 接收长度限制,并伴随 DataNode 与 NameNode 通信超时、心跳失联。

从故障复盘角度,可以将链路整理为:

DataNode 上的 block 数量持续增加

单次 FBR 请求报文变大

超过 128 MiB RPC 接收上限

NameNode 拒收请求,FBR 失败

本次故障还伴随通信超时和心跳失联

NameNode 将 DataNode 标记为 dead

部分块副本暂时不可用,读写和启动恢复受影响

提高上限至 256 MiB并使配置生效

分批重启异常 DataNode 服务

重新注册并完成块汇报

节点状态与业务访问恢复

这里有一个必须保留的边界:FBR 失败不是 dead 的直接判据。NameNode 将 DataNode 判定为 dead,直接依据是心跳更新时间超过过期阈值。 不能据此推出“任何一次 FBR 超限都会导致节点 dead”。

二、环境和故障现象

现场主要环境如下:

项目情况
Hadoop 版本3.4.2
DataNode 数量8 台
HDFS 副本数2 副本
NameNodeHA 部署,现场存在多组 NameNode 目标
DataNode 块数量单台已达到约千万级
本例 DataNode 存储目录配置中只有一个数据目录
业务Kafka → Logstash → WebHDFS,以及 Flink on YARN

故障最初出现在 Logstash 的 WebHDFS 输出端:

webhdfs write caused an exception:
Failed to connect to host datanode-2:9864,
Broken pipe - No message available.

后续还观察到:

  • NameNode 页面上有 3 台 DataNode 显示异常、失联。
  • 这些物理机仍然可以使用,上面的 Flink/YARN 任务也没有全部退出。
  • 一次 NameNode 重启后,启动页面长时间停留在安全模式约 91%,提示等待块汇报。
  • 调整 IPC 限制并重启异常 DataNode 服务后,节点重新加入,集群恢复。

Broken pipe 只是应用侧看到的连接异常,不能单凭它就断定是 Logstash worker、网络不通或 HDFS 坏块。

三、排查中,哪些现象容易误导?

3.1 HTTP 端口能访问,不等于 HDFS 写入正常

排查时,在 Logstash 容器内访问 DataNode 的 HTTP 端口,可以得到 302 Found。进一步测试 WebHDFS 创建临时文件,也曾成功得到 201 Created。

这些测试说明:对应时刻、对应目标的 HTTP 请求可以完成。但一次成功不能覆盖所有 DataNode,也不能证明持续写入、已有文件的 APPEND、块汇报和 NameNode 心跳都正常。

WebHDFS 的 9864 HTTP 链路与 DataNode 向 NameNode 发送 FBR 的 RPC 链路,是不同的观测对象。

3.2 fsck 显示 HEALTHY,也不能排除通信问题

我们对部分业务路径执行过:

hdfs fsck hdfs://ns1/data/example/ -files -blocks -locations

某些时刻、某些路径的结果是 HEALTHY;排查期间也有文件出现过缺失块相关提示。

这两类结果都应结合检查时间、路径范围、DataNode 在线状态理解。一次路径级 HEALTHY,不是整个集群 RPC 和心跳始终正常的证明;出现 missing block,也不应立刻认定磁盘上的所有副本都已经物理损坏。

本次根因判断最终依赖的是 RPC 超限日志、FBR 失败记录,以及配置调整后的恢复结果,而不是仅凭某一次 fsck 输出。

诊断时先使用只读检查,不要直接使用 fsck -delete 等破坏性操作。HDFS 命令文档

3.3 Flink 显示 RUNNING,不代表 HDFS 和 checkpoint 正常

物理机、DataNode 服务、YARN NodeManager、Flink TaskManager 是不同层次的对象。

DataNode 被判定为 dead,不会直接等价于物理机宕机,也不会自动让同机的所有业务进程退出。

因此,不能只检查 Flink 是否为 RUNNING,还应检查最近一次成功 checkpoint 的时间、失败原因,以及任务是否存在反压或输出阻塞。

四、关键证据:RPC 长度超限和 FBR 重试

4.1 找到 RPC 超限日志

NameNode 侧出现过类似日志:

Requested data length X is longer than maximum configured RPC length Y

其中:

  • X 是本次请求声明的长度。
  • Y 是 RPC 服务端配置的接收上限。
  • 本例原上限为 134217728 字节,也就是 128 MiB。

这里没有保留现场报错中完整的 X 数值,因此不补写一个未经记录的报文大小。遇到同类问题,应同时保存完整日志、来源 DataNode 地址和准确时间。

Hadoop RPC 服务端会检查请求长度,超过配置上限时拒绝该请求。Hadoop 3.4.2 RPC 接收检查实现

4.2 整理 DataNode 块汇报日志

最初使用下面的命令搜索:

grep 'sent block report' *datanode*.log

如果需要包含按日期轮转的普通日志文件,可以按实际命名扩大范围,例如:

grep -H 'sent block report' *datanode*.log*

如果轮转文件是 gzip 压缩文件,应另用 zgrep,不要把压缩文件当普通文本处理。

提取时需要同时保留成功和失败记录。以下是简化后的格式:

Successfully sent block report ...
The reports had 9577508 total blocks ...

Unsuccessfully sent block report ...
The reports had 9833388 total blocks ...

失败记录中的 total blocks,表示本次生成的报告涉及多少块,并不表示 NameNode 已经接收成功,也不表示这些块损坏。

4.3 合并四天记录,按真实时间点观察

将 block1.log 和 block.log 合并后,得到:

项目结果
记录时间范围2026-10-23 00:24:56.998 ~ 2026-10-26 21:08:40.339
DataNode 范围仅 datanode-1
总记录数39,866
成功记录数63
失败记录数39,803
完全重复记录0

这些记录包含不同 NameNode 目标,不能把主备或不同目标的块数直接相加。正式监控时,还应核对 nameservice、block pool 的对应关系。

下面是 namenode1 目标组中几个关键观测点:

原始日志时间报告中的 block 数量状态
10-25 12:25:00.8059,577,508所提供日志中,失败前最后一条成功记录
10-25 17:30:06.1359,833,388所提供日志中第一条失败记录
10-26 08:00:17.79910,298,944失败记录中的块数峰值
10-26 08:08:21.17110,283,769所提供日志中最后一条失败记录
10-26 10:10:39.61210,283,772此后第一条成功记录
10-26 21:08:40.33910,270,083所提供日志中该组最后一条记录

这些时间是日志观测点,不应直接当成精确的配置变更时间或完整故障起止时间。

在这里插入图片描述

图中红色表示失败汇报,蓝色表示成功汇报。红色看起来像一条连续线,是因为失败后频繁重试,点非常密集;不是只有一条失败记录。图按原始时间间隔展示,没有计算增长率,也没有平滑拟合。

五、128 MiB 限制的到底是什么?

5.1 它不是 HDFS 数据块大小限制

ipc.maximum.data.length 的单位是字节,限制的是 RPC 服务端可以接收的 IPC 消息长度。

Hadoop 3.4.2 的默认值为:

<property>
    <name>ipc.maximum.data.length</name>
    <value>134217728</value>
</property>
134217728 bytes = 128 MiB
268435456 bytes = 256 MiB

这个 128 MiB 与常见的 HDFS block size 恰好可能同为一个数值,但不是同一个概念。FBR 上报的是块信息,并不是把所有 block 的业务数据通过 RPC 再发送一遍。

另外,ipc.maximum.response.length 是客户端接收响应的长度限制,不应与服务端接收请求的 ipc.maximum.data.length 混用。Hadoop 3.4.2 默认配置

5.2 为什么不能给出“多少个 block 一定超过 128 MiB”?

因为真正被检查的是序列化后的 RPC 请求长度,而不是 block 个数。

报告的编码方式、块信息内容、请求附加信息和拆分方式都会影响最终大小。因此,“千万级 block”是本次现场的规模描述,不是所有 Hadoop 集群通用的固定故障阈值。

监控 block 数量有助于发现风险,但判断是否逼近 RPC 上限,仍应结合真实报文大小和超限日志。

六、为什么默认拆分机制没有兜住?

Hadoop 3.4.2 的相关默认配置是:

<property>
    <name>dfs.blockreport.intervalMsec</name>
    <value>21600000</value>
</property>

<property>
    <name>dfs.blockreport.split.threshold</name>
    <value>1000000</value>
</property>

其中,正常周期 FBR 的默认间隔为 6 小时;split.threshold 默认是 100 万。启动、重试等情形不能简单理解成“严格每 6 小时才会发生一次汇报”。

拆分阈值最容易被误解成:

每 100 万个 block 自动切成一个 RPC。

实际不是这样。它决定的是:多个 storage 的报告合并发送,还是按 storage 分开发送。

达到拆分条件后,也不是把同一个 storage 中的块再按固定数量分批。因此,即使设置为 0,强制按 storage 拆分,也不能保证一个很大的 storage 报告不会超限。Hadoop 3.4.2 配置说明

现场 DataNode 的数据目录配置只有一个条目:

<property>
    <name>dfs.datanode.data.dir</name>
    <value>file:///opt/hadoop/dfs/data</value>
</property>

这表示只配置了一个 HDFS 数据目录;目录下面的底层文件系统是否使用 RAID/LVM,是另一个问题。结合 FBR 日志中的 containing 1 storage report(s),可以看到该次汇报只有一个 storage report,按 storage 拆分并不能将它继续拆小。

七、为什么节点上的任务还在,DataNode 却显示 dead?

NameNode 不是通过“这台物理机还有没有其他进程”来判断 DataNode 存活,而是检查其心跳更新时间。

Hadoop 3.4.2 的 DatanodeManager 通过最近更新时间与心跳过期阈值比较,判断 DataNode 是否 dead。dead 判定实现

对于同一个 NameNode 目标,普通心跳和 FBR 在同一个 BPServiceActor 主循环中调度,长时间阻塞的同步汇报可能拖延后续心跳;是否有额外保活机制及其效果,还要结合实际配置。通信循环实现

因此,本次现象不矛盾:

  • 物理机仍然存活。
  • Flink/YARN 进程仍然运行。
  • DataNode 与 NameNode 的有效心跳已过期,被标记为 dead。

不过,仅靠 FBR 失败日志不能证明具体是哪一次调用阻塞了多久。若要进一步证明线程级阻塞路径,应补充同一时段的 RPC 超时、心跳日志和线程栈,不能将所有 FBR 失败直接等同于心跳失联。

八、这次如何恢复?

本次实际恢复动作包括:将 IPC 接收上限提高到 256 MiB,以及重启异常 DataNode 服务。之后节点重新加入,块汇报恢复。

8.1 调整接收端配置

在相关 Hadoop 进程实际使用的 core-site.xml 中配置:

<property>
    <name>ipc.maximum.data.length</name>
    <value>268435456</value>
    <description>Maximum accepted IPC message length in bytes.</description>
</property>

对于 DataNode 向 NameNode 发送 FBR 的方向,必须确认接收 FBR 的 NameNode RPC 服务实际加载了新上限。只修改 DataNode 上的 XML,不能改变 NameNode 的接收限制。

HA 环境还应检查相关 Active、Standby,以及实际接收汇报的 Observer,避免切换后重新遇到旧上限。

下面的命令可辅助核对本机 CLI 读取的配置:

hdfs getconf -confKey ipc.maximum.data.length

但它只说明该 CLI 环境读到了什么,不能单独证明已经运行的 NameNode 进程采用了新值。还需核对进程配置来源和配置生效过程。

8.2 控制变更范围,不要一次性重启所有节点

生产环境应按管理平台和 HA 维护流程执行,先检查备用节点状态及同步情况,再安排切换和重启,不要同时停止主备 NameNode。NameNode HA 运维说明

异常 DataNode 服务也应分批处理。尤其在双副本且已有多台节点异常时,继续同时停止其他 DataNode,可能使更多块失去可用副本。

本次“重启后恢复”不应理解为重启修复了坏块,而是配置上限调整后,相关进程重新建立通信并完成块汇报。

8.3 恢复后检查什么?

# 检查 DataNode 在线情况和集群报告
hdfs dfsadmin -report

# 检查安全模式状态,不是强制退出
hdfs dfsadmin -safemode get

# 针对受影响路径做只读检查
hdfs fsck hdfs://ns1/data/example/ -files -blocks -locations

同时检查:

  1. NameNode 页面上的节点是否恢复,Last contact 是否持续更新。
  2. DataNode 是否重新出现成功 FBR,RPC 超限日志是否停止。
  3. 安全模式是否正常退出,缺失块、低副本指标是否改善。
  4. 原先失败的业务写入或 JAR 读取是否恢复。
  5. Flink 是否重新完成 checkpoint,而不仅是保持 RUNNING。

NameNode 启动卡在安全模式时,不要仅为了让页面变绿而直接强制退出安全模式。先确认哪些块尚未上报、哪些副本当前不可访问。启动安全模式会等待足够的块副本信息上报,相关机制可参考 HDFS 架构说明。

九、将 block 数量接入 Grafana

这次通过日志重建了块数曲线。长期运行不应依赖人工 grep,而应持续采集。

9.1 NameNode 视角:每台 DataNode 已知的块数

下面的命令使用 jq 解析 HTTP JMX 返回值。主机名应替换为当前实际 Active 地址,认证方式按集群配置处理。

curl -fsS --connect-timeout 5 --max-time 15 \
  'http://nn-active:9870/jmx?qry=Hadoop:service=NameNode,name=NameNodeInfo' \
| jq -r '
  ["DataNode","blocks","lastContact_s","lastBlockReport_min"],
  (
    .beans[0].LiveNodes
    | fromjson
    | to_entries[]
    | [.key, .value.numBlocks, .value.lastContact, .value.lastBlockReport]
  )
  | @tsv
'

其中 LiveNodes.numBlocks 是 NameNode 视角。块汇报异常时,该视角可能不完整;采集器也不能因为某个节点不在 LiveNodes 中,就直接把它的块数填成 0。NameNode JMX 字段实现

9.2 DataNode 视角:每个 volume 的块数

curl -fsS --connect-timeout 5 --max-time 15 \
  'http://datanode-1:9864/jmx?qry=Hadoop:service=DataNode,name=DataNodeInfo' \
| jq '
  .beans[0].VolumeInfo
  | fromjson
  | to_entries[]
  | {volume: .key, blocks: .value.numBlocks}
'

VolumeInfo 包含各存储卷的本地块数。它与单个 namespace、单次 FBR 的统计范围不一定相同,监控命名时要写清口径。DataNode 存储卷字段实现

还可以查看 BPServiceActorInfo 中的 block pool、目标 NameNode 和报告大小相关字段:

curl -fsS --connect-timeout 5 --max-time 15 \
  'http://datanode-1:9864/jmx?qry=Hadoop:service=DataNode,name=DataNodeInfo' \
| jq '.beans[0].BPServiceActorInfo | fromjson'

其中 maxBlockReportSize 不能直接当作完整 RPC 长度:它是该轮已记录报告的块列表编码大小统计,不包括全部请求开销;失败时还可能为 0。maxDataLength 反映 DataNode 侧读取的配置,也不能替代对 NameNode 接收端实际配置的确认。不要把这些字段的比值当成精确的安全余量。报告统计实现

9.3 有原始数据,还需要接通采集链路

Hadoop JMX → exporter/采集脚本 → Prometheus → Grafana

先查看现有 Hadoop 面板的数据源和采集 job。如果使用 Prometheus,可在 Grafana Explore 中按实际 job 搜索 block 相关指标:

count by (__name__) (
  {job="实际的Hadoop采集job", __name__=~".*[Bb]lock.*"}
)

若 exporter 的 /metrics 已有对应指标,检查 Prometheus 抓取和过滤配置;若 JMX 有、exporter 没有,则需要补采集。

特别注意,LiveNodes、VolumeInfo 是嵌套 JSON 字符串,普通 JMX 数值匹配规则不一定自动展开它们。

新增指标可以采用如下形式,以下名称为自定义示意,并不是 Hadoop 保证已有的指标名称:

# TYPE hdfs_datanode_blocks gauge
hdfs_datanode_blocks{nameservice="ns1",datanode="datanode-1:9866"} 10270083

建议使用已有 exporter 扩展字段;也可以用定时脚本读取 JMX,通过 node_exporter 的 textfile collector 输出 .prom 文件。文件应原子更新,并单独监控采集成功状态、数据新鲜度;采集失败不能伪装成块数为 0。node_exporter textfile collector

Grafana 面板按 DataNode 展示原始 Gauge 值,不要用 rate() 代替当前存量,也不要把 HA 主备的相同统计相加。不同 exporter 的名称不同,以实际暴露结果为准。Grafana Prometheus 查询说明

十、提高到 256 MiB 后,还需要做什么?

256 MiB 解决了当前报文被拒收的问题,但它没有减少现有 block,也没有保证未来永远不会再次超限。官方设置这一限制本身就包含避免过大消息造成内存风险的考虑,不能无限放大。

后续治理应同时覆盖三件事:

10.1 治理 block 数量,而不只看磁盘使用率

排查哪些业务持续产生大量小文件,制定可验证的合并和保留策略。清理前确认数据归属、下游依赖和恢复需求,通过 HDFS 正常接口操作,不要直接删除 DataNode 的块文件。

不能仅凭“单台一千万块”就断言全是小文件,也不能仅凭磁盘还有空闲就认为元数据和 FBR 没有压力。

10.2 冷数据仍需纳入存储管理

数据只是不经常读取,并不表示其仍在 DataNode 上保存的块可以不参与必要的块汇报。块汇报用于描述 DataNode 持有的块,不以业务的读取热度筛选。HDFS 块汇报与副本管理说明

长期不用的数据,可以评估生命周期清理或迁往独立归档存储。不要把“迁移 HDFS 存储介质”“降低副本”当成完全免除块汇报的办法。具体方案要结合可靠性要求,而不是为了压低指标直接删数据或降副本。

10.3 监控心跳、块数和业务恢复能力

建议至少监控:

  • 每台 DataNode、每个 volume 的 block 数量。
  • DataNode 存活状态、最近联系时间和最近块汇报时间。
  • RPC 长度超限日志、FBR 失败及耗时。
  • NameNode RPC、JVM 堆内存和 GC。
  • 缺失块、低副本和业务写入失败。
  • Flink 最近一次成功 checkpoint 的时间。

不能将通用 block 数量阈值直接标成“128 MiB 的上限”。阈值应结合本集群实际报告大小、增长情况和变更后的承载余量确定。

总结

这次问题最容易误判的地方,是“机器和业务任务还在运行,但 DataNode 在 HDFS 中已经 dead”。

最终定位表明:DataNode 块数量增长,使单次 FBR 请求超过原有 128 MiB 接收限制;现场同时发生通信、心跳异常,NameNode 最终将相关 DataNode 标记为 dead。提高上限到 256 MiB,并重启异常 DataNode 服务后恢复。

排查同类问题时,记住以下几点:

  1. Broken pipe 是应用侧症状,不是根因结论。
  2. RPC 消息长度上限与 HDFS block size 不是一回事。
  3. dfs.blockreport.split.threshold 按 storage 拆分,不是每固定数量的块切一包。
  4. FBR 失败不等于必然 dead;dead 的直接判据是心跳过期。
  5. 提高 IPC 上限是当前恢复措施,长期仍要治理块数量并持续监控。

HDFS 的容量规划,既要看存了多少 TB,也要看管理了多少个 block。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/magic_kid_2010/article/details/169266237

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--