HDFS 简介
HDFS 架构
如同其名称,HDFS (Hadoop Distributed File System)是 Hadoop 框架内进行分布式存储的组件,具有容错性和可扩展性。
HDFS 可以作为 Hadoop 集群的一部分,也可以作为独立的通用分布式文件系统。例如, HBase 就建立在 HDFS 之上,而 Spark 也可以将 HDFS 作为数据源之一。学习 HDFS 的架构和基本操作,对于特定集群的配置、改进和诊断将会有较大的帮助。
HDFS 是 Hadoop 应用程序主要使用的分布式存储,即数据的来源和去向。HDFS 集群主要由管理文件系统元数据的 NameNode 和存储实际数据的 DataNodes 组成,其架构如下图所示。此图描述了 NameNode 、 DataNodes 和客户端(Client)之间的交互模式。
如图所示,HDFS 使用了主/从架构设计(Master / Slave)。**在 HDFS 集群中,包含了一个 NameNode 和多个 DataNode 。**其中 NameNode 可以视作主服务器(Master),它负责管理文件系统命名空间(NameSpace)和调节客户端对文件的访问。而 DataNodes 负责管理连接到它们运行的节点的存储,通常是集群中的每个节点运行着一个 DataNode。HDFS 对外部公开文件系统的命名空间,并且允许用户的数据以文件形式进行存储。
在 HDFS 内部,一个文件被分割成一个或多个块(Block)来进行存储,若干个块存储在一组 DataNode 中。默认情况下,一个 Block 的大小是 128MB(Hadoop 2.x) ,也可以通过配置文件修改这个值的大小。
NameNode 负责维护块与 DataNode 之间的映射关系,可以执行诸如打开、关闭、重命名文件和目录等文件系统命名空间操作。而 DataNode 负责处理来自文件系统客户端的读写请求,同时配合 NameNode 的指令来进行块的创建、删除和复制等操作。
集群中单个 NameNode 的存在极大地简化了系统的体系结构。NameNode 负责所有 HDFS 元数据的仲裁和存储。但对于用户而言,他们的数据永远不会经过 NameNode 流转。客户端(Client)与 NameNode 进行通信,可以获得文件的元数据或者文件修改的信息,若要进行实际的文件读写,则是直接与 DataNodes 打交道。
通俗来讲,可以将 NameNode 和 DataNode 视作是运行在商用机器上的软件。这些机器通常运行着 Linux 操作系统。由于 HDFS 是使用 Java 语言构建的,因此任何支持 Java 的机器都可以运行 NameNode 和 DataNode 的实例。Java 语言具有的高度可移植性使得 HDFS 可以在广泛的环境中运行(只要有 JVM)。
在完全分布式的部署环境中,多个节点里一般有一个专用节点用于运行 NameNode 。而集群中的其他节点都运行着 DataNode 的一个实例。在这种体系结构的设计下,同一个节点上也有可能运行着多个 DataNode,但实际的生产环境部署中这种情况很少发生。
文件系统命名空间
在使用上,可以将 HDFS 与传统的 Linux 文件系统类比。Linux 主要通过目录树的形式来组织文件,而 HDFS 也支持传统的分层文件组织,其命名空间的层次结构与大多数现有文件系统相似。
用户或者应用程序可以通过访问接口(命令行、API 等)在 HDFS 中创建目录和存储文件。可以创建和删除文件,将文件从一个目录移动到另一个目录,或者重命名文件。
截至 2.6.1 版本,HDFS 还没有实现用户配额或访问权限,也暂不支持硬链接或软链接。但 HDFS 的架构并不排除实现这些特性的可能,在今后的版本更新中可能会对上述功能提供支持。
在 HDFS 中,对文件系统命名空间或者他们的属性的任何更改,都由 NameNode 负责记录。文件的副本数量称为该文件的复制因子(Replication Factor),该信息由 NameNode 进行存储。用户可以指定 HDFS 上应当维护的文件副本数量。
数据副本
HDFS 在研发之初,就被设计用于在大型集群中,以跨节点、高可靠的方式存储非常大的文件。前文提到了 HDFS 是以块(Block)为实体来存储文件的,具体而言,它将每个文件存储为块的序列。除了最后一个块以外,文件中的所有的块都是相同的大小。
如上图所示,part-0 文件的副本数量为 2 ,在具体存储时由编号为 1 和 3 的块构成;part-1 文件的副本数量为 3 ,在具体存储时被分隔成了编号 2 、 4 、 5 的块来分布到不同的 DataNode 上。
在 HDFS 集群内部,文件的块将会被复制成多份,以保证容错性。每个文件都可以配置块的大小和复制因子数量。上一小节提到用户可以指定这个数量,具体而言,该复制因子可以在文件创建时被指定,也可以在文件已经存储后进行更改。在 HDFS 中,所有文件都只能写入一次,并且任何时候都只有一个写入者。
NameNode 决定了文件块如何被复制。它定期从集群中的每个 DataNode 接收心跳和块的状态报告。如果能接收到 DataNode 发送过来的心跳,意味着它们的工作状态是正常的;而块的状态报告包含了 DataNode 上所有块的列表。
因此 HDFS 具有一定的高可用性,当某个文件副本因为磁盘损坏或者其他原因丢失的时候,系统内部可以通过调度从集群其他地方恢复这个文件的副本。
文件系统元数据持久化
HDFS 的命名空间由 NameNode 进行存储。NameNode 通过名为 EditLog 的事务日志来持久记录发生在文件系统元数据中的每一个更改。例如,在 HDFS 中创建一个新的文件,将会导致 NameNode 将一条记录插入到指示这一点的 EditLog 中。NameNode 使用操作系统本地文件系统中的文件来存储 EditLog 。
此外,整个文件系统命名空间(包括块到文件和文件系统属性的映射关系)都存储在名为 FsImage 的文件之中。同样 FsImage 也作为一个文件存储在 NameNode 所在的本地文件系统中。NameNode 在启动时会从磁盘读取 FsImage 和 EditLog ,并将 EditLog 中表示的所有事务应用到内存中的 FsImage 映像中,随后将这个新版本的 FsImage 刷新到磁盘上进行持久化保存。当然,它也可以删除(Truncate)旧的 EditLog ,因为其中的事务已经持久地保存在 FsImage 中了。
**上述过程称为检查点(Checkpoint)。**在当前版本(2.6.1)中,只有当 NameNode 启动时才会出现检查点,今后的版本可能会有定期检查的功能。
其它特性
此外,在面试过程中经常被提及的一个问题便是 HDFS 使用的通信协议。所有 HDFS 通信协议都建立在 TCP/IP 协议之上。
对于客户端与 NameNode 的通信,是客户端主动建立到 NameNode 节点上可配置的 TCP 端口之间的连接,可称之为 Client 协议。
而 DataNode 与 NameNode 之间的通信依赖于 DataNode 协议。
远程过程调用(RPC)抽象包装了上述两种协议。从设计的角度来说,NameNode 不会主动发出任何 RPC 请求,而只响应由 DataNode 或客户端发出的 RPC 请求。
以下是可供扩展阅读的资料:
HDFS 基本操作
初始化 HDFS
这仅限于从未格式化,如果有数据请谨慎操作
hdfs namenode -format
导入文件
任何对 HDFS 文件系统的操作都是以 hdfs dfs 开头,再辅以相应的操作参数。最常用的参数是 put ,其用法如下,可在终端中输入该命令。
hdfs dfs -put /home/hadoop-policy.xml /policy.xml
命令最后的 /policy.xml 代表存放于 HDFS 的文件名是 policy.xml ,路径是 / (根目录)。如果你想继续沿用之前的文件名,则可以直接填写路径 / 。
如果需要上传多个文件,则可以连续地填写本地目录的文件路径,并以 HDFS 目标存放路径作为结尾:
hdfs dfs -put /home/hadoop/sample_data/*.sh /user/hadoop
在填写路径相关的参数时,其规则与 Linux 系统下的文件目录规则相同,你可以使用通配符(例如 *.sh)等技巧来简化操作。
文件和文件夹操作
文件操作
(1)列出文件
同样地,可以使用 -ls 参数来列出指定目录中的文件:
hdfs dfs -ls /user/hadoop
如果需要查看某个文件的内容,则可以使用 cat 参数。最容易想到的是直接填写 HDFS 上的某个文件路径。
(2)文件对比
如果需要比对本地目录与 HDFS 之上的文件,则可以分别填写它们的路径。但需要注意的是,本地目录需要以 file:// 指示符开头,并辅以文件路径(如 /home/hadoop/.bashrc ,不要忘记路径开始的 / )。否则,任何填写到此处的路径都会默认识别为 HDFS 上的路径。
hdfs dfs -cat file:///home/hadoop/.bashrc /user/hadoop/mapred-env.sh
(3)复制
如果需要复制某个文件到另一个路径,则可以使用 cp 参数。
hdfs dfs -cp /user/hadoop/mapred-env.sh /user/hadoop/copied_file.txt
(4)移动
同样地,如果需要移动某个文件,则使用 mv 参数。这与 Linux 的文件系统命令格式基本相同。
hdfs dfs -mv /user/hadoop/mapred-env.sh /moved_file.txt
(5)追加
如果想把一些新的内容追加到 HDFS 上的某个文件中,则可以使用 appendToFile 参数。并且在填写待追加的本地文件路径时,可以填写多个。最后一个参数将作为被追加的对象,该文件必须在 HDFS 上存在,否则将报错。
echo 1 >> a.txt echo 2 >> b.txt hdfs dfs -appendToFile a.txt b.txt /user/hadoop/mapred-env.sh
可以使用 tail 参数来查看文件尾部内容,以确认是否追加成功。
hdfs dfs -tail /user/hadoop/mapred-env.sh
(6)删除
如果需要删除某个文件或目录,则使用 rm 参数。该参数还可以附带 -r 和 -f,其意义与 Linux 文件系统相同。
hdfs dfs -rm /moved_file.txt
目录操作
(1)遍历创建
实际上,如果需要同时创建多个目录,你可以直接将多个目录的路径作为参数填入。-p 参数表明当其父目录不存在时,将会被自动创建。
hdfs dfs -mkdir -p /user/hadoop/dir1 /user/hadoop/dir2/sub_dir1
(2)查看资源占有
如果想查看某些文件或者目录占用了多少的空间,则可以使用 du 参数:
hdfs dfs -du /user/hadoop/
导出文件
在前文中我应用程序计算完成并产生了记录结果的文件,则可以使用 get 参数来将其导出到 Linux 系统的本地目录中。
在此处第一个路径参数指的是 HDFS 内部的路径,而最后一个路径指的是保存在本地目录的路径。
hdfs dfs -get /user/hadoop/mapred-env.sh /home/hadoop/exported_file.txt
如果导出成功,可以在本地目录找到该文件
Web 管理界面
每个 NameNode 和 DataNode 都在内部运行着一个 Web 服务器,用于显示集群的当前状态等基本信息。在默认配置下,NameNode 的首页是http://localhost:50070/。它列出了集群中的 DataNode 和集群的基本统计数据。
打开浏览器,在地址栏中输入:
http://localhost:50070/
在 OverView 中可以看到当前“集群”中活跃的 DataNode 节点数量:
如果集群发生了问题,并且使用 HDFS 时始终报错,可以先从这里检查集群的状态是否正常,再结合日志进行分析。
Web 界面也可以用于浏览 HDFS 内部的目录和文件,在顶部菜单栏点击 “Utilities” 下的 “Broswe the file system” 链接。
WebHDFS REST API
WebHDFS 部署
webhdfs:使用 http 协议访问 HDFS
配置项
1、编辑 httpfs-env.sh,设置 webdfs 访问端口
<!-- >打开httpfs-env.sh <--> vim /opt/hadoop-2.7.3/etc/hadoop/httpfs-env.sh <!-- >增加如下内容:<--> export HTTPFS_HTTP_PORT=14000
2、编辑 core-site.xml,设置代理主机
<property> <name>hadoop.proxyuser.root.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.root.groups</name> <value>*</value> </property>
3、编辑 hdfs-site.xml
<property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property>
4、重启 NameNode,执行
httpfs.sh start
(使用内嵌的 tomcat 启动)
5、命令参考资料:
如果将 HDFS 作为某个云计算系统的存储组件组成部分,外部访问是不可缺少的功能。为此, HDFS 提供了基于 HTTP 协议访问的方式—— WebHDFS 。
WebHDFS 概述
文件系统 URI 与 HTTP URL
维基百科对 URI 的定义如下:
统一资源标识符(Uniform Resource Identifier, URI)是为通过 URI 方案明确标识资源和可扩展性而设计的字符串。
WebHDFS 的文件系统语法格式是以 webhdfs:// 开头的,一个标准的 WebHDFS 文件系统 URI 写法如下:
webhdfs://:/
上面的 WebHDFS URI 等价于下面的 HDFS URI 。
hdfs://:/
在 REST API 中,在路径之前多了一个前缀 /webhdfs/v1 ,在路径之后多了一些操作的参数 ?op=...,因此相关的 HTTP URL 写法应当是如下格式:
http://:/webhdfs/v1/?op=...
启用 WebHDFS
在启用 WebHDFS 之前,首先应该关闭 Hadoop 集群。任何对 Hadoop 的修改都应当先关闭集群以避免对数据产生影响。
使用 Vim 编辑器打开 hdfs-site.xml 文件:
vim /opt/hadoop-2.6.1/etc/hadoop/hdfs-site.xml
打开之后,在 configuration 标签中新增以下属性和配置值:
<property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property>
默认情况下,如果外部应用想要访问 Namenode ,则可以直接与 Namenode 的 IP 和 50070 端口进行通信。
同样地,如果外部应用想要使用 WebHDFS 诸如上传下载的文件管理功能,则需要通过 Datanode 的 IP 和 50075 端口来与其进行通信。
在实际应用中,如果不想过于细致地区分这些端口,则可以按照以上的方式来启用 Namenode 的 WebHDFS 功能。
修改完毕后,应当再次启动 Hadoop 的相关服务
创建目录
在集群启动完成之后,可以利用 curl 工具来模拟 HTTP 请求。
在终端中输入以下命令来尝试创建目录:
curl -i -X PUT "http://localhost:50070/webhdfs/v1/user/hadoop/webhdfs_dir?op=MKDIRS"
如果直接使用以上命令来创建目录,可能会遇到权限相关报错信息:
这是由于 WebHDFS 启用的安全认证模式导致的。在目前的安全等级下,我们可以附加一个 user.name 参数来指明以哪个用户创建目录。更高的权限设置可以在官方文档 Authentication for Hadoop HTTP web-consoles 中查看如何设置。
在之前的 URL 中附加一个参数 &user.name=hadoop 来表示我们用 hadoop 用户创建目录。
再次尝试在终端中输入以下命令:
curl -i -X PUT "http://localhost:50070/webhdfs/v1/user/hadoop/webhdfs_dir?op=MKDIRS&user.name=hadoop"
上传文件
使用 WebHDFS 上传文件需要两个步骤,首先是在 NameNode 上创建文件的信息。首先提交一个 HTTP 的 PUT 请求,不需要自动跟踪重定向,此时也不需要发送文件的数据。
将 op 参数设置为 CREATE,表明当前操作是创建文件。附加的参数 overwrite=true 是文件名相同时的解决办法,即覆盖原来的文件。
请在终端中输入以下命令来创建文件,此时请求的 URL 仍然是 NameNode 的 IP 和端口:
curl -i -X PUT "http://localhost:50070/webhdfs/v1/user/hadoop/new_uploaded_file.txt?op=CREATE&overwrite=true"
可以看到 HTTP 响应信息里有一个 Location 字段,第二步便是利用申请到的 Location 来上传实际的文件数据。需要注意的是,此时的 URL 已经变成了 DataNode 的 IP 和端口。在本课程中由于是伪分布式部署,你可以用 localhost 替代其 IP 。
在终端中输入以下命令来发送数据,你可以直接复制之前返回的 Localtion 里的 URL 作为请求目标。
**请注意,涉及到文件修改的操作,都需要身份认证。**因此我们还需要在请求的 URL 尾部添加一个参数,即使用哪个用户来创建:user.name=hadoop
curl -i -X PUT -T /home/hadoop/sample_data/yarn-site.xml "http://localhost:50075/webhdfs/v1/user/hadoop/new_uploaded_file.txt?op=CREATE&namenoderpcaddress=localhost:9000&overwrite=true&user.name=hadoop"
可以看到响应信息里, Location 字段已经生成了上传成功的文件的 URI 。
重命名文件和目录
有了之前的基础,重命名文件和目录的操作比较容易理解,直接使用 RENAME 作为操作参数的值。并指定 destination 参数即可,该参数是指重命名之后的文件路径。
curl -i -X PUT "http://localhost:50070/webhdfs/v1/user/hadoop/new_uploaded_file.txt?op=RENAME&destination=/user/hadoop/rename.txt&user.name=hadoop"
下载文件
下载文件只需要发送一个 HTTP GET 请求,并自动跟随重定向。
curl -i -L "http://localhost:50070/webhdfs/v1/user/hadoop/rename.txt?op=OPEN" -o /home/hadoop/downloaded.txt
请求首先被重定向到可以读取到文件数据的 DataNode ,然后客户端(也就是我们发出请求的这方)将会被重定向到 DataNode 以读取文件的数据。你也可以使用 curl 的 -o 参数将其保存到文件中。
列出文件和状态统计
如果想知道某个目录下有哪些文件,可以使用 LISTSTATUS 操作:
curl -i "http://localhost:50070/webhdfs/v1/user/hadoop?op=LISTSTATUS"
返回的信息将以 JSON 格式组织,在具体开发时可以利用一些 JSON 解析工具对这些数据进行处理。
如果想要查看文件或目录的状态,可以使用 GETFILESTATUS 操作。发送一个 HTTP GET 请求即可:
curl -i "http://localhost:50070/webhdfs/v1/user/hadoop?op=GETFILESTATUS"