天天看点

深入理解 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

           

复制代码

有问题欢迎大家留言,我们一起讨论。

继续阅读