天天看點

深入了解 HDFS(二):Replica

作者:冰心de小屋

對于我們上傳的檔案,HDFS 會複制多份,主要是為了降低資料丢失的風險,複制檔案背後是複制 block,block 最終會存儲在 datanode 中,那麼在該過程中 namenode 參考了哪些因素選擇的 datanode?内部處理流程又是如何?一起來分析。

1. 複制粒度

對于原始的 block 和複制的 block,統一叫做 replica,下面是和複制相關的一些細粒度參數:

  • dfs.blocksize: replica 的大小,預設 128m;
  • dfs.replication: 副本數量,預設 3,生成 replica 的過程是串行的,同一時刻隻能有一個 writer;
  • dfs.replication.max:副本最大數量,預設 512,主要用于對 dfs.replication 的校驗;
  • dfs.namenode.replication.min:副本最小數量,預設 1,主要用于 replica 生成後數量的校驗。

2. 參考因素

從類别上可分為:用戶端因素和服務端因素。

用戶端因素:

  • CreateFlag.NO_LOCAL_WRITE:防止寫入和用戶端相同 IP 的 datanode,假設用戶端和 datanode 在同一個主機上,用戶端上傳檔案,預設政策下 datanode 都會存儲 1 個副本,這樣一來資料肯定不均衡,是以通過設定 NO_LOCAL_WRITE 可有效避免;
  • CreateFlag.NO_LOCAL_RACK:防止寫入和用戶端相同機架的 datanode,和 NO_LOCAL_WRITE 類似,隻不過 datanode 換做成同機架下的所有 datanode 執行個體。
  • favoredNodes:如何服務端開啟了 dfs.namenode.block-placement-policy.default.prefer-local-node 的設定,用戶端上傳檔案時,通過 DistributedFileSystem.createFile -> HdfsDataOutputStreamBuilder.favoredNodes 進行設定,namenode 會根據上傳的 favoredNodes 優先選擇。

服務端因素:

  • dfs.block.replicator.classname:replication 配置設定政策,直接影響到 replication 配置設定到哪個 datanode 中,預設配置設定政策 BlockPlacementPolicyDefault,除此之外還有 BlockPlacementPolicyRackFaultTolerant 和 BlockPlacementPolicyWithNodeGroup 比較常用這裡會重點講解,其餘的配置設定政策還有 AvailableSpaceBlockPlacementPolicy、AvailableSpaceRackFaultTolerantBlockPlacementPolicy 和 BlockPlacementPolicyWithUpgradeDomain,細節方面大家可檢視下官方文檔,後續我會酌情補充。
  • dfs.replication: 副本數量,預設 3,會決定選擇 datanode 數量;
  • dfs.namenode.avoid.write.stale.datanode:不允許寫入處于 stale 狀态的 datanode,預設 false;
  • dfs.namenode.write.stale.datanode.ratio: 按照 stale 和 live 狀态分别統計 datanode 數量,如果二者比值小于等于這個參數,即使設定了 dfs.namenode.avoid.write.stale.datanode = true ,也會選擇處于 stale 狀态下的 datanode。

3. Replica 配置設定政策

假設搭建了下面的叢集,叢集的副本數為 3:

深入了解 HDFS(二):Replica

3.1 BlockPlacementPolicyDefault:預設政策

3.1.1 未配置機架感覺

所有 datanode 會預設配置設定到 /default-rack 虛拟機架中,屏蔽某些次要參數,該政策主要處理流程:

  1. 校驗用戶端所在節點是否有 datanode 執行個體:
  2. 如果有:選擇該 datanode;
  3. 如果沒有:從可用的 datanode 清單中随機選擇 1 個。
  4. 對于每次選擇的 datanode 都需要在可用清單中删除;
  5. 依次從可用清單中選擇第 2 個、第 3 個。
深入了解 HDFS(二):Replica

3.1.2 機架感覺

如果想開啟 HDFS 機架感覺,你需要做幾件事:

  1. 編寫腳本:主要目的是為了查詢 datanode 所在機架。腳本隻有 1 個輸入參數主機 ip 或主機名,是以你需要根據主機 ip 或主機名輸出比對的機架資訊,可參考下面的腳本進行配置:
#!/bin/bash
declare -A topology
topology["172.17.48.1"]="rack1"
topology["172.17.48.2"]="rack1"
topology["172.17.48.3"]="rack2"
topology["172.17.48.4"]="rack2"
topology["172.17.48.5"]="rack3"
topology["172.17.48.6"]="rack3"
topology["datanode-001"]="rack1"
topology["datanode-002"]="rack1"
topology["datanode-003"]="rack2"
topology["datanode-004"]="rack2"
topology["datanode-005"]="rack3"
topology["datanode-006"]="rack3"
rack=${topology[$1]}
[[ ${#rack} > 0 ]] && echo "/bj/$rack" || echo "/default-rack"           

複制代碼

  1. 在 core-site.xml 中配置 net.topology.script.file.name 的值為腳本的完整路徑名;
<property>
  <name>net.topology.script.file.name</name>
  <value>/home/df66a0d7/topology.sh</value>
</property>           

複制代碼

  1. 重新開機叢集後,輸出叢集拓補結構:hdfs dfsadmin -printTopology
深入了解 HDFS(二):Replica

從結果可以看出 HDFS 已經開啟了機架感覺,那接着分析處理流程:

  1. 校驗用戶端所在節點是否有 datanode 執行個體:
  2. 如果有:選擇該 datanode;
  3. 如果沒有:從可用的 datanode 清單中随機選擇 1 個。
  4. 從第 1 個 datanode 所在機架外的其他機架,随機選擇 1 個 datanode 作為第 2 個;
  5. 在第 2 個 datanode 所在機架随機選擇 1 個作為第 3 個。
深入了解 HDFS(二):Replica

3.2 BlockPlacementPolicyRackFaultTolerant:貪婪政策

該政策的主要目是盡可能的在不同的機架上選擇 datanode,主要處理流程:

  1. 通過調用 getMaxNodesPerRack 計算平均每個機架可選擇的最大 datanode 數量;
  2. 根據計算的 maxNodesPerRack 調用 chooseTargetInOrder 一次性選擇和副本數相同數量的 datanode;
  3. 為了保證選擇的 datanode 和副本數相等,内部又做了很多保證機制。
深入了解 HDFS(二):Replica

如果決定使用貪婪政策,你需要編寫腳本,開啟機架感覺功能,同時在 hdfs-site.xml 中需要設定屬性:

<property>
  <name>net.topology.script.file.name</name>
  <value>/home/df66a0d7/topology.sh</value>
</property>
<property>
  <name>dfs.block.replicator.classname</name>
  <value>org.apache.hadoop.hdfs.server.blockmanagement.BlockPlacementPolicyRackFaultTolerant</value>
</property>

           

複制代碼

3.3 BlockPlacementPolicyWithNodeGroup:分組政策

1 個 42U 19 英寸的機櫃,除了預留散熱和交,換機位置,可以輕松部署 16 台 1U 伺服器。在同一個機架内,如果你想更細粒度的對 datanode 進行分組選擇,可以試試分組政策。

深入了解 HDFS(二):Replica

分組政策内部處理流程:

  1. 校驗用戶端所在節點是否有 datanode 執行個體:
  2. 如果有:選擇該 datanode;
  3. 如果沒有:從可用的 datanode 清單中随機選擇 1 個。
  4. 從第 1 個 datanode 所在機架外的其他機架,随機選擇 1 個 datanode 作為第 2 個;
  5. 在第 2 個 datanode 所在機架不同的 node-group 中選擇 1 個 作為第 3 個。
深入了解 HDFS(二):Replica

使用分組政策你需要進行如下操作:

  1. 開啟機架感覺:
#!/bin/bash
declare -A topology
topology["172.17.48.1"]="/rack1/group11"
topology["172.17.48.2"]="/rack1/group11"
topology["172.17.48.3"]="/rack2/group21"
topology["172.17.48.4"]="/rack2/group21"
topology["172.17.48.5"]="/rack2/group22"
topology["172.17.48.6"]="/rack2/group22"
topology["datanode-001"]="/rack1/group11"
topology["datanode-002"]="/rack1/group11"
topology["datanode-003"]="/rack2/group21"
topology["datanode-004"]="/rack2/group21"
topology["datanode-005"]="/rack2/group22"
topology["datanode-006"]="/rack2/group22"
rack=${topology[$1]}
[[ ${#rack} > 0 ]] && echo "$rack" || echo "/default-rack/group1"           

複制代碼

  1. 在 core-site.xml 中設定:
<property>
  <name>net.topology.impl</name>
  <value>org.apache.hadoop.net.NetworkTopologyWithNodeGroup</value>
</property>
<property>
  <name>net.topology.nodegroup.aware</name>
  <value>true</value>
</property>           

複制代碼

  1. 在 hdfs-site.xml 中設定:
<property>
  <name>net.topology.script.file.name</name>
  <value>/home/df66a0d7/topology.sh</value>
</property>
<property>
  <name>dfs.block.replicator.classname</name>
  <value>
    org.apache.hadoop.hdfs.server.blockmanagement.BlockPlacementPolicyWithNodeGroup
  </value>
</property>           

複制代碼

4. 快速搭建 HDFS

4.1 主機清單

172.17.48.7 namenode


172.17.48.1 datanode-001
172.17.48.2 datanode-002
172.17.48.3 datanode-003
172.17.48.4 datanode-004
172.17.48.5 datanode-005
172.17.48.6 datanode-006

           

複制代碼

4.2 跳轉主機環境準備

我通常會選擇 namenode 作為跳轉主機,下面使用 root 使用者操作:

# 確定目前使用者為 172.17.48.10 root使用者
# 1. 生成密鑰資訊,一路回車即可
ssh-keygen


# 2. 安裝 pdsh 可以批量操作主機
yum install -y pdsh


# 3. 将 namenode 的密鑰複制到其他主機,包括自己的主機
for i in `seq 1 7`
do
  # 輸出其他主機密碼
  ssh-copy-id 172.17.48.$i
done


# 4. 編輯:/etc/hosts
echo "172.17.48.7 namenode" >> /etc/hosts
for i in `seq 1 6`
do
  echo "172.17.48.$i datanode-00$i" >> /etc/hosts
done


# 5. 編輯:/etc/profile
vim /etc/profile


export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64
export HADOOP_HOME=/home/5CC99CC1/hadoop-3.3.6
export PATH=$PATH:$HOME/.local/bin:$HOME/bin:$JAVA_HOME/bin:$HADOOP_HOME/bin
export ips="172.17.48.1,172.17.48.2,172.17.48.3,172.17.48.4,172.17.48.5,172.17.48.6,172.17.48.7"


alias n1="ssh namenode"
alias d1="ssh datanode-001"
alias d2="ssh datanode-002"
alias d3="ssh datanode-003"
alias d4="ssh datanode-004"
alias d5="ssh datanode-005"
alias d6="ssh datanode-007"


# 6. 傳送給其他節點
for i in `seq 1 7`
do
  scp /etc/hosts 172.17.48.$i:/etc/hosts
  scp /etc/profile 172.17.48.$i:/etc/profile
done

           

複制代碼

4.3 安裝 JDK

在跳轉機上使用 root 使用者操作。

#!/bin/bash
# 關閉防火牆,ips 參考 4.2
pdsh -w $ips "systemctl stop firewalld"
pdsh -w $ips "systemctl disable firewalld"


# 安裝JDK
pdsh -w $ips "echo y | yum install java-1.8.0-openjdk-devel"


# 添加使用者,最好随機生成使用者名和密碼,避免使用 hadoop:hadoop,很容易被破解
pdsh -w $ips "adduser -m 5CC99CC1 -G wheel"
pdsh -w $ips "echo '5CC99CC1:CFA02D2C68D5'|chpasswd"

           

複制代碼

4.4 安裝 HDFS

在跳轉機上使用 5CC99CC1 使用者操作。

#!/bin/bash
# 1. 通用
NAME=hadoop-3.3.6
FILE=$NAME.tar.gz
HOME=/home/5CC99CC1
TMP_HOME=$HOME/dfs/tmp


# 2. NameNode
NAME_NODE_NAME=hadoop-3.3.6
NAME_NODE_HOST=namenode
NAME_NODE_PORT=8020
NAME_NODE_STORAGE=$HOME/dfs/name
NAME_NODE2_STORAGE=$HOME/dfs/namesecondary


# 3. DataNode
DATA_NODE_NAME=hadoop-3.3.6
DATA_NODE_HOST=datanode-001,datanode-002,datanode-003,datanode-004,datanode-005,datanode-006
DATA_NODE_STORAGE=$HOME/dfs/data
DATA_NODE_REPLICATION=3


# 4. 安裝 DataNode 
target=$(cd "$(dirname "$0")"; pwd)
cd $target


# 5. 處理安裝包
if [ ! -f $FILE ]
then
 wget https://mirrors.aliyun.com/apache/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz?spm=a2c6h.25603864.0.0.2e278a13NqaPHe -O hadoop-3.3.6.tar.gz
fi


tar -xvf $FILE


# 6. 設定 core-site.xml
cat << EOF > $NAME/etc/hadoop/core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at


    http://www.apache.org/licenses/LICENSE-2.0


  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->


<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://$NAME_NODE_HOST:$NAME_NODE_PORT</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>$TMP_HOME</value>
    </property>
</configuration>
EOF


# 7. 設定 hdfs-site.xml
cat << EOF > $NAME/etc/hadoop/hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at


    http://www.apache.org/licenses/LICENSE-2.0


  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->


<!-- Put site-specific property overrides in this file. -->
<configuration>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>$NAME_NODE_STORAGE</value>
  </property>
  <property>
    <name>dfs.namenode.checkpoint.dir</name>
    <value>$NAME_NODE2_STORAGE</value>
  </property>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>$DATA_NODE_STORAGE</value>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>$DATA_NODE_REPLICATION</value>
  </property>
</configuration>
EOF


# 8. 設定環境變量 hadoop-env.sh
HADOOP_ENV=$NAME/etc/hadoop/hadoop-env.sh
echo "JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64" >> $HADOOP_ENV
# 可以友善調試 namenode 和 datanode
echo 'export HDFS_NAMENODE_OPTS="-Xmx3g -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999"'  >> $HADOOP_ENV
echo 'export HDFS_DATANODE_OPTS="-Xmx3g -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9998"' >> $HADOOP_ENV


# 9. 同步 datanode 節點
mv $NAME $DATA_NODE_NAME
for node in ${DATA_NODE_HOST//,/ }
do
  scp -r $DATA_NODE_NAME $node:$HOME
done


# 10. 同步 namenode 節點
for node in ${DATA_NODE_HOST//,/ }
do
  echo $node >> $DATA_NODE_NAME/etc/hadoop/workers.tmp
done
mv $DATA_NODE_NAME/etc/hadoop/workers.tmp $DATA_NODE_NAME/etc/hadoop/workers
mv $DATA_NODE_NAME $NAME_NODE_NAME
scp -r $NAME_NODE_NAME $NAME_NODE_HOST:$HOME

           

複制代碼

4.5 啟動 HDFS

在跳轉機上使用 5CC99CC1 使用者操作。

# format 
$HADOOP_HOME/bin/hdfs namenode -format
# 啟動 namenode 
$HADOOP_HOME/bin/hdfs --daemon start namenode


# 啟動 datanode
pdsh -w $ips "/home/5CC99CC1/hadoop-3.3.6/bin/hdfs --daemon start datanode"

           

複制代碼

4.6 模拟上傳測試

使用 root 使用者安裝 fio

yum install -y fio

           

複制代碼

腳本功能:生成指定大小檔案上傳到 HDFS,之後檢視該檔案的 block 配置設定情況。

#!/bin/bash
# 上傳 1m 檔案:sh upload.sh 1m
fio -filename=$1 -direct=1 -ioengine=libaio -bs=4k -size=$1 -numjobs=1 -iodepth=16 -runtime=1 -thread -rw=write -group_reporting -name="write_test"
hdfs dfs -copyFromLocal $1 /tmp
hdfs fsck /tmp/$1  -files -locations -blocks

           

複制代碼

有問題歡迎大家留言,我們一起讨論。

繼續閱讀