聊聊复杂网络环境下 hdfs 的 BlockMissingException 异常|参数 dfs.client.use.datanode.hostname
1 从一个复杂网络环境下的 hdfs 报错问题聊起
大家知道,企业真实的网络环境是复杂多变的,这可能有多种原因:
一方面,单台服务器可以安装多块网卡配置多个 IP,而且还可以做网卡绑定 NIC bonding/NIC teaming,链路聚合 Link Aggregation 等;
另一方面,容器技术如 docker/k8s,也通过 linux 的 network namespace 提供了多个隔离的网络命名空间和对应的多个 IP;
还有一个原因,在采用公有云私有云混合云的部署架构时,单台服务器经常会配置一个面向外部的公网 IP,和面向内部的私网 IP。
在复杂的网络环境中部署并使用 hadoop 时,如果服务端的配置或客户端的使用不当,就可能会遇见各种问题:
其中一个常见的问题是,hdfs 客户端读写 datanode 数据时报错 BlockMissingException- 在公有云如 aws EC2 或阿里云 ecs 上搭建 hadoop 集群时,就经常会出现该错误;
某示例报错的详细错误信息如下:
2 BlockMissingException 的问题原因和解决方案
BlockMissingException 的问题原因,主要有两个:
第一个原因是,hdfs 文件系统发生了异常,报错信息中所提示的 hdfs 块确实丢失了:此时通过命令 “hdfs fsck /”验证文件系统的完整性时,会报告丢失的块信息;(也可以由系统管理员通过命令 “hadoop dfsadmin -report”检查文件系统状态);
第二个原因是,hdfs 文件系统正常且没有 hdfs 块丢失,但是报错信息中所提示的 hdfs 块(比如 BP-XXXX-10.46.10.80-XXXX:blk_xx_xx file=xxxxx),客户端无法通过特定的 IP 或 HOSTNAME 创建到对应 datanode 的 TCP 连接进而读取 hdfs 块数据:此时通过命令 “hdfs fsck /”或 “hadoop dfsadmin -report” 检查 hdfs 文件系统健康状况时,都是提示文件系统时健康且完整的;
对应于上述 BlockMissingException 的两个原因,主要的解决方案也有两大类:
对于真实发生了 hdfs block 块丢失的情形:可以进一步分析 hdfs block 丢失的原因,常见的有部分 datanode 进程启动异常,或部分 datanode 节点的磁盘故障,此时针对性地进行修复并重启即可(如果丢失的数据确实无法修复,在业务确认后,可能不得不通过命令 “hdfs fsck / -delete” 删除损坏的 block 对应的上层 hdfs 文件;)
对于没有发生 hdfs block 块丢失,而仅仅是由于 hdfs 客户端无法通过特定的 IP 或 HOSTNAME 创建到 datanode 的 TCP 连接进而读取 hdfs 块数据的情形:可能需要检查并修复客户端和 datanode 之间的端口级别的网络连通性(ping datanode-ip, telnet datanode-ip datanode-port),也可能需要配置客户端参数 dfs.client.use.datanode.hostname=true;
3 参数 dfs.client.use.datanode.hostname 的技术背景
根据 hdfs 读写文件的内部机制: hdfs 客户端读写 hdfs 文件时,客户端首先需要创建到 namenode 的 TCP 连接,然后由 namenode 告知客户端该文件特定 block 对应的 datanode 信息,此后客户端才会去创建到特定 datanode 的 TCP 连接进而读写特定 block 数据(其实这里还有个细节:namenode 会告知客户端特定 block 对应的多个 datanode,而客户端只需要创建到其中某一个 datanode 的 TCP 连接就可以读写数据了);
默认情况下,上述读写流程中,namenode 告知客户端该文件特定 block 对应的 “datanode 信息",该 “datanode 信息" 就是 datanode 节点的 IP 地址,可能是内网 IP,也可能是外网 IP,如果是内网 IP,外部的客户端读写 block 数据时,就会因无法访问指定的内网 IP 而报错 BlockMissingException;
当配置参数 dfs.client.use.datanode.hostname=true 时,上述读写流程中,namenode 告知客户端该文件特定 block 对应的 “datanode 信息",该 “datanode 信息" 返回的就是 datanode 节点的 hostname 了,如果客户端正确地配置了/etc/hosts 文件或 ndns 服务,就可以解析该 hostname 为可访问的公网 IP,就能够正常创建连接读写 block 数据了;
可以通过如下命令,查询相关参数的配置值:
所以概括起来,在复杂的网络环境中搭建 hadoop 集群时,比如公有云 aws EC2 或阿里云 ecs,为确保不会出现上述 BlockMissingException 问题:
hadoop 服务端配置 namenode/datanode 监听 wildcard 通配符地址:绑定到通配符地址"0.0.0.0" 后,私网 IP 和公网 IP 都会被监听;(set the value of dfs.namenode.rpc-bind-host to 0.0.0.0 and Hadoop will listen on both the private and public network interfaces allowing remote access and datanode access; The wildcard is a special local IP address, It usually means "any" and can only be used for bind operations.)
(可选)hadoop 服务端配置 dfs.datanode.use.datanode.hostname=ture:表示 datanode 之间的通信也通过域名方式,这样能够使得更换内网 IP 变得十分简单、方便,而且可以让特定 datanode 间的数据交换变得更容易,但与此同时也存在一个副作用,当 DNS 解析失败时会导致整个 Hadoop 不能正常工作,所以要保证 DNS 的可靠;
客户端配置 dfs.client.use.datanode.hostname=true;
客户端配置/etc/hosts 文件或 dns 服务,将主机名与 DataNode 服务器的公网 ip 进行映射;
其实关于 Namenode 返回 datanode 信息给客户端时,到底应该用 hostname 还是 IP 来标识 datanode,不同时期 hadoop 有不同考量:
早期 nn 使用 hostName 来标志 dn,但为了减轻 dns 解析带来的性能下降,hadoop 通过 HADOOP-985 提供了通过 IP 标识 dn 的功能;
后期在面对云原生和容器化带来的复杂的网络环境时,hadoop 又通过 HDFS-3150 添加了通过 hostname 标识 dn 的功能,并提供了可配置参数 dfs.client.use.datanode.hostname/dfs.datanode.use.datanode.hostname
其它相关 jira:Using socket address for datanode registry breaks multihoming:https://issues.apache.org/jira/browse/HADOOP-6867
4 相关源码与参考链接
版权声明: 本文为 InfoQ 作者【明哥的IT随笔】的原创文章。
原文链接:【http://xie.infoq.cn/article/85a769d1fbabeef405a18c84c】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论