HDFS原理与应用

298 阅读12分钟

这是我参与「第四届青训营 」笔记创作活动的的第11天

什么是计算机集群结构?

分布式文件系统把文件分布存储到多个计算机节点上,成千上万的计算机节点构成集群;目前的分布式文件系统所采用的计算机集群都是由普通硬件构成的,大大降低了硬件的开销。

  • 文件系统定义:文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易
  • 文件名:在文件系统中,文件名用于定位存储位置
  • 元数据(MetaData):保存文件属性的数据,如文件名、文件长度、文件所属用户组,文件存储位置等
  • 数据块(Block):存储文件的最小单元,对存储介质划分了固定的区域,使用时按这些区域分配使用

为什么需要块?

hdfs默认一个块128MB,一个文件被分为多个块,以块作为存储单位;块的大小远远大于普通文件系统,可以最小化寻址开销;抽象的块概念可以带来以下明显的好处:

  • 支持大规模文件存储 文件按块为单位进行存储,一个大规模文件被拆成若干个文件块,不同文件块可以分发到不同节点上,因此,一个文件的大小不会收到单个节点存储容量的限制,可以远远大于网络中任意节点的存储容量。
  • 简化系统设计 大大简化了存储管理,因为文件块大小固定,这样很容易计算出一个节点可以存储多少文件块;同时,也方便了元数据的管理,元数据不需要和文件块一起存储,可以由其它系统管理元数据。
  • 适合数据备份 每个文件块都可以冗余存储到多个节点上,大大提高了系统的容错性和可用性。

hdfs的概述

Hadoop分布式文件系统hdfs具有高度的容错能力,认为硬件总是不可靠的,旨在部署于低成本硬件上;提供对应用程序的高吞吐量访问,并且适用于具有大数据集的应用程序;hdfs放宽了一些对POSIX的要求,以实现对文件系统数据的流式访问;支持存储TB-PB级别的数据;最初是作为Apache Nutch Web搜索引擎项目的基础结构而构建的,是Apache Hadoop Core项目的一部分。

不适用场景

  • 低时间延迟数据访问的应用 例如几十毫秒的范围,因为hdfs是为高数据吞吐量应用而优化的,这样会造成以高时间延迟为代价
  • 大量小文件 NameNode启动时,将文件系统的元数据加载到内存,因此文件系统所能存储的文件总数受限于 NameNode内存容量。比如,当每个文件目录和数据块的存储信息大约占150字节,如果有一百万个文件,每个文件占一个数据块,那么至少需要300MB的内存空间,设想如果是面对十亿文件的存储,内存空间的占用是非常大的。
  • 多用户写入,任意修改文件 当前hdfs文件只有一个writer,而且写操作总是在文件的末尾,也不支持在文件任意位置修改。

流式数据访问

hdfs以流式数据访问来存储超大文件,一次写入,多次读取,实现高效的访问。 数据集通常是由数据源生成或者从数据源复制而来,接着长时间在此数据集上进行各类分析,每次分析都涉及 该数据集的大部分甚至是全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更为重要;收集到部分数据就开始处理,不是收集到全部数据再处理,因此是分批读取全部数据集中的一部分,所以数据是按块存储的。

hdfs设计结构

HDFS架构包含三个部分:NameNode、DataNode、Client

  • NameNode:用于存储、生成文件,生成文件系统的元数据,运行一个实例;
  • DataNode:用于存储实际的数据,将自己管理的数据块上报给NameNode,运行多个实例
  • Client:支持业务访问HDFS,从NameNode、DataNode获取数据返回业务,多个实例,和业务一起运行

局限性

  • 命名空间限制,名称节点保存在内存中;名称节点所能容纳的对象(文件、块)的数量会收到文件大小的限制
  • 性能瓶颈,整个文件系统的吞吐量,受限于单个名称节点的吞吐量,
  • 由于集群中,只有一个名称节点,只有一个命名空间,因此无法对不同的应用程序进行隔离
  • 一旦这个唯一的名称节点发生故障,集群不可用 SecondaryNameNode干什么去了? 这是因为2NN是冷备份,即第一个节点发生故障后,必须停止一段时间,才能恢复,提供对外服务;所以在hdfs2.0中,不仅提供了2NN还提供了热备份

HDFS的文件快为128M的好处文件分块越大,快数就会越少,登记它们位置信息的数据越少,节约了NameNode的空间,同时节省了寻址访问时间。

只设置一个NameNode,在简化设计的同时也带来了一系列明显的局限性

  • 不适用于低延迟数据访问,寻址慢
  • 无法高效存储大量小文件
  • 不支持多用户写入及任意位置修改文件

架构原理

  • 分布式存储系统的基本概念,这些概念基本上每个分布式存储系统都会涉及到。

    • 容错能力
    • 一致性模型
    • 可扩展性
    • 节点体系模式
    • 数据放置策略
    • 单机存储引擎
  • HDFS组件 image-20220803113157552.png

    • Client/SDK:读写操作的发起点,HDFS很多读写逻辑都是在SDK中实现的。
    • NameNode:元数据节点,是HDFS的中枢节点,也是服务的入口。
    • DataNode:数据节点,存放实际用户数据。

Client 写流程

image-20220803113231823.png

Client 读流程

image-20220803113745770.png

NameNode(元数据节点)

image-20220803113903242.png

  • 维护目录树:维护目录树的增删改查操作,保证所有修改都能持久化,以便机器掉电不会造成数据丢失或不一致。
  • 维护文件和数据块的关系:文件被切分成多个块,文件以数据块为单位进行多副本存放。
  • 维护文件块存放节点信息:通过接收DataNode的心跳汇报信息,维护集群节点的拓扑结构和每个文件块所有副本所在的DataNode类表。
  • 分配新文件存放节点:Client创建新的文件时候,需要有NameNode来确定分配目标DataNode。

DataNode(数据节点)

image-20220803122059896.png

  • 数据块存取:DataNode需要高效实现对数据块在硬盘上的存取。

  • 心跳汇报:把存放在本机的数据块列表发送给NameNode,以便NameNode能维护数据块的位置信息,同时让NameNode确定该节点处于正常存活状态。

  • 副本复制:

    • 数据写入时 Pipeline IO操作
    • 机器故障时补全副本

关键设计

分布式存储系统的基本概念,这些概念基本上每个分布式存储系统都会涉及到。

  • 容错能力:能够处理绝大部分异常场景,例如服务器宕机、网络异常、磁盘故障、网络超时等。
  • 一致性模型:为了实现容错,数据必须多副本存放,一致性要解决的问题是如何保障这多个副本的内容都是一致的。
  • 可扩展性:分布式存储系统需要具备横向扩张scale-out 的能力。
  • 节点体系模式:常见的有主从模式、对等模式等,不管哪种模式,高可用是必须的功能。
  • 数据放置策略:系统是由多个节点组成,数据是多个副本存放时,需要考虑数据存放的策略。
  • 单机存储引擎:在绝大部分存储系统中,数据都是需要落盘持久化,单机引擎需要解决的是根系统特点,如何高效得存取硬盘数据。

NameNode 目录树维护

完整的 metadata 信息就应该由 FSImage 文件和 edit log 文件组成。fsimage 中存储的信息就相当于整个 hdfs 在某一时刻的一个快照。

image-20220803131943004.png

  • fsimage

    • 文件系统目录树
    • 完整的存放在内存中
    • 定时存放到硬盘上
    • 修改是只会修改内存中的目录树

image-20220803132028553.png

  • EditLog

    • 目录树的修改日志
    • client更新目录树需要持久化EditLog后才能表示更新成功
    • EditLog可存放在本地文件系统,也可存放在专用系统上
    • NameNode HA方案一个关键点就是如何实现EditLog共享

NameNode数据放置

image-20220803132706295.png

  • 数据块信息维护

    • 目录树保存每个文件的块id
    • NameNode维护了每个数据块所在的节点信息
    • NameNode根据DatalNode汇报的信息动态维护位置信息
    • NameNode不会持久化数据块位置信息

DataNode

  • 数据块的硬盘存放

    • 文件在NameNode已分割成block
    • DataNodellblock为单位对数据进行存取

image-20220803141154166.png

image-20220803141447488.png

  • 启动扫盘

    • DataNode需要知道本机存放了哪些数据块
    • 启动时把本机硬盘上的数据块列表加载在内存中

HDFS 写异常处理

Lease Recovery

租约:CIient 要修改一个文件时,需要通过NameNode 上锁,这个锁就是租约(Lease)。

情景:文件写了一半,client自己挂掉了。可能产生的问题:

  • 副本不一致,当出现不一致时会比较副本之间的大小,选取较小的副本返回
  • Lease无法释放,客户端获取租约后会有10分钟的超时时间,客户端需要定期向NameNode续租,如无续租并超时 NameNode会判断客户端已经挂掉然后把客户端踢掉,并释放租约。

image-20220803141559384.png

Pipeline Recovery

情景: 文件写入过程中,DataNode 侧出现异常挂掉了。

image-20220803141914358.png

异常出现的时机:

  • 创建连接时
  • 数据传输时
  • complete阶段

Client 读异常处理

情景:读取文件的过程,DataNode 侧出现异常挂掉了

解决方法:节点 Failover

增强情景:节点半死不过,读取很慢

image-20220803151506770.png

旁路系统

Balancer:均衡 DataNode的容量

image-20220803151818640.png

Mover:确保副本放置符合策略要求

image-20220803151844178.png

控制面建设

控制面建设:保障系统稳定运行

**HouseKeeping组件:**比如Balancer,Mover等, 这些组件不运行不会马上影响读写操作,但是长时间会积累系统性问题,例如读写不均衡导致IO热点等。

可观测注设施:

  • 指标埋点
  • 数据采集
  • 访问日志
  • 数据分析

运维体系建设:

  • 运维操作需要平台化
  • NameNode操作复杂
  • DataNode 机器规模庞大
  • 组件控制面API

如何搭建

分别修改4台hostname

> hostnamectl set-hostname  node(n)
> bash

全部会话ssh配置文件检查

> vim /etc/ssh/sshd_config
:set number 
115 改成UseDNS no
117改成MaxStartups 1000

全部会话重启ssh服务

> systemctl restart sshd.service

全部会话修改服务器对应的内网地址

> vim /etc/hosts
192.168.0.230 node1 
192.168.0.250 node2 
192.168.0.167 node3 
192.168.0.125 node4 

全部会话编辑cloud.cfg配置文件

> vim /etc/cloud/cloud.cfg
按49,shift+g直接跳转49行
注释49、50、51行

全部会话关闭防火墙

查看防火前状态
> systemctl status firewalld
如果是开启的,需要先
> systemctl stop firewalld
> systemctl disable firewalld

全部会话生成id_rsa.pub文件

> ssh-keygen

分别在node2、3、4执行,按要求输入密码

> scp /root/.ssh/id_rsa.pub root@node1:~/.ssh/id2
> scp /root/.ssh/id_rsa.pub root@node1:~/.ssh/id3
> scp /root/.ssh/id_rsa.pub root@node1:~/.ssh/id4

在node1上汇总

> cd /root/.ssh/
> cat id_rsa.pub id2 id3 id4 >> authorized_keys

在node1上分发到其他三台服务器

> scp authorized_keys root@node2:~/.ssh/
> scp authorized_keys root@node3:~/.ssh/
> scp authorized_keys root@node4:~/.ssh/

全部会话创建

> mkdir -p /home/modules/data/buf/
> mkdir -p /home/test_tools/
> mkdir -p /home/nm/localdir
## 在node1下执行
#从华为公有云 OBS 的公共存储中下载 Hadoop 软件包到 node1 的/home目录下,下载完成后需要修改软件包目录名称
> cd /home/
> wget https://xunfang.obs.cn-south-1.myhuaweicloud.com/kunpeng_bigdata_pro_extend_tools.tar.gz
> mv kunpeng_bigdata_pro_extend_tools.tar.gz extend_tools.tar.gz
> tar -zxvf extend_tools.tar.gz
> cd /home/extend_tools
> tar -zxvf hadoop-2.8.3.tar.gz -C /home/modules
> ls /home/modules/ | grep hadoop
hadoop-2.8.3

配置hadoop-env.sh

node1上执行命令

> vim /home/modules/hadoop-2.8.3/etc/hadoop/hadoop-env.sh
#修改JAVA_HOME
export JAVA_HOME=/usr/lib/jvm/java

预配置 core-site.xml

node1上执行命令

> vim /home/modules/hadoop-2.8.3/etc/hadoop/core-site.xml

<configuration></configuration>之间填写

	<property>
		<name>fs.obs.readahead.inputstream.enabled</name>
		<value>true</value>
	</property>
	<property>
		<name>fs.obs.buffer.max.range</name>
		<value>6291456</value>
	</property>
	<property>
		<name>fs.obs.buffer.part.size</name>
		<value>2097152</value>
	</property>
	<property>
		<name>fs.obs.threads.read.core</name>
		<value>500</value>
	</property>
	<property>
		<name>fs.obs.threads.read.max</name>
		<value>1000</value>
	</property>
	<property>
		<name>fs.obs.write.buffer.size</name>
		<value>8192</value>
	</property>
	<property>
		<name>fs.obs.read.buffer.size</name>
		<value>8192</value>
	</property>
	<property>
		<name>fs.obs.connection.maximum</name>
		<value>1000</value>
	</property>
	<property>
		<name>fs.defaultFS</name>
		<value>hdfs://node1:8020</value>
	</property>
	<property>
		<name>hadoop.tmp.dir</name>
		<value>/home/modules/hadoop-2.8.3/tmp</value>
	</property>
	<property>
		<name>fs.obs.buffer.dir</name>
		<value>/home/modules/data/buf</value>
	</property>
	<property>
		<name>fs.obs.impl</name>
		<value>org.apache.hadoop.fs.obs.OBSFileSystem</value>
	</property>
	<property>
		<name>fs.obs.connection.ssl.enabled</name>
		<value>false</value>
	</property>
	<property>
		<name>fs.obs.fast.upload</name>
		<value>true</value>
	</property>
	<property>
		<name>fs.obs.socket.send.buffer</name>
		<value>65536</value>
	</property>
	<property>
		<name>fs.obs.socket.recv.buffer</name>
		<value>65536</value>
	</property>
	<property>
		<name>fs.obs.max.total.tasks</name>
		<value>20</value>
	</property>
	<property>
		<name>fs.obs.threads.max</name>
		<value>20</value>
	</property>

配置OBS存储

下载credentials.csv,获取AK/SK,例如

User NameAccess Key IdSecret Access Key
stu13Q9SJLOXAQIQNVDLFC7CSc8URca7UYpDwRQR0pAf6WhvxWlDjPnl9RRcWtRoR

重新修改core-site.xml

node1执行

> vim /home/modules/hadoop-2.8.3/etc/hadoop/core-site.xml

按照实际情况填写如下内容

	<property>
		<name>fs.obs.access.key</name>
		<value>Q9SJLOXAQIQNVDLFC7CS</value>
	</property>
	<property>
		<name>fs.obs.secret.key</name>
		<value>c8URca7UYpDwRQR0pAf6WhvxWlDjPnl9RRcWtRoR</value>
	</property>
	<property>
		<name>fs.obs.endpoint</name>
		<value>obs.cn-north-4.myhuaweicloud.com</value>
	</property>

配置hdfs-site.xml

node1上执行

> vim /home/modules/hadoop-2.8.3/etc/hadoop/hdfs-site.xml
	<property>
		<name>dfs.replication</name>
		<value>3</value>
	</property>
	<property>
		<name>dfs.namenode.secondary.http-address</name>
		<value>node1:50090</value>
	</property>
	<property>
		<name>dfs.namenode.secondary.https-address</name>
		<value>node1:50091</value>
	</property>

配置 yarn-site.xml

node1上执行

> vim /home/modules/hadoop-2.8.3/etc/hadoop/yarn-site.xml
	<property>
		<name>yarn.resourcemanager.hostname</name>
		<value>node1</value>
		<description>表示 ResourceManager 安装的主机</description>
	</property>
	<property>
		<name>yarn.resourcemanager.address</name>
		<value>node1:8032</value>
		<description>表示 ResourceManager 监听的端口</description>
	</property>
	<property>
		<name>yarn.nodemanager.aux-services</name>
		<value>mapreduce_shuffle</value>
		<description>为 map reduce 应用打开 shuffle 服务</description>
	</property>
	<property>
		<name>yarn.nodemanager.local-dirs</name>
		<value>/home/nm/localdir</value>
		<description>表示 nodeManager 中间数据存放的地方</description>
	</property>
	<property>
		<name>yarn.nodemanager.resource.memory-mb</name>
		<value>3072</value>
		<description>表示这个 NodeManager 管理的内存大小</description>
	</property>
	<property>
		<name>yarn.nodemanager.resource.cpu-vcores</name>
		<value>2</value>
		<description>表示这个 NodeManager 管理的 cpu 个数</description>
	</property>

配置 mapred-site.xml

node1上执行命令

> cp /home/modules/hadoop-2.8.3/etc/hadoop/mapred-site.xml.template /home/modules/hadoop-2.8.3/etc/hadoop/mapred-site.xml
> vim /home/modules/hadoop-2.8.3/etc/hadoop/mapred-site.xml

<configuration></configuration>之间填写

	<property>
		<name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>
	<property>
		<name>mapreduce.jobhistory.address</name>
		<value>node1:10020</value>
	</property>
	<property>
		<name>mapreduce.jobhistory.webapp.address</name>
		<value>node1:19888</value>
	</property>
	<property>
		<name>mapred.task.timeout</name>
		<value>1800000</value>
	</property>

配置slaves

node1上执行

> vim /home/modules/hadoop-2.8.3/etc/hadoop/slaves
删除localhost,添加以下内容
node2
node3
node4

拷贝插件 jar 包到指定目录

node1上执行

> cp /home/extend_tools/hadoop-huaweicloud-2.8.3.33.jar /home/modules/hadoop-2.8.3/share/hadoop/common/lib/
> cp /home/extend_tools/hadoop-huaweicloud-2.8.3.33.jar /home/modules/hadoop-2.8.3/share/hadoop/tools/lib
> cp /home/extend_tools/hadoop-huaweicloud-2.8.3.33.jar /home/modules/hadoop-2.8.3/share/hadoop/httpfs/tomcat/webapps/webhdfs/WEB-INF/lib/
> cp /home/extend_tools/hadoop-huaweicloud-2.8.3.33.jar /home/modules/hadoop-2.8.3/share/hadoop/hdfs/lib/

分发组件

node1上执行

> for i in {2..4};do scp -r /home/modules/hadoop-2.8.3 root@node${i}:/home/modules/;done
#在`node2、3、4`上检查
> ls /home/modules/ | grep hadoop
hadoop-2.8.3

全部会话执行添加环境变量

> vim /etc/profile
添加
export HADOOP_HOME=/home/modules/hadoop-2.8.3
export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
export HADOOP_CLASSPATH=/home/modules/hadoop-2.8.3/share/hadoop/tools/lib/*:$HADOOP_CLASSPATH
export JAVA_HOME=/usr/lib/jvm/java
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

全部会话生效

> source /etc/profile

检查是否生效,全部会话执行

> echo $HADOOP_HOME

初始化namenode

node1上执行

> hdfs namenode -format

启动Hadoop集群

> start-dfs.sh ; start-yarn.sh