探讨hdfs的系统架构以及以及整体常用的命令和系统分析,本文主要探讨高可用版本的hdfs集群,并基于日常工作中的沉淀进行思考和整理。更多关于分布式系统的架构思考请参考文档关于常见分布式组件高可用设计原理的理解和思考
NameNode 的高可用发展史在 Hadoop2.0 以前,每个 HDFS 集群只有一个 NameNode,一旦这个节点不可用,则整个HDFS集群将处于不可用状态(即HDFS2.0以前,NameNode存在单点故障风险)。 在HDFS集群中同时运行两个NameNode,通过HA的方式进行集群切换,从而达到高可用的目的。
NN节点有如下2种状态:
当 Active NameNode 停止服务时,Standby NameNode 能够快速进行故障切换,以保证 HDFS 集群服务不受影响。
hdfs的系统架构如下
相关核心的组件和角色作用如下
组件 | 部署模式 | 组件作用 | 备注 |
---|---|---|---|
NN(active) | 单机部署 | 存储集群的元数据,具体集群数据的全局视角 | 给客户端提供请求服务等,和standby节点进行形成主备 |
NN(standby) | 单机部署 | 存储集群的元数据,具体集群数据的全局视角 | 配合active完成checkpoints,合并editlog和fimage,和active节点进行形成主备 |
zk | 多节点部署 | zk提供hdfs的NN选主锁和消息通知,zkfc接受相关zk进行主从切换 | 通过Zab 协议来保证分布式事务的最终一致性 |
zkfc | 和NN部署 | 和zk交互,通过zk的消息通知,并调用NN的rpc接口实现集群选主和切主 | fencing NN,防止出现多active形成脑裂 |
jn | 多节点部署 | active和standby节点的editlog数据桥梁,通过rpc完成数据传输 | 通过paxos协议选主,建议单数节点部署(3/5/7等) |
DN | 单机部署 | 数据的存储节点,会定期上报心跳和当前节点的block信息给NN | 保存当前节点的块信息,并监控块状态 |
hdfs-site.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed ...
-->
<configuration>
<!-- 定义hdfs的namespace,可以配置多个,使用","分隔 -->
<property>
<name>dfs.nameservices</name>
<value>cluster</value>
</property>
<!-- 定义hdfs的NN节点 -->
<property>
<name>dfs.ha.namenodes.cluster</name>
<value>namenode1,namenode2</value>
</property>
<!-- 定义hdfs的副本数量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 定义是否开启web监控页面 -->
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
<!-- 定义hdfs的NN的editlog保存路径 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/data2/hadoop/dfs/name</value>
</property>
<!-- 定义hdfs的NN1,NN2节点信息 -->
<property>
<name>dfs.namenode.rpc-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:9000</value>
</property>
<property>
<name>dfs.namenode.rpc-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:9000</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.cluster.namenode1</name>
<value>namenode1:53310</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.cluster.namenode2</name>
<value>namenode2:53310</value>
</property>
<property>
<name>dfs.namenode.http-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:50070</value>
</property>
<property>
<name>dfs.namenode.https-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:9871</value>
</property>
<property>
<name>dfs.namenode.https-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:9871</value>
</property>
<!-- 定义hdfs的jn节点信息 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://xx.xx.xx.xx:8485;xx.xx.xx.xx:8485;xx.xx.xx.xx:8485/cluster</value>
</property>
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/data2/hadoop/journal</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.cluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 定义hdfs的NN的fencing信息,NN通过切主时会获取原主的信息,如果调用rpc原主降为standby失败,就会通过fencing ssh到对应的服务器杀死原主的NN进程 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence(root:36000)
shell(/bin/true)
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.coNNect-timeout</name>
<value>30000</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<property>
<name>ha.failover-controller.cli-check.rpc-timeout.ms</name>
<value>60000</value>
</property>
<property>
<name>ipc.client.coNNect.timeout</name>
<value>60000</value>
</property>
<!-- 定义hdfs的DN数据保存目录,可以配置多个路径,用","分割 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>/hdfsdata1/hadoop/dfs/data</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>xx.xx.xx.xx:9868</value>
</property>
<property>
<name>dfs.namenode.secondary.https-address</name>
<value>xx.xx.xx.xx:9869</value>
</property>
<property>
<name>dfs.namenode.datanode.registration.ip-hostname-check</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.rpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<!-- 定义hdfs的DN内部的平衡、同步fimage的带宽限制 -->
<property>
<name>dfs.image.transfer.bandwidthPerSec</name>
<value>52428800</value>
</property>
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>52428800</value>
</property>
<property>
<name>dfs.datanode.balance.max.concurrent.moves</name>
<value>50</value>
</property>
<!-- 定义hdfs的需要下线的机器列表 -->
<property>
<name>dfs.hosts.exclude</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/excludes_datanodes</value>
</property>
<property>
<name>dfs.namenode.http-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.https-bind-host</name>
<value>0.0.0.0</value>
</property>
<!-- 定义hdfs的zk路径,通过在该路径下创建临时节点选主 -->
<property>
<name>ha.zookeeper.parent-znode</name>
<value>/hadoop-hdfs-ha</value>
</property>
<property>
<name>dfs.datanode.https.address</name>
<value>0.0.0.0:9865</value>
</property>
<property>
<name>dfs.block.access.token.enable</name>
<value>true</value>
</property>
<!-- 定义hdfs的kerberos鉴权,如果设置鉴权可以不配置 -->
<property>
<name>dfs.namenode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<!-- 定义hdfs的kerberos用户,_HOST代表本机的hostname,因此没台DN的用户hdfs/[email protected]有差别,需要针对每个DN单独配置证书 -->
<property>
<name>dfs.namenode.kerberos.principal</name>
<value>hdfs/[email protected]</value>
</property>
<property>
<name>dfs.namenode.kerberos.https.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>dfs.namenode.kerberos.internal.spnego.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>dfs.datanode.data.dir.perm</name>
<value>755</value>
</property>
<property>
<name>dfs.datanode.address</name>
<value>0.0.0.0:9866</value>
</property>
<property>
<name>dfs.datanode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.datanode.kerberos.principal</name>
<value>hdfs/[email protected]</value>
</property>
<property>
<name>dfs.datanode.kerberos.https.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>dfs.journalnode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.journalnode.kerberos.principal</name>
<value>hdfs/[email protected]</value>
</property>
<property>
<name>dfs.journalnode.kerberos.internal.spnego.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>dfs.web.authentication.kerberos.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>dfs.web.authentication.kerberos.keytab</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.data.transfer.protection</name>
<value>integrity</value>
</property>
<!-- 定义hdfs的DN的最大文件数量 -->
<property>
<name>dfs.namenode.fs-limits.max-directory-items</name>
<value>3200000</value>
</property>
</configuration>
core-site.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed ...
-->
<configuration>
<!-- 定义hdfs的zk配置 -->
<property>
<name>ha.zookeeper.auth</name>
<value>digest:zk_user:zk_passwd</value>
</property>
<property>
<name>ha.zookeeper.acl</name>
<value>digest:zk_user:Yg6OG5Tas/LEH5bd73noFMYG3xo=:rwcda</value>
</property>
<property>
<name>hadoop.http.filter.initializers</name>
<value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>
</property>
<property>
<name>hadoop.http.authentication.type</name>
<value>kerberos</value>
</property>
<property>
<name>hadoop.http.authentication.signature.secret.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/secret</value>
</property>
<property>
<name>hadoop.http.authentication.simple.anonymous.allowed</name>
<value>false</value>
</property>
<!-- 定义hdfs的kerberos认证信息 -->
<property>
<name>hadoop.security.authentication</name>
<value>kerberos</value>
</property>
<property>
<name>hadoop.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hadoop.http.authentication.kerberos.principal</name>
<value>HTTP/[email protected]</value>
</property>
<property>
<name>hadoop.http.authentication.kerberos.keytab</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/data2/hadoop/tmp</value>
</property>
<!-- 定义hdfs的namespace集群信息,和hdfs-site.xml定义一致 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://cluster</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>131072</value>
</property>
<property>
<name>hadoop.proxyuser.hadoop.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hadoop.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.groups</name>
<value>*</value>
</property>
<!-- 定义hdfs的zk访问地址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>xx.xx.xx.xx:2181,xx.xx.xx.xx:2181,xx.xx.xx.xx:2181</value>
</property>
<property>
<name>ha.zookeeper.session-timeout.ms</name>
<value>30000</value>
</property>
<!-- 定义hdfs的机架topo配置 -->
<property>
<name>topology.script.file.name</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs_rack_info.py</value>
</property>
<!-- 定义hdfs的DN 汇报心跳配置 -->
<property>
<name>ipc.client.coNNect.max.retries</name>
<value>10</value>
</property>
<property>
<name>ipc.client.coNNect.retry.interval</name>
<value>5000</value>
</property>
<property>
<name>ipc.client.coNNect.max.retries.on.timeouts</name>
<value>3</value>
</property>
</configuration>
对于NN来说,最大的问题还是响应客户端的rpc请求,由于只能单点响应客户端请求,因此单个NN的需要进行系统优化响应服务请求。
1, dfs.namenode.handler.count
参数:namenode的服务器线程数。
NameNode有一个工作线程池用来处理客户端的远程过程调用及集群守护进程的调用。处理程序数量越多意味着要更大的池来处理来自不同DataNode的并发心跳以及客户端并发的元数据操作。对于大集群或者有大量客户端的集群来说,通常需要增大参数dfs.namenode.handler.count的默认值10。设置该值的一般原则是将其设置为集群大小的自然对数乘以20,即20logN,N为集群大小。
<property>
<name>dfs.namenode.handler.count</name>
<value>200</value>
</property>
1, dfs.datanode.balance.bandwidthPerSec
参数: datanode 平衡带宽
描述:指定每个datanode可以利用每秒字节数来平衡目标的最大带宽。
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>52428800</value>
</property>
2,dfs.datanode.max.transfer.threads
参数:datanode 最大传输线程数
描述:指定用于传输数据进出DN的最大线程数。集群中如果不一致,会造成数据分布不均。
<property>
<name>dfs.datanode.max.transfer.threads</name>
<value>100</value>
</property>
hdfs dfs
下面为显示的内容:
Usage: hadoop fs [generic options]
[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-checksum <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>]
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] [-h] <path> ...]
[-cp [-f] [-p | -p[topax]] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] <src> <localdst>]
[-help [cmd ...]]
[-ls [-d] [-h] [-R] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
整理日常操作hdfs常用的命令,便于针对hdfs的文件操作
1, –ls:查看指定目录下内容
eg:hadoop fs –ls /user/wangwu
2, –cat:显示文件内容
eg:hadoop fs -cat /user/wangwu/data.txt
3, –put:将本地文件存储至hadoop
eg:hadoop fs –put /home/t/file.txt /user/t
4, –put:将本地文件夹存储至hadoop
eg:hadoop fs –put /home/t/dir_name /user/t
5, -get:将hadoop上某个文件down至本地已有目录下
eg:hadoop fs –get /user/t/ok.txt /home/t
6, –rm:删除hadoop上指定文件或文件夹
eg:hadoop fs –rm /user/t/ok.txt
7, 删除hadoop上指定文件夹(包含子目录等)
eg:hadoop fs –rm /user/t
8, –mkdir:在hadoop指定目录内创建新目录
eg:hadoop fs –mkdir /user/t
9, -touchz:在hadoop指定目录下新建一个空文件
eg:hadoop fs -touchz /user/new.txt
10, –mv:将hadoop上某个文件重命名
eg:hadoop fs –mv /user/test.txt /user/ok.txt
11, -setrep:设置HDFS中文件的副本数量
eg:hadoop fs -setrep 10 /tmp/tt/student.txt
12, 将正在运行的hadoop作业kill掉
eg:hadoop job –kill [job-id]
13, -help:输出这个命令参数
eg:hadoop fs -help rm
14, -moveFromLocal:从本地剪切粘贴到HDFS
eg:hadoop fs -moveFromLocal ./stuDNet.txt /tmp/test/
15, -appendToFile:追加一个文件到已经存在的文件末尾
eg:hadoop fs -appendToFile liubei.txt /sanguo/shuguo/zhangsan.txt
16, -chgrp , -chmod, -chown:Linux文件系统中的用法一样,修改文件所属权限
eg:hadoop fs -chmod 666 /sanguo/shuguo/zhangsan.txt
17, -copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
eg:hadoop fs -copyFromLocal README.txt /
18, -copyToLocal:从HDFS拷贝到本地
eg:hadoop fs -copyToLocal /sanguo/shuguo/zhangsan.txt ./
19, -cp :从HDFS的一个路径拷贝到HDFS的另一个路径
eg:hadoop fs -cp /sanguo/shuguo/zhangsan.txt /zhuge.txt
20, -tail:显示一个文件的末尾
eg:hadoop fs -tail /sanguo/shuguo/zhangsan.txt
21, -rmdir:删除空目录
eg:hadoop fs -rmdir /test
22, -du:统计文件夹的大小信息
eg:hadoop fs -du -h /user/itcast/test
1.3 K /user/itcast/test/README.txt
15 /user/itcast/test/jinlian.txt
1.4 K /user/itcast/test/nihao.txt
用于日常运维命令,便于进行服务运维,提升系统稳定性。
1, 设置文件副本数量
如果hdfs的磁盘使用率过高,可以临时调整大文件副本,注意如果调整的副本数量过小,可能会面临数据可靠性风险,慎重!
hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt
2, 检查hdfs的是否有坏块或者副本缺失
hdfs fsck / -list-corruptfileblocks
3, 检查hdfs的文件是否有坏块或者副本缺失
hdfs fsck / -files -blocks -locations
hdfs fsck /user/root/hello/yarn-demo-1.0-SNAPSHOT-jar-with-dependencies.jar -files -blocks -locations
也可以通过该命令查看对应的文件的block所在的位置
3, 执行hdfs执行格式化
执行格式化操作,会清理当前集群的所有数据,重新创建一个新的集群,一般只在新部署hdfs集群时使用,一定要慎重!!!
# 格式化zk,会在zk上创建临时目录,用于选主
hdfs zkfc -formatZK
# 格式化整个hdfs系统
hdfs namenode -format
4, 同步active的元数据
完成active部署后,standby需要跟active同步元数据,才能达成一致,该操作需要再standby节点执行,否则可能会造成元数据丢失.
hdfs namenode -bootstrapStandby
5, hdfs安全模式
当hdfs集群的数据副本丢失过多,或者集群存活的DN数量到达阈值时,就会主动进入安全模式,集群只读,需要人工修复。
# 检查是否进入安全模式
hdfs dfsadmin -safemode get
# 如何离开安全模式
hdfs dfsadmin -safemode leave
6,强行切换NN
# 强行指定某个节点为active
hdfs haadmin -transitionToActive -forcemanual namenode1
# 强行指定某个节点为standby
hdfs haadmin -transitionToStandby -forcemanual namenode2
7, 获取nn的集群节点状态
# 获取所有的nn节点状态
hdfs haadmin -getAllServiceState
#获取单个nn的节点状态
hdfs haadmin -getServiceState nn1
hdfs haadmin -getServiceState nn2
5, 刷新节点信息
通常使用于下线节点
# 配置完exclude后,通过手动刷新配置生效
hdfs dfsadmin -refreshNodes
hdfs的数据写流程,整体流程如下。
客户端写入的总体流程如下
这里面有几个关键概念,需要进行整理
客户端在给DN发送数据时,
读详细步骤:
Hadoop 默认的副本数为3,并且在机架的存放上也有一定的策略。优先按照如下策略选择合适的DN节点:
(1)第 1 个副本存放在 HDFS 客户端所在的节点上。
(2)第 2 个副本存放在与第1个副本不同的机架上,并且是随机选择的节点。
(3)第 3 个副本存放在与第2个副本相同的机架上,并且是不同的节点。
1,如果DN收到一半 DN挂了了的处理流程
client发送数据以block作为基础单元。
2,读取文件时DN挂了
DataNode 挂了只需要失败转移到其他副本所在的 DataNode 继续读取
3.读取到的文件数据损坏
hdfs擅长处理大数据规模场景下的数据处理场景,常规的接口响应是秒级(几秒到十几秒),数据处理、清晰等场景下,相对于数据的处理和聚合运算,这些时间相对客户忽略不计。有些场景不适用于hdfs集群,总结如下