如果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中要換一下文件。整個操作的過程中都不涉及集羣的重啓~