1 HDFS的运行机制
HDFS集群中的节点分为两种角色,一种角色负责管理整个集群的元数据,是名称节点(name node);另一种角色负责存储文件数据块和管理文件数据块,是数据节点(datanode)。
1.1 NameNode
1.1.1名称节点负责响应客户端的请求,负责管理整个文件系统的元数据。
1.1.2HDFS的内部工作机制对客户端是透明的,客户端对HDFS的读、写操作都必须先向name node申请。所以元数据是关键。
1.1.3负责维持文件的副本数量
1.1.4 为了保证集群的元数据不丢失,生产环境下需要配置辅助名称节点(secondary name node,简称2NN)作为元数据的备份,万一name node的元数据丢失就可以从2NN恢复元数据。
1.1.5 名称节点可用的前提是此节点运行名为“NameNode”的进程。
1.2 数据节点Data Node
1.2.1 负责存放被切割后的文件块。文件在data node的存储单位是块(block)。
1.2.2 如果hadoop集群是完全分布式模式,那么一个文件的每一个块必须有多个副本,存放在不同的datanode上。当客户端把某个文件写入HDFS时,首先按照固定的块大小(block size)把该文件切成若干个块,再分布式地存储在若干台data node上。
1.2.3 data node通过心跳信息定期地向name node汇报自身保存的文件块信息。
1.2.4 数据节点可用的前提是此节点运行名为“DataNode”的进程。
下面介绍一下客户端向HDFS写数据和读数据的流程,hadoop集群规模以“1台namenode + 3台data node”为例。
1.3 客户端向HDFS写数据的流程
1.3.1 客户端和name node通信,name node检查目标文件是否已存在,父目录是否存在,检查通过以后name node通知客户端可以写入
1.3.2 客户端向name node请求上传文件的第一个块(block1),询问name node应该把block1上传到哪些data node主机上。客户端每传一个block都要向name node请求
1.3.3 name node把3台data node服务器信息返回给客户端。基于可靠性的考虑,每个文件块都有副本,每个副本分别存放在不同的data node服务器上。副本存放的data node选择策略:首选在本地机架的一个节点上存放副本, 第二个副本在本地机架的另一个不同节点,第三个副本在不同机架的不同节点上
1.3.4 正式上传之前,客户端请求与3台中的其中一台(dn1)建立传输通道,dn1又和dn2主机建立传输通道,dn2又和dn3建立传输通道
1.3.5整个传输通道建立完成后,客户端把block1从磁盘读出来放到本地缓存, 开始向dn1上传block1,上传时以package为单位进行传输,package默认大小64kb。dn1每收到一个package就会复制一份传给dn2,dn2再复制一份传给dn3。
1.3.6 当block1传输完成后,客户端再次向name node申请上传此文件的第二个块block2
1.4 客户端从HDFS读数据的流程
1.4.1 客户端和name node通信,查询元数据,找到文件块存放的data node服务器信息
1.4.2 客户端收到name node返回的信息(例如block1存放在dn1和dn2上,block2在dn2和dn3上),去找到相应的dn(按就近挑选dn原则,然后随机),再请求和dn建立socket流
1.4.3 socket流建立好以后,dn开始向客户端发送数据(从磁盘读取数据放入socket流)
1.4.4 客户端以packet为单位接收文件块,把各个块在本地合并成一个完整的文件
1.5 名称节点是如何管理元数据的?
Name node对数据的管理采用了三种存储形式:memory meta data, fsimage, edit log.
1.5.1 memorymeta data:内存元数据。在name node服务器启动时会把fsimage从磁盘加载到内存,有了元数据就可以向客户端提供读、写服务了
1.5.2 fsimage是在磁盘中的元数据镜像文件,整个文件系统的元数据(包括所有目录和文件inode序列化信息)都永久保存在此文件里;它位于name node的工作目录中
1.5.3 edit logs是数据操作日志文件(可通过日志运算出元数据),客户端对HDFS中的文件进行写操作(增加或删除)之前,先把此操作行为记录到edit log文件中,再把成功操作代码发送给客户端。edit log需要定期通过checkpoint机制融合到fsimage,以保证name node的元数据是最新的,详见下面的描述。
1.6 若名称节点发生磁盘故障,如何挽救集群以及数据?
这个需要提前单独准备一台主机,专门用于部署辅助名称节点(2NN)。同时,name node(NN)和2NN的工作目录存储结构完全相同,这样一来,一旦NN服务器挂掉,可以从2NN的工作目录中将fsimage拷贝到NN的工作目录,以恢复元数据。注意:这种补救措施是事后的,不能保证内存中的元数据是最新的,因为2NN不能充当name node使用(只有name node才能及时更新元数据)。要想让HDFS集群持续对外提供服务,需要引入高可用(HA)配置,HDFS高可用的原理可继续阅读下文。
实际上2NN是用来备份NN中的元数据,备份的内容包括NN服务器上最新的fsimage文件以及全部的日志文件(edit log),备份的过程叫做checkpoint(检查点),过程概要:每隔一段时间,会由2NN将NN上积累的所有edits和一个最新的fsimage下载到2NN本地,并加载到内存进行merge(融合),再把融合后的文件上传给NN。具体checkpoint流程如下:
1.6.1 在做检查点之前,先让NN上正在写的日志文件滚动一下,以供2NN拷贝。日志文件和fsimage的路径可从配置文件hdfs-site.xml的dfs.namenode.name.dir属性中找到,例如我的元数据存放的路径是/home/centos/namemeta
<property>
<name>dfs.namenode.name.dir</name>
<value>/home/centos/namemeta</value>
</property>
下面有个子目录current存放所有的fsimage和edit log文件,查看此目录的树结构:
current/
|-- edits_0000000000000010612-0000000000000010613
|-- edits_0000000000000010614-0000000000000010615
|-- edits_0000000000000010616-0000000000000010617
|-- edits_inprogress_0000000000000010618
|-- fsimage_0000000000000010472
|-- fsimage_0000000000000010472.md5
|-- fsimage_0000000000000010563
|-- fsimage_0000000000000010563.md5
`-- seen_txid
1.6.2 在HDFS集群首次做检查点时才会从NN下载最新的fsimage文件,以后做检查点时只需下载NN上的edit log(因为editlog比fsimage要小)。2NN有了fsimage和editlog,就可以在2NN服务器的内存中对二者进行融合,生成一个检查点文件fsimage.checkpoint,再把此文件上传到NN并重命名为fsimage文件
1.6.3 NN根据刚才2NN传来的最新fsimage更新内存中的元数据
1.6.4 默认是间隔30分钟做一次checkpoint
2 如何实现HDFS高可用
HDFS的高可用指的是HDFS持续对各类客户端提供读、写服务的能力,因为客户端对HDFS的读、写操作之前都要访问name node服务器,客户端只有从name node获取元数据之后才能继续进行读、写。所以HDFS的高可用的关键在于name node上的元数据持续可用。
前面说过2NN的功能是checkpoint,把NN的fsimage和edit log做定期融合,融合后传给NN, 以确保备份到的元数据是最新的,这一点类似于做了一个元数据的快照。Hadoop官方提供了一种quorum journal manager来实现高可用,那么就没必要配置2NN了。
在高可用配置下,edit log不再存放在名称节点,而是存放在一个共享存储的地方,这个共享存储由若干个Journal Node组成,一般是3个节点(JN小集群), 每个JN专门用于存放来自NN的编辑日志,编辑日志由活跃状态的名称节点写入。
要有2个名称节点,二者之中只能有一个处于活跃状态(active),另一个是待命状态(standby),只有active节点才能对外提供读写HDFS服务,也只有active态的NN才能向JN写入编辑日志;standby的名称节点只负责从JN小集群中的JN节点拷贝数据到本地存放。另外,各个DATA NODE也要同时向两个名称节点报告状态(心跳信息、块信息)。
那么一主一从的2个名称节点同时和3个JN构成的组保持通信,活跃的名称节点负责往JN集群写入编辑日志,待命的名称节点负责观察JN组中的编辑日志,并且把日志拉取到待命节点。再加上俩节点各自的fsimage镜像文件,这样一来就能确保两个NN的元数据保持同步。一旦active不可用,提前配置的zookeeper会把standby节点自动变为active,继续对外提供服务。详见下文的 2.2如何通过自动容灾而不是手动容灾?
2.1手动实现高可用的大概流程:
2.1.1 准备3台服务器分别用于运行JournalNode进程(也可以运行在date node服务器上),准备2台namenode服务器用于运行NameNode进程,数据节点数量不限
2.1.2 分别启动3台JN服务器上的JournalNode进程,分别在date node服务器启动DataNode进程
2.1.3 需要同步2台name node之间的元数据。具体做法:从第一台NN拷贝元数据到放到另一台NN,然后启动第一台的NameNode进程,再到另一台名称节点上做standby引导
2.1.4 把第一台名节点的edit日志初始化到JN节点,以供standby节点到JN节点拉取数据
2.1.5启动standby状态的名称节点,这样就能同步fsimage文件
2.1.6 模拟故障,手动把active状态的NN故障,转移到另一台NN
2.2如何通过自动容灾而不是手动容灾?
通过zookeeper实现。
自动容灾引入两个组件:zk quarum + zk容灾控制器。
运行NN的2台主机还要运行ZKFC进程(DFSZKFailoverController),主要负责: 健康监控、session管理、leader选举。
大概流程是:
2.2.7 在上面HA配置的基础上,要另外准备3台主机安装zookeeper,3台主机形成一个小的zk集群.
2.2.8 启动ZK集群每个节点上的QuorumPeerMain进程
2.2.9 登录其中一台NN, 在ZK中初始化HA状态
2.2.10 模拟故障:停掉活跃的NameNode进程,会发现自动切换active到另一台NN,模拟过程如下——
[centos@h201 current]$ xcall jps # 查看HDFS集群的每个节点上运行哪些进程
----------执行jps 在主机h201
44992 DFSZKFailoverController
46624 Jps
44110 QuorumPeerMain
44686 NameNode
命令已成功执行!----------
----------执行jps 在主机h202
17521 Jps
17210 DataNode
17021 QuorumPeerMain
17310 JournalNode
命令已成功执行!----------
----------执行jps 在主机h203
15777 JournalNode
15988 Jps
15494 QuorumPeerMain
15677 DataNode
命令已成功执行!----------
----------执行jps 在主机h204
16516 DataNode
16616 JournalNode
16826 Jps
命令已成功执行!----------
----------执行jps 在主机h205
23170 DFSZKFailoverController
23844 Jps
23064 NameNode
命令已成功执行!----------
[centos@h201 hadoop]$ hdfs haadmin-getServiceState nn1 #h201是第一台NN
active
[centos@h201 hadoop]$ hdfs haadmin-getServiceState nn2 # h205是另一台NN
standby
[centos@h201 hadoop]$jps
44992 DFSZKFailoverController
44110 QuorumPeerMain
44686 NameNode
47326 Jps
[centos@h201 hadoop]$kill -9 44686 # 模拟nn1故障, DFSZKFailoverController自动把nn2激活
[centos@h201 hadoop]$ hdfs haadmin-getServiceState nn2
active
---------------------------------<全文完>------------------------------