本文已参与 新人创作礼 活动,一起开启掘金创作之路
HDFS是Hadoop的核心子项目,下面分享下hadoop-hdfs的具体搭建过程。
HDFS(Hadoop Distributed File System),作为Google File System(GFS)的实现,是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于廉价的商用服务器上。它所具有的高容错、高可靠性、高可扩展性、高获得性、高吞吐率等特征为海量数据提供了不怕故障的存储,为超大数据集的应用处理带来了很多便利。
关于分布式文件系统
在现代的企业环境中,单机容量往往无法存储大量数据,需要跨机器存储。统一管理分布在集群上的文件系统称为分布式文件系统。
传统的网络文件系统(NFS)虽然也称为分布式文件系统,但是其存在一些限制。由于NFS中,文件是存储在单机上,因此无法提供可靠性保证,当很多客户端同时访问NFS Server时,很容易造成服务器压力,造成性能瓶颈。另外如果要对NFS中的文件进行操作,需要首先同步到本地,这些修改在同步到服务端之前,其他客户端是不可见的。某种程度上,NFS不是一种典型的分布式系统,虽然它的文件的确放在远端(单一)的服务器上面。
HDFS的设计目标
存储非常大的文件: 这里非常大指的是几百M、G、或者TB级别。实际应用中已有很多集群存储的数据达到PB级别。根据Hadoop官网,Yahoo!的Hadoop集群约有10万颗CPU,运行在4万个机器节点上。更多世界上的Hadoop集群使用情况,参考Hadoop官网.
采用流式的数据访问方式: HDFS基于这样的一个假设:最有效的数据处理模式是一次写入、多次读取数据集经常从数据源生成或者拷贝一次,然后在其上做很多分析工作,分析工作经常读取其中的大部分数据,即使不是全部。因此读取整个数据集所需时间比读取第一条记录的延时更重要。
运行于商业硬件上: Hadoop不需要特别贵的、reliable的(可靠的)机器,可运行于普通商用机器(可以从多家供应商采购) ,商用机器不代表低端机器。在集群中(尤其是大的集群),节点失败率是比较高的HDFS的目标是确保集群在节点失败的时候不会让用户感觉到明显的中断。
HDFS不适合的应用类型
1) 低延时的数据访问
对延时要求在毫秒级别的应用,不适合采用HDFS。HDFS是为高吞吐数据传输设计的,因此可能牺牲延时HBase更适合低延时的数据访问。
2)大量小文件
文件的元数据(如目录结构,文件block的节点列表,block-node mapping)保存在NameNode的内存中, 整个文件系统的文件数量会受限于NameNode的内存大小。
经验而言,一个文件/目录/文件块一般占有150字节的元数据内存空间。如果有100万个文件,每个文件占用1个文件块,则需要大约300M的内存。因此十亿级别的文件数量在现有商用机器上难以支持。
3)多方读写,需要任意的文件修改
HDFS采用追加(append-only)的方式写入数据。不支持文件任意offset的修改。不支持多个写入器(writer)。
HDFS架构
HDFS客户端:
1、提供一些命令来管理、访问 HDFS,比如启动或者关闭HDFS。
2、与 DataNode 交互,读取或者写入数据;读取时,要与 NameNode 交互,获取文件的位置信息;写入 HDFS 的时候,Client 将文件切分成 一个一个的Block,然后进行存储。
NameNode: 即Master,
1、管理 HDFS 的名称空间。
2、管理数据块(Block)映射信息
3、配置副本策略
4、处理客户端读写请求。
DataNode: 就是Slave。NameNode 下达命令,DataNode 执行实际的操作。
1、存储实际的数据块。
2、执行数据块的读/写操作。
Secondary NameNode: 并非 NameNode 的热备。当NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务。
1、辅助 NameNode,分担其工作量。
2、定期合并 fsimage和fsedits,并推送给NameNode。
3、在紧急情况下,可辅助恢复 NameNode。
4、HDFS读、写文件过程
从HDFS读取内容:
1、首先调用DistributedFileSystem对象的open方法,其实获取的是一个DistributedFileSystem的实例。
2、DistributedFileSystem通过RPC(远程过程调用)获得文件的第一批block的locations,同一block按照重复数会返回多个locations,这些locations按照hadoop拓扑结构排序,距离客户端近的排在前面。
3、前两步会返回一个FSDataInputStream对象,该对象会被封装成 DFSInputStream 对象,DFSInputStream可以方便的管理DataNode和NameNode数据流。客户端调用read方法,DFSInputStream就会找出离客户端最近的DataNode并连接DataNode。
4、数据从DataNode源源不断的流向客户端。
5、如果第一个block块的数据读完了,就会关闭指向第一个block块的DataNode连接,接着读取下一个block块。这些操作对客户端来说是透明的,从客户端的角度来看只是读一个持续不断的流。
6、如果第一批block都读完了,DFSInputStream就会去NameNode拿下一批blocks的location,然后继续读,如果所有的block块都读完,这时就会关闭掉所有的流。
向HDFS写入内容:
1.客户端通过调用DistributedFileSystem的create方法,创建一个新的文件。
2.DistributedFileSystem通过RPC(远程过程调用)调用NameNode,去创建一个没有blocks关联的新文件。创建前,NameNode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,NameNode 就会记录下新文件,否则就会抛出IO异常。
3.前两步结束后会返回 FSDataOutputStream 的对象,和读文件的时候相似,FSDataOutputStream 被封装成 DFSOutputStream,DFSOutputStream 可以协调NameNode和 DataNode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列data queue。
4.DataStreamer 会去处理接受 data queue,它先问询 NameNode 这个新的 block 最适合存储的在哪几个DataNode里,比如重复数是3,那么就找到3个最适合的DataNode,把它们排成一个 pipeline。DataStreamer 把 packet 按队列输出到管道的第一个 DataNode 中,第一个 DataNode又把 packet 输出到第二个 DataNode 中,以此类推。
5.DFSOutputStream 还有一个队列叫 ack queue,也是由 packet 组成,等待DataNode的收到响应,当pipeline中的所有DataNode都表示已经收到的时候,这时akc queue才会把对应的packet包移除掉。
6.客户端完成写数据后,调用close方法关闭写入流。
7.DataStreamer 把剩余的包都刷到 pipeline 里,然后等待 ack 信息,收到最后一个ack 后,通知 DataNode 把文件标示为已完成。
部署过程
集群节点分布
| IP地址 | 主机名 | Namenode | Datanode | Resource Manager | NodeManager |
|---|---|---|---|---|---|
| 192.168.60.100 | master | Y | N | Y | N |
| 192.168.60.101 | slave1 | N | Y | N | Y |
| 192.168.60.102 | slave2 | N | Y | N | Y |
配置hosts(所有节点)
# vim /etc/hosts
192.168.60.100 master
192.168.60.101 slave1
192.168.60.102 slave2
配置用户和SSH
以下操作三节点都做:
# mkdir -p /data/hdfs
# useradd -M -d /data/hdfs hdfs
# echo 'hdfs'|passwd hdfs --stdin#
# cp /etc/skel/.bash* /data/hdfs/
# chown -R hdfs:hdfs /data/hdfs
# su - hdfs
$ ssh-keygen -t rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys
在master上将公钥复制到两台slave:
$ ssh-copy-id -i ~/.ssh/id_rsa.pub hdfs@slave1
$ ssh-copy-id -i ~/.ssh/id_rsa.pub hdfs@slave2
在两个slave节点上将公钥复制到master:
$ ssh-copy-id -i ~/.ssh/id_rsa.pub hdfs@master
部署Java环境(所有节点)
$ cd /data/hdfs
$ tar -zxvf jdk-8u172-linux-x64.tar.gz
# vim /etc/profile
export JAVA_HOME=/data/hdfs/jdk1.8.0_172
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
# source /etc/profile
# java -version
部署hadoop
1)创建目录(所有节点) 创建三个目录,分别用于保存HDFS的数据文件、namenode文件及临时文件:
$ mkdir -p /data/hdfs/{data,namenode,tmp}
2)解压配置环境变量(master节点)
$ tar -zxvf hadoop-3.2.0.tar.gz
# vim /etc/profile
export HADOOP_HOME=/data/hdfs/hadoop-3.2.0
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin
# source /etc/profile
3)设置hadoop配置文件(master节点)
在hadoop-env.sh中添加JAVA路径:
$ vi $HADOOP_HOME/etc/hadoop/hadoop-env.sh
添加:
export JAVA_HOME=/data/hdfs/jdk1.8.0_172
$ cd $HADOOP_HOME/etc/hadoop
$ vi core-site.xml
添加:
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/data/hdfs/tmp</value>
<description>
A base for other temporary directories
</description>
</property>
<property>
<name>io.file.buffer.size</name>
<value>131072</value>
</property>
<property>
<name>fs.default.name</name>
<value>hdfs://master:9000</value>
</property>
<property>
<name>hadoop.proxyuser.root.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
</configuration>
$ vi hdfs-site.xml //配置副本数量,数据存放目录等
<configuration>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/data/hdfs/namenode</value>
<final>true</final>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/data/hdfs/data</value>
<final>true</final>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>master:9001</value>
</property>
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
</configuration>
$ vi yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>master</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>master:18040</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>master:18030</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address</name>
<value>master:18088</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>master:18025</value>
</property>
<property>
<name>yarn.resourcemanager.admin.address</name>
<value>master:18141</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
</configuration>
$ vi mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>
$ vi slaves
salve1
slave2
配置完将hadoop整个目录copy到连个slave的家目录下面:
$ scp -r ~/hadoop-3.2.0/etc/hadoop hdfs@slave1:~/hadoop-3.2.0/etc/
$ scp -r ~/hadoop-3.2.0/etc/hadoop hdfs@slave2:~/hadoop-3.2.0/etc/
4)启动namenode(master节点)
$ hdfs namenode -format //格式化
$ hdfs --daemon start namenode //master节点上启动namenode守护进程
可以使用start-dfs.sh命令代替
5)启动datanode(slave节点)
$ hdfs --daemon start datanode //salve节点上启动datanode守护进程
可以使用start-dfs.sh命令代替
6)启动YARN
$ start-yarn.sh //master节点
# yarn-daemon.sh start nodemanager //slave节点
7、UI登录hadoop
http://IP:9870 HDFS管理界面
http://IP:18088 YARN管理界面
关于HDFS的一些简单操作
1) 在HDFS中创建目录
$ hdfs dfs -mkdir /test #在根目录下创建了test目录
2)放文件到HDFS
$ hdfs dfs -put file1 path1 #当前路径下的file1 放入HDFS路径path1中
$ hdfs dfs-copy FromLocal path1/file path2 #path1路径下file放入HDFS的path2下
3)List出HDFS中的内容
$ hdfs dfs -ls path #将HDFS中path路径中内容列出
$ hdfs dfs -ls -R path #将HDFS中path路径中内容递归列出
4) 从HDFS下载
$ hdfs dfs -get path/file #将HDFS中path路径下file下载到当前路径
好了,关于hadoop-hdfs集群的搭建就分享到这,具体的使用还是详见官网说明文档吧,希望对大家有所帮助,我们下期再见~~