如果Hadoop集群已经在运行了,这时可能需要动态的添加新的数据节点到Hadoop系统中去,或者将某个数据节点下线,由于业务的需要,集群是不能重启的,那么具体的DataNode添加、删除步骤是什么样的呢?
下面以DataNode的上线为例详细说明下如何动态的给HDFS集群新增数据节点(Hadoop2.0版本)。
首先简单说下几个相关的配置文件。
(1)由dfs.hosts配置选项指定的白名单文件,当要新上线数据节点的时候,需要把数据节点的名字追加在此文件中;
(2)由dfs.hosts.exclude配置选项指定的黑名单文件,当要下线数据节点的时候,需要把数据节点的名字追加在此文件中;
下面开始步骤(假设所有新增数据节点上的Hadoop环境都已经部署完毕):
Step1:关闭新加入数据节点的防火墙。
Step2:在两个NameNode节点的hosts文件中加入新增数据节点的hostname。
Step3:在每个新增数据节点的hosts文件中加入两个NameNode的hostname。
Step4:在两个NameNode上,打通向新增数据节点无密钥SSH登录的通道。
Step5:在两个NameNode上的dfs.hosts指定的白名单文件中追加上所有新增的数据节点的hostname,注意是追加!并且,保证在dfs.hosts.exclude指定的黑名单文件中不含有新增的数据节点的hostname。
Step6:找一个客户端,配置文件和其他节点一致,执行如下刷新命令:
hdfs dfsadmin -refreshNodes
(此步也可以在任何其他节点上进行)
Step7:还是在第6步的客户端上操作,此时需要更改下hdfs-site.xml中的配置选项,将类似于如下的配置选项:
<property> <name>dfs.namenode.rpc-address.mcs.nn0</name> <value>namenode0:9000</value> </property> <property> <name>dfs.namenode.rpc-address.mcs.nn1</name> <value>namenode1:9000</value> </property>
改为:
<property> <name>dfs.namenode.rpc-address.mcs.nn0</name> <value>namenode1:9000</value> </property> <property> <name>dfs.namenode.rpc-address.mcs.nn1</name> <value>namendoe0:9000</value> </property>
Step8:在第7步操作的客户端上重新执行命令:
hdfs dfsadmin -refreshNodes
Step9:在数据节点上启动DataNode进程,命令如下:
hadoop-daemon.sh start datanode
Step10:查看数据节点进程的情况(通过日志),查看NameNode的web界面。
Step11:在两个NameNode节点上,更改slaves文件,将要上线的数据节点hostname追加到slaves文件中。
至于第7步中为什么要更改配置之后在进行第8步的刷新,是因为在Hadoop2.x版本中引入了Federation机制,用户可根据情况定义多组NameNode,每一组有两个NameNode,一个为active,另一个为standby,实现了HA。由于执行刷新命令的节点相当于一个Client,Client将触发当前nameservice中的第一个NameNode执行刷新命令,要使得两个NameNode都刷新,则要更改下配置之后再刷新。具体来说刷新是调用如下方法:
public int refreshNodes() throws IOException { int exitCode = -1; DistributedFileSystem dfs = getDFS(); dfs.refreshNodes(); exitCode = 0; return exitCode; }
通过获取文件系统对象FileSystem之后,调用FSNamesystem类中的refreshNodes方法:
void refreshNodes() throws IOException { checkOperation(OperationCategory.UNCHECKED); checkSuperuserPrivilege(); getBlockManager().getDatanodeManager().refreshNodes(new HdfsConfiguration()); }
在检查完相应的操作权限之后,最后由DatanodeManager类来执行具体的刷新实现:
public void refreshNodes(final Configuration conf) throws IOException { refreshHostsReader(conf); namesystem.writeLock(); try { refreshDatanodes(); } finally { namesystem.writeUnlock(); } }
其中,refreshHostsReader是重新读取dfs.hosts指定的配置文件,将其中的内容加载到内存中,更新白名单列表includes。之后,执行最关键的一步,刷新数据节点列表:
private void refreshDatanodes() throws IOException { for(DatanodeDescriptor node : datanodeMap.values()) { // Check if not include. if (!inHostsList(node)) { node.setDisallowed(true); // case 2. } else { if (inExcludedHostsList(node)) { startDecommission(node); // case 3. } else { stopDecommission(node); // case 4. } } } }
此处的inHostsList方法是当数据节点发来心跳进行注册的时候,判断此节点是否在白名单之中,如果不在的话,就会拒绝该数据节点的注册请求,并抛出异常:
DisallowedDatanodeException:"Datanode denied communication with namenode: " + nodeID
至于数据节点的下线,和上述步骤类似,只不过Step5中要换一下文件。整个操作的过程中都不涉及集群的重启~