一、问题描述
某生产环境中发现YupDB运行的Hadoop集群中有个datanode不能正常启动,查看datanode的日志发现:
org.apache.hadoop.util.DiskChecker$DiskErrorException: Too many failed volumes - current valid volumes: 11, volumes configured: 12, volumes failed: 1, volume failures tolerated: 0
原因是该datanode有一块磁盘不能正常读写,而集群中配置了每个节点最大允许磁盘损坏的数量为0,导致不能正常启动。hdfs-site.xml中相关配置如下:
<property>
<name>dfs.datanode.failed.volumes.tolerated</name>
<value>0</value>
</property>
因为上面的配置为0,当datanode节点有磁盘损坏时,认为此datanode不可用。
二、排查过程
为了尽快恢复出问题的datanode,修改dfs.datanode.failed.volumes.tolerated为1,设置最大允许损坏的磁盘数量为1。重启集群后发现既作为namenode又作为datanode的节点上datanode的进程不能正常启动,查看日志发现:
org.apache.hadoop.util.DiskChecker$DiskErrorException: Invalid volume failure config value: 1
原因是既作为namenode又作为datanode的节点只配置了1块盘作为datanode的数据存储,而我们又把dfs.datanode.failed.volumes.tolerated这个参数配置成了1,所以datanode进程不能正常启动。将既作为namenode又作为datanode节点上hdfs-site.xml文件中的dfs.datanode.failed.volumes.tolerated参数改为0。
进行上面的修改后,重启hdfs,结果一直处于安全模式。查看是有数据丢失。因为集群设置的是两副本,就算有一块磁盘损坏也不会造成数据丢失,困惑中。
为什么会造成数据丢失呢,难道是有人手动修改副本数?去50070页面查看文件副本数,发现查看的数据都只有一份副本。决定自己put一个文件试试看有几个副本。执行hadoop fs -put命令put一个文件到hdfs上,再去查看竟然只有一份副本。好吧,磁盘损坏,还没有备份,数据一定会丢失。
三、问题分析及解决
为什么配置是两副本而实际放到hdfs之后只有一个副本呢?这就要涉及到rack(机架)的概念了。
数据存到hdfs上时,如果配置的是两副本,先根据集群中节点的繁忙程度选定第一个副本存在哪个datanode节点,如果datanode处于不同的rack中,根据就近原则选定跨交换机最少的rack中datanode存储第二份副本。下面举例说明为什么会出现配置两份副本,但是实际存储只有一份副本的情况。
集群共四台机器:
- hm(主namenode兼职datanode并且只有一块盘用作datanode数据存储,位于rack0)
- hb(备namenode兼职datanode并且只有一块盘用作datanode数据存储,位于rack1)
- hs1(datanode1,位于rack1)
- hs2(datanode2,位于rack1)
正常情况一份数据写到hdfs,理论上第一个副本会有两种情况,写到rack0中的hm节点,或者写到rack1中的hb、hs1、hs2之中的某个节点。相应的第二份副本则会存到另外一个机架的节点中。如果仅仅是这样还不会出现只有一份副本的情况,那么为什么会出现缺少副本的情况呢,原因在于配置的dfs.datanode.du.reserved参数,给非hdfs使用的空间保留的过大,导致hm节点不能写入数据。所以数据就都只有一个副本了。
基于上述的配置,数据的第一份副本写入到rack1中的hs1、或者hs2中,正常会去rack0中的hm写第二份副本,但现在hm节点给非hdfs使用的空间保留的过大,不能写入数据,所以只有一副本。
解决上述问题的办法有两个:
1. 将所有服务器都放到一个机架内。
2. 将能写入数据的节点放到不同的机架内。
做好了修改之后,再次put一个文件到hdfs上之后,发现副本数恢复正常配置的两份,证明修改生效。