第三章HDFS集群搭建与操作


4.1 节点基础环境准备

搭建真正分布式HDFS集群至少需要3台Linux节点,这里准备5台Linux节点,节点名称和ip信息如下,我们可以从头搭建各个Linux节点也可以基于已有快照创建各个Linux节点。

节点IP 节点名称
192.168.179.4 node1
192.168.179.5 node2
192.168.179.6 node3
192.168.179.7 node4
192.168.179.8 node5

这里默认已经创建好以上各个节点,并且每个节点分配资源为4核2G内存,在后续大数据学习过程中建议每台节点至少给到2核2G,否则可能一些组件不能正常运行问题。下面对这5个节点进行基础配置。

4.1.1 配置各个节点的Ip

启动每台节点,在对应的节点路径“/etc/sysconfig/network-scripts”下配置ifg-ens33文件配置IP(注意,不同机器可能此文件名称不同,一般以ifcfg-xxx命名),以配置ip 192.168.179.4为例,ifcfg-ens33配置内容如下:

TYPE=Ethernet
BOOTPROTO=static
#使用static配置DEFROUTE=yesPEERDNS=yesPEERROUTES=yesIPV4_FAILURE_FATAL=noIPV6INIT=yesIPV6_AUTOCONF=yesIPV6_DEFROUTE=yesIPV6_PEERDNS=yesIPV6_PEERROUTES=yesIPV6_FAILURE_FATAL=noONBOOT=yes#开机启用本配置IPADDR=192.168.179.4#静态IPGATEWAY=192.168.179.2#默认网关NETMASK=255.255.255.0#子网掩码DNS1=192.168.179.2#DNS配置 可以与默认网关相同

以上其他节点配置只需要修改对应的ip即可,配置完成后,在每个节点上执行如下命令重启网络服务:

systemctl restart network.service

检查每个节点ip执行如下命令:

ipaddr

4.1.2 配置主机名

在每台节点上修改/etc/hostname,配置对应的主机名称,参照节点IP与节点名称对照表分别为:node1、node2、node3、node4、node5。配置完成后需要重启各个节点,才能正常显示各个主机名。

关于Centos hostname配置有如下几点建议:

  • hostname只允许包含ascii字符里的数字0-9,字母a-zA-Z,连字符-和.,其他都不允许。例如,不允许出现其他标点符号,不允许空格,不允许下划线,不允许中文字符。
  • hostanme开头和结尾字符不允许是连字符。
  • hostanme强烈建议不要用数字开头,尽管这一条不是强制的。
  • hostanme建议用小写字母而不用大写字母。

4.1.3 关闭防火墙

执行如下命令确定各个节点上的防火墙开启情况,需要将各个节点上的防火墙关闭:

#检查防火墙状态firewall-cmd --state

#临时关闭防火墙(重新开机后又会自动启动)systemctl stop firewalld 或者systemctl stop firewalld.service

#设置开机不启动防火墙systemctl disablefirewalld

4.1.4 关闭SELinux

SELinux就是Security-Enhanced Linux的简称,安全加强的linux。传统的linux权限是对文件和目录的owner, group和other的rwx进行控制,而SELinux采用的是委任式访问控制,也就是控制一个进程对具体文件系统上面的文件和目录的访问,SELinux规定了很多的规则,来决定哪个进程可以访问哪些文件和目录。虽然SELinux很好用,但是在多数情况我们还是将其关闭,因为在不了解其机制的情况下使用SELinux会导致软件安装或者应用部署失败。

在每台节点/etc/selinux/config中将SELINUX=enforcing改成SELINUX=disabled即可。

4.1.5 配置阿里云yum源

后续为了方便在Linux节点上安装各个软件,我们将yum源改成国内阿里云yum源,这样下载软件速度快一些,每个节点具体操作按照以下步骤进行。

#安装wget,wget是linux最常用的下载命令(有些系统默认安装,可忽略)
yum -y install wget

#备份当前的yum源
mv
/etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

#下载阿里云的yum源配置
wget -O
/etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

#清除原来文件缓存,构建新加入的repo结尾文件的缓存
yum clean all
yum makecache

配置完成后,可以在每台节点上安装“vim”命令,方便后续操作:

#在各个节点上安装 vim命令yum-y install vim

4.1.6 设置Linux 系统显示中文/英文

Linux系统默认显示不支持中文,可以通过配置支持显示中文。每台节点具体操作参照如下步骤。

#查看当前系统语言echo$LANG#显示结果如下,说明默认支持显示英文en_US.UTF-8

#临时修改系统语言为中文,重启节点后恢复英文LANG=“zh_CN.UTF-8”#如果想要永久修改系统默认语言为中文,需要创建/修改/etc/locale.conf文件,写入以下内容,设置完成后需要重启各节点。LANG=“zh_CN.UTF-8”

4.1.7 设置自动更新时间

后续基于Linux各个节点搭建HDFS时,需要各节点的时间同步,可以通过设置各个节点自动更新时间来保证各个节点时间一致,具体按照以下操作来执行。

  1. 修改本地时区及ntp服务

yum -y install ntp
rm -rf /etc/localtime
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
/usr/sbin/ntpdate -u pool.ntp.org

  1. 自动同步时间

设置定时任务,每10分钟同步一次,配置/etc/crontab文件,实现自动执行任务。建议直接crontab -e 来写入定时任务。使用crontab -l 查看当前用户定时任务。

#各个节点执行 crontab -e 写入以下内容
*
/10 * * * * /usr/sbin/ntpdate -u pool.ntp.org >/dev/null2>&1#重启定时任务
service crond restart

#查看日期
date

4.1.8 设置各个节点之间的ip映射

每个节点都有自己的IP和主机名,各个节点默认进行文件传递或通信时需要使用对应的ip进行通信,后续为了方便各个节点之间的通信和文件传递,可以配置各个节点名称与ip之间的映射,节点之间通信时可以直接写对应的主机名称,不必写复杂的ip。每台节点具体操作按照以下操作进行。

进入每台节点的/etc/hosts下,修改hosts文件,vim /etc/hosts:

#在文件后面追加以下内容192.168.179.4node1192.168.179.5node2192.168.179.6node3192.168.179.7node4192.168.179.8node5

各个节点配置完成后,可以使用ping命令互相测试使用节点名称是否可以正常通信。

[root@node5 ~]# ping node1PING node1 (192.168.179.4) 56(84) bytesof data.
64bytesfromnode1 (192.168.179.4): icmp_seq=1ttl=64time=0.892ms
64bytesfromnode1 (192.168.179.4): icmp_seq=2ttl=64time=0.415ms

4.1.9 配置节点之间免密访问

后续搭建HDFS集群时需要Linux各个节点之间免密,节点两两免秘钥的根本原理如下:假设A节点需要免秘钥登录B节点,只要B节点上有A节点的公钥,那么A节点就可以免密登录当前B节点。具体操作步骤如下。

  1. 安装ssh客户端

需要在每台节点上安装ssh客户端,否则,不能使用ssh命令(最小化安装Liunx,默认没有安装ssh客户端),这里在Centos7系统中默认已经安装,此步骤可以省略:

yum -y install openssh-clients

  1. 创建.ssh目录

在每台节点执行如下命令,在每台节点的“~”目录下,创建.ssh目录,注意,不要手动创建这个目录,因为有权限问题。

cd~
ssh
localhost
#这里会需要输入节点密码#exit

  1. 配置各节点向一台节点通信免密

在每台节点上执行如下命令,给当前节点创建公钥和私钥:

ssh-keygen -t rsa -P ‘’-f ~/.ssh/id_rsa

将node1、node2、node3、node4、node5的公钥copy到node1上,这样这五台节点都可以免密登录到node1。命令如下:

#在node1上执行如下命令,需要输入密码ssh-copy-id node1#会在当前~/.ssh目录下生成authorized_keys文件,文件中存放当前node1的公钥#

#在node2上执行如下命令,需要输入密码ssh-copy-id node1#会将node2的公钥追加到node1节点的authorized_keys文件中

#在node3上执行如下命令,需要输入密码ssh-copy-id node1#会将node3的公钥追加到node1节点的authorized_keys文件中

#在node4上执行如下命令,需要输入密码ssh-copy-id node1#会将node4的公钥追加到node1节点的authorized_keys文件中

#在node5上执行如下命令,需要输入密码ssh-copy-id node1#会将node5的公钥追加到node1节点的authorized_keys文件中

  1. 各节点免密

将node1节点上~/.ssh/authorized_keys拷贝到node2、node3、node4、node5各节点的~/.ssh/目录下,执行如下命令:

scp ~/.ssh/authorized_keys node2:`pwd`
scp ~/.ssh/authorized_keys node3:`pwd`
scp ~/.ssh/authorized_keys node4:`pwd`
scp ~/.ssh/authorized_keys node5:`pwd`

以上node1向各个几点发送文件时需要输入密码,经过以上步骤,节点两两免密完成。

4.2 Hadoop集群运行模式

可以参考Hadoop官网进行Hadoop集群搭建,目前Hadoop集群搭建有三种模式:

  • 本地模式:单机运行,只能单机测试MapReduce运行任务,生产和测试都不会使用。
  • 伪分布式模式:单机运行,一台节点部署所有Hadoop角色,模拟分布式集群环境,具备Hadoop集群所有功能,生产和测试一般不会使用。
  • 完全分布式模式:多台服务器部署Hadoop集群,各个角色分布到不同节点上执行,生产和测试环境中经常使用。

后续搭建Hadoop 中HDFS集群时,我们会基于伪分布式和完全分布式两种搭建方式进行讲解,重点掌握完全分布式集群搭建。

4.3 HDFS伪分布式集群搭建

4.3.1 安装JDK

按照以下步骤在node1节点上安装JDK8。

  1. 在node1节点创建/software目录,上传并安装jdk8 rpm包

rpm -ivh /software/jdk-8u181-linux-x64.rpm

以上命令执行完成后,会在每台节点的/user/java下安装jdk8。

  1. 配置jdk环境变量

在node1节点上配置jdk的环境变量:

export JAVA_HOME=/usr/java/jdk1.8.0_181-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

以上配置完成后,最后执行“source /etc/profile”使配置生效。

4.3.2 HDFS伪分布式集群搭建

这里我们在 node1节点上进行HDFS伪分布式集群搭建,按照如下步骤进行搭建即可。

  1. 下载安装包并解压

我们安装Hadoop3.3.6版本,此版本目前是比较新的版本,搭建HDFS集群前,首先需要在官网下载安装包,地址如下: https://hadoop.apache.org/releases.html。下载完成安装包后,上传到node1节点的/software目录下并解压到opt目录下

#将下载好的hadoop安装包上传到node1节点上[root@node1~]# ls /software/hadoop-3.3.6.tar.gz

#将安装包解压到/opt目录下[root@node1~]# cd /software/[root@node1software]# tar -zxvf ./hadoop-3.3.6.tar.gz -C /opt

解压之后可以看到Hadoop安装包内容如下:

[root@node1 hadoop-3.3.6]# llbin
etc
includelib
libexec
LICENSE-
binarylicenses-binaryLICENSE.txt
logs
NOTICE-
binaryNOTICE.txt
README.txt
sbin
share

以上Hadoop解压文件重要目录解释如下:

  • bin目录:Hadoop最基本的管理脚本和使用脚本的目录,用户可以直接使用这些脚本管理和使用Hadoop。
  • etc目录:Hadoop配置文件所在的目录,包括core-site,xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml、works等。
  • include:对外提供的编程库头文件,这些头文件均是用C++定义的,通常用于C++程序访问HDFS或者编写MapReduce程序。
  • lib目录:lib目录包含了Hadoop对外提供的编程动态库和静态库,与include目录中的头文件结合使用。
  • sbin目录:Hadoop管理脚本所在的目录,主要包含HDFS和YARN中各类服务的启动/关闭脚本。
  • share目录:存放Hadoop的依赖jar包、文档、和官方案例,对HDFS 操作依赖的jar包都在这里。
  1. 在node1节点上配置Hadoop的环境变量

[root@node1 software]# vim /etc/profile
export HADOOP_HOME=
/opt/hadoop-3.3.6/
export PATH=$PATH:$HADOOP_HOME
/bin:$HADOOP_HOME/sbin:

#使配置生效
source/etc/profile

  1. 配置hadoop-env.sh

启动伪分布式HDFS集群时会判断$HADOOP_HOME/etc/hadoop/hadoop-env.sh文件中是否配置JAVA_HOME,所以需要在hadoop-env.sh文件加入以下配置(大概在54行有默认注释配置的JAVA_HOME):

#vim /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh
export JAVA_HOME=
/usr/java/jdk1.8.0_181-amd64/

  1. 配置core-site.xml

进入 $HADOOP_HOME/etc/hadoop路径下,修改core-site.xml文件,指定HDFS集群数据访问地址及集群数据存放路径。

#vim /opt/hadoop-3.3.6/etc/hadoop/core-site.xml
<configuration><!-- 指定NameNode的地址 –><property><name>fs.defaultFS</name><value>hdfs://node1:8020</value></property><!-- 指定 Hadoop 数据存放的路径 –><property><name>hadoop.tmp.dir</name><value>/opt/data/local_hadoop</value></property></configuration>

  1. 配置hdfs-site.xml

进入 $HADOOP_HOME/etc/hadoop路径下,修改hdfs-site.xml文件,指定NameNode和SecondaryNameNode节点和端口。

#vim /opt/hadoop-3.3.6/etc/hadoop/hdfs-site.xml
<configuration><!-- 指定block副本数–><property><name>dfs.replication</name><value>1</value></property><!-- NameNode WebUI访问地址–><property><name>dfs.namenode.http-address</name><value>node1:9870</value></property><!-- SecondaryNameNode WebUI访问地址–><property><name>dfs.namenode.secondary.http-address</name><value>node1:9868</value></property></configuration>

  1. 配置workers指定DataNode节点

进入 $HADOOP_HOME/etc/hadoop路径下,修改workers配置文件,加入以下内容:

#vim /opt/hadoop-3.3.6/etc/hadoop/workers
node1

  1. 配置start-dfs.sh&stop-dfs.sh

进入 $HADOOP_HOME/sbin路径下,在start-dfs.sh和stop-dfs.sh文件顶部添加操作HDFS的用户为root,防止启动错误。

#分别在start-dfs.sh 和stop-dfs.sh文件顶部添加如下内容HDFS_NAMENODE_USER=root
HDFS_DATANODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root

4.3.3 格式化并启动HDFS集群

HDFS完全分布式集群搭建完成后,首次使用需要进行格式化,在NameNode节点(node1)上执行如下命令:

#在node1节点上格式化集群[root@node1~]# hdfs namenode -format

格式化集群完成后就可以在node1节点上执行如下命令启动集群:

#在node1节点上启动集群[root@node1~]# start-dfs.sh

至此,Hadoop完全分布式搭建完成,可以浏览器访问HDFS WebUI界面,通过此界面方便查看和操作HDFS集群。WebUI访问地址如下,在Hadoop2.x版本中,访问的WEBUI端口为50070,Hadoop3.x 访问WebUi端口是9870。

NameNode WebUI访问地址为:https://node1:9870,需要在window中配置hosts。

 

image.png

SecondaryNameNode WebUI访问地址为:https://node1:9868

 

image.png

停止集群时只需要在NameNode节点上执行stop-dfs.sh命令即可。后续再次启动HDFS集群只需要在NameNode节点执行start-dfs.sh命令,不需要再次格式化集群。

4.3.4 查看集群目录

  1. 查看NameNode数据目录

可以在hdfs-site.xml中配置“dfs.namenode.name.dir”属性来指定NameNode存储数据的目录,默认NameNode数据存储在${hadoop.tmp.dir}/dfs/name目录,进入“/opt/data/local_hadoop/dfs/name/current”查看相应NameNode存储数据信息:

[root@node1 ~]# ll /opt/data/local_hadoop/dfs/name/current
edits_0000000000000000001-
0000000000000000002edits_inprogress_0000000000000000003
fsimage_0000000000000000000
fsimage_0000000000000000000.md5
seen_txid
VERSION

一个运行的NameNode如下的目录结构,该目录结构在第一次格式化的时候创建。

 

image.png

  • in_use.lock文件用于NameNode锁定存储目录。这样就防止其他同时运行的NameNode实例使用相同的存储目录。
  • edits表示edits log日志文件。
  • fsimage表示文件系统元数据镜像文件。
  • seen_txid记录edits操作编号,NameNode在checkpoint之前首先要切换新的edits log文件,在切换时更新seen_txid的值。上次合并fsimage和editslog之后的第一个操作编号。
  • VERSION文件是一个Java的属性文件。

 

image.png

  • layoutVersion是一个负数,定义了HDFS持久化数据结构的版本。这个版本数字跟hadoop发行的版本无关。当layout改变的时候,该数字减1(比如从-57到-58)。当对HDFDS进行了升级,就会发生layoutVersion的改变。
  • namespaceID是该文件系统的唯一标志符,当NameNode第一次格式化的时候生成。
  • clusterID是HDFS集群使用的一个唯一标志符,在HDFS联邦的情况下,就看出它的作用了,因为联邦情况下,集群有多个命名空间,不同的命名空间由不同的NameNode管理。
  • blockpoolID是block池的唯一标志符,一个NameNode管理一个命名空间,该命名空间中的所有文件存储的block都在block池中。
  • cTime标记着当前NameNode创建的时间。对于刚格式化的存储,该值永远是0,但是当文件系统更新的时候,这个值就会更新为一个时间戳。
  • storageType表示当前目录存储NameNode内容的数据结构。

 

  1. 查看SecondaryNameNode数据目录

可以在hdfs-site.xml中配置“dfs.namenode.checkpoint.dir”属性来指定SecondaryNameNode存储数据的目录,默认SecondaryNameNode数据存储在${hadoop.tmp.dir}/dfs/namesecondary目录,进入“/opt/data/local_hadoop/dfs/namesecondary/current”查看相应SecondaryNameNode存储数据信息:

[root@node1 ~]# ll /opt/data/local_hadoop/dfs/namesecondary/current
edits_0000000000000000001-
0000000000000000002fsimage_0000000000000000000
fsimage_0000000000000000000.md5
fsimage_0000000000000000002
fsimage_0000000000000000002.md5
VERSION

SecondaryNameNode数据目录主要是定期对NameNode中edits和fsimage进行合并,然后将合并数据推送给NameNode。

  1. 查看DataNode数据目录

可以在hdfs-site.xml中配置“dfs.datanode.data.dir”属性来指定DataNode存储数据的目录,默认NameNode数据存储在${hadoop.tmp.dir}/dfs/data目录,进入“/opt/data/local_hadoop/dfs/data/current”查看相应DataNode存储Block数据信息:

[root@node1 ~]# ll /opt/data/local_hadoop/dfs/data/current
BP-
1620277224-192.168.179.4-1705932038786VERSION

DataNode关键文件和目录结构如下:

 

image.png

  • HDFS块数据存储于blk_前缀的文件中,包含了被存储文件原始字节数据的一部分。
  • 每个block文件都有一个.meta后缀的元数据文件关联。该文件包含了一个版本和类型信息的头部,后接该block中每个部分的校验和。
  • 每个block属于一个block池,每个block池有自己的存储目录,该目录名称就是该池子的ID(跟NameNode的VERSION文件中记录的block池ID一样)。
  • 当一个目录中的block达到64个(通过dfs.datanode.numblocks配置)的时候,DataNode会创建一个新的子目录来存放新的block和它们的元数据。这样即使当系统中有大量的block的时候,目录树也不会太深。同时也保证了在每个目录中文件的数量是可管理的,避免了多数操作系统都会碰到的单个目录中的文件个数限制(几十几百上千个)。
  • 如果dfs.datanode.data.dir指定了位于在不同的硬盘驱动器上的多个不同的目录,则会通过轮询的方式向目录中写block数据。需要注意的是block的副本不会在同一个DataNode上复制,而是在不同的DataNode节点之间复制。

4.4 HDFS完全分布式集群搭建

4.4.1 节点规划

在前面课程中,我们知道Hadoop集群中有Namenode,SecondaryNameNode,DataNode各个角色,这里我们需要搭建HDFS完全分布式集群,在我们现有Linux集群中节点对应角色划分如下:

节点IP 节点名称 NameNode SecondaryNameNode DataNode
192.168.179.4 node1
192.168.179.5 node2
192.168.179.6 node3
192.168.179.7 node4
192.168.179.8 node5

4.4.2 安装JDK

按照以下步骤在各个节点上安装JDK8。

  1. 各个节点创建/software目录,上传并安装jdk8 rpm包

rpm -ivh /software/jdk-8u181-linux-x64.rpm

以上命令执行完成后,会在每台节点的/user/java下安装jdk8。

  1. 配置jdk环境变量

在每台节点上配置jdk的环境变量:

export JAVA_HOME=/usr/java/jdk1.8.0_181-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

每台节点配置完成后,最后执行“source /etc/profile”使配置生效。

4.4.3 HDFS完全分布式集群搭建

  1. 下载安装包并解压

我们安装Hadoop3.3.6版本,此版本目前是比较新的版本,搭建HDFS集群前,首先需要在官网下载安装包,地址如下: https://hadoop.apache.org/releases.html。下载完成安装包后,上传到node1节点的/software目录下并解压,没有此目录,可以先创建此目录

#将下载好的hadoop安装包上传到node1节点上[root@node1~]# ls /software/hadoop-3.3.6.tar.gz

[root@node1~]# cd /software/[root@node1software]# tar -zxvf ./hadoop-3.3.6.tar.gz

  1. 在node1节点上配置Hadoop的环境变量

[root@node1 software]# vim /etc/profile
export HADOOP_HOME=
/software/hadoop-3.3.6/
export PATH=$PATH:$HADOOP_HOME
/bin:$HADOOP_HOME/sbin:

#使配置生效
source/etc/profile

  1. 配置hadoop-env.sh

由于通过SSH远程启动进程的时候默认不会加载/etc/profile设置,JAVA_HOME变量就加载不到,而Hadoop启动需要读取到JAVA_HOME信息,所有这里需要手动指定。在对应的$HADOOP_HOME/etc/hadoop路径中,找到hadoop-env.sh文件加入以下配置(大概在54行有默认注释配置的JAVA_HOME):

#vim /software/hadoop-3.3.6/etc/hadoop/hadoop-env.sh
export JAVA_HOME=
/usr/java/jdk1.8.0_181-amd64/

  1. 配置core-site.xml

进入 $HADOOP_HOME/etc/hadoop路径下,修改core-site.xml文件,指定HDFS集群数据访问地址及集群数据存放路径。

#vim /software/hadoop-3.3.6/etc/hadoop/core-site.xml
<configuration><!-- 指定NameNode的地址 –><property><name>fs.defaultFS</name><value>hdfs://node1:8020</value></property><!-- 指定 Hadoop 数据存放的路径 –><property><name>hadoop.tmp.dir</name><value>/opt/data/hadoop</value></property></configuration>

  1. 配置hdfs-site.xml

进入 $HADOOP_HOME/etc/hadoop路径下,修改hdfs-site.xml文件,指定NameNode和SecondaryNameNode节点和端口。

#vim /software/hadoop-3.3.6/etc/hadoop/hdfs-site.xml
<configuration><!-- NameNode WebUI访问地址–><property><name>dfs.namenode.http-address</name><value>node1:9870</value></property><!-- SecondaryNameNode WebUI访问地址–><property><name>dfs.namenode.secondary.http-address</name><value>node2:9868</value></property></configuration>

  1. 配置workers指定DataNode节点

进入 $HADOOP_HOME/etc/hadoop路径下,修改workers配置文件,加入以下内容:

#vim /software/hadoop-3.3.6/etc/hadoop/workers
node3
node4
node5

  1. 配置start-dfs.sh&stop-dfs.sh

进入 $HADOOP_HOME/sbin路径下,在start-dfs.sh和stop-dfs.sh文件顶部添加操作HDFS的用户为root,防止启动错误。

#分别在start-dfs.sh 和stop-dfs.sh文件顶部添加如下内容HDFS_NAMENODE_USER=root
HDFS_DATANODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root

  1. 分发安装包

将node1节点上配置好的hadoop安装包发送到node2~node5节点上。这里由于Hadoop安装包比较大,也可以先将原有hadoop安装包上传到其他节点解压,然后在node1节点上只分发hfds-site.xml 、core-site.xml文件即可。

#在node1节点上执行如下分发命令
[root@node1 ~]# cd
/software/[root@node1 software]# scp -r ./hadoop-3.3.6/node2:/software/[root@node1 software]# scp -r ./hadoop-3.3.6/node3:/software/[root@node1 software]# scp -r ./hadoop-3.3.6/node4:/software/[root@node1 software]# scp -r ./hadoop-3.3.6/node5:/software/

  1. 在node2、node3、node4、node5节点上配置HADOOP_HOME

#分别在node2、node3、node4、node5节点上配置HADOOP_HOME
vim
/etc/profile
export HADOOP_HOME=
/software/hadoop-3.3.6/
export PATH=$PATH:$HADOOP_HOME
/bin:$HADOOP_HOME/sbin:

#最后记得Sourcesource/etc/profile

4.4.4 格式化并启动HDFS集群

HDFS完全分布式集群搭建完成后,首次使用需要进行格式化,在NameNode节点(node1)上执行如下命令:

#在node1节点上格式化集群[root@node1~]# hdfs namenode -format

格式化集群完成后就可以在node1节点上执行如下命令启动集群:

#在node1节点上启动集群[root@node1~]# start-dfs.sh

至此,Hadoop完全分布式搭建完成,可以浏览器访问HDFS WebUI界面,通过此界面方便查看和操作HDFS集群。WebUI访问地址如下,在Hadoop2.x版本中,访问的WEBUI端口为50070,Hadoop3.x 访问WebUi端口是9870。

NameNode WebUI访问地址为:https://node1:9870,需要在window中配置hosts。

 

image.png

SecondaryNameNode WebUI访问地址为:https://node1:9868

 

image.png

停止集群时只需要在NameNode节点上执行stop-dfs.sh命令即可。后续再次启动HDFS集群只需要在NameNode节点执行start-dfs.sh命令,不需要再次格式化集群。

4.4.5 查看集群目录

在HDFS完全分布式集群中,NameNode、SecondaryNameNode、DataNode数据目录与伪分布式集群目录路径一样,只是不同角色分配到了不同的集群节点中。

  1. 查看NameNode数据目录

可以在hdfs-site.xml中配置“dfs.namenode.name.dir”属性来指定NameNode存储数据的目录,默认NameNode数据存储在${hadoop.tmp.dir}/dfs/name目录,进入node1节点“/opt/data/hadoop/dfs/name/current”查看相应NameNode存储数据信息:

[root@node1 ~]# ll /opt/data/hadoop/dfs/name/current
edits_0000000000000000001-
0000000000000000002edits_inprogress_0000000000000000003
fsimage_0000000000000000000
fsimage_0000000000000000000.md5
seen_txid
VERSION

一个运行的NameNode如下的目录结构,该目录结构在第一次格式化的时候创建。

 

image.png

  • in_use.lock文件用于NameNode锁定存储目录。这样就防止其他同时运行的NameNode实例使用相同的存储目录。
  • edits表示edits log日志文件。
  • fsimage表示文件系统元数据镜像文件。
  • seen_txid记录edits操作编号,NameNode在checkpoint之前首先要切换新的edits log文件,在切换时更新seen_txid的值。上次合并fsimage和editslog之后的第一个操作编号。
  • VERSION文件是一个Java的属性文件。

 

image.png

  • layoutVersion是一个负数,定义了HDFS持久化数据结构的版本。这个版本数字跟hadoop发行的版本无关。当layout改变的时候,该数字减1(比如从-57到-58)。当对HDFDS进行了升级,就会发生layoutVersion的改变。
  • namespaceID是该文件系统的唯一标志符,当NameNode第一次格式化的时候生成。
  • clusterID是HDFS集群使用的一个唯一标志符,在HDFS联邦的情况下,就看出它的作用了,因为联邦情况下,集群有多个命名空间,不同的命名空间由不同的NameNode管理。
  • blockpoolID是block池的唯一标志符,一个NameNode管理一个命名空间,该命名空间中的所有文件存储的block都在block池中。
  • cTime标记着当前NameNode创建的时间。对于刚格式化的存储,该值永远是0,但是当文件系统更新的时候,这个值就会更新为一个时间戳。
  • storageType表示当前目录存储NameNode内容的数据结构。
  1. 查看SecondaryNameNode数据目录

可以在hdfs-site.xml中配置“dfs.namenode.checkpoint.dir”属性来指定SecondaryNameNode存储数据的目录,默认SecondaryNameNode数据存储在${hadoop.tmp.dir}/dfs/namesecondary目录,进入node2节点的“/opt/data/hadoop/dfs/namesecondary/current”查看相应SecondaryNameNode存储数据信息:

[root@node2 ~]# ll /opt/data/hadoop/dfs/namesecondary/current
edits_0000000000000000001-
0000000000000000002fsimage_0000000000000000000
fsimage_0000000000000000000.md5
fsimage_0000000000000000002
fsimage_0000000000000000002.md5
VERSION

SecondaryNameNode数据目录主要是定期对NameNode中edits和fsimage进行合并,然后将合并数据推送给NameNode。

  1. 查看DataNode数据目录

可以在hdfs-site.xml中配置“dfs.datanode.data.dir”属性来指定DataNode存储数据的目录,默认NameNode数据存储在${hadoop.tmp.dir}/dfs/data目录,进入node3~node5任意节点的“/opt/data/hadoop/dfs/data/current”路径查看相应DataNode存储Block数据信息:

[root@node1 ~]# ll /opt/data/hadoop/dfs/data/current
BP-
1620277224-192.168.179.4-1705932038786VERSION

DataNode关键文件和目录结构如下:

 

image.png

  • HDFS块数据存储于blk_前缀的文件中,包含了被存储文件原始字节数据的一部分。
  • 每个block文件都有一个.meta后缀的元数据文件关联。该文件包含了一个版本和类型信息的头部,后接该block中每个部分的校验和。
  • 每个block属于一个block池,每个block池有自己的存储目录,该目录名称就是该池子的ID(跟NameNode的VERSION文件中记录的block池ID一样)。
  • 当一个目录中的block达到64个(通过dfs.datanode.numblocks配置)的时候,DataNode会创建一个新的子目录来存放新的block和它们的元数据。这样即使当系统中有大量的block的时候,目录树也不会太深。同时也保证了在每个目录中文件的数量是可管理的,避免了多数操作系统都会碰到的单个目录中的文件个数限制(几十几百上千个)。
  • 如果dfs.datanode.data.dir指定了位于在不同的硬盘驱动器上的多个不同的目录,则会通过轮询的方式向目录中写block数据。需要注意的是block的副本不会在同一个DataNode上复制,而是在不同的DataNode节点之间复制。

4.5 HDFS集群搭建注意点

在搭建和使用HDFS集群中需要注意如下点:

  • HDFS伪分布式集群只是测试,HDFS完全分布式是重点,需要掌握HDFS完全分布式搭建。
  • 如果在集群搭建过程中出现错误需要重新格式化集群时,那么需要删除相关配置文件中指定的目录后,再次格式化,不删除先前集群数据目录会导致新的集群格式化不成功或者使用时有问题。
  • 无论是伪分布式还是完全分布式集群搭建,格式化集群只需要在集群搭建完成后第一次启动时执行,后续使用HDFS集群不需要格式化,只需要执行start-dfs.sh/stop-dfs.sh 启停集群即可。
  • window访问HDFS集群时,如果使用别名而非IP访问,需要在windows “C:\Windows\System32\drivers\etc\hosts”文件中配置ip和别名映射。

4.6 HDFS shell操作

基于xshell来操作HDFS时,可以使用HADOOP_HOME/bin/hadoopfs具体命令或者使用HADOOP_HOME/bin/hdfs dfs 具体命令,其中fs也可以使用dfs命令代替,dfs是fs的实现类。

hadoop fs 命令执行后,可以查看操作HDFS用法:

 

image.png

hadoop fs 命令等价于hdfs dfs ,都可以操作HDFS。

 

image.png

下面介绍HDFS中常用的一些操作命令。

4.6.1 -help

help主要是查看命令的帮助。

[root@node1 ~]# hdfs dfs -helpUsage: hadoop fs [generic options]
[
-appendToFile [-n] <localsrc> … <dst>]
[
-cat [-ignoreCrc] <src> …]
[
-checksum [-v] <src> …]
[
-chgrp [-R] GROUP PATH…]
[
-chmod [-R] <MODE[,MODE]… | OCTALMODE> PATH…]
[
-chown [-R] [OWNER][:[GROUP]] PATH…]
[
-concat <target path> <src path> <src path> …]
… …

4.6.2 -ls

-ls:主要显示目录信息,查看HDFS中某个目录下的文件信息。

[root@node1~\]# hdfs dfs -ls

4.6.3 -mkdir

-mkdir 在HDFS中创建目录,还可跟上-p来创建多级目录。

[root@node1~]# hdfs dfs -mkdir /hello[root@node1~]# hdfs dfs -mkdir /hello/a/b/cmkdir:`/hello/a/b/c’:Nosuchfileordirectory[root@node1~]# hdfs dfs -mkdir -p /hello/a/b/c

4.6.4 -moveFromLocal

-moveFromLocal:将文件从本地剪切到HDFS目录中。

[root@node1~]# vim data.txthello zhangsan
hello lisi
hello wangwu
[root
@node1~]# hdfs dfs -moveFromLocal ./data.txt /hello/[root@node1~]# hdfs dfs -ls /hello

4.6.5 -cat

-cat : 显示HDFS文件内容命令。

[root@node1 ~]# hdfs dfs -cat /hello/data.txt
hello zhangsan
hello lisi
hello wangwu

4.6.6 -appendToFile

-appendToFile :追加一个文件到已经存在的文件末尾。

[root@node1~]# vim data2.txtaaa
bbb
ccc

#将data2.txt文件内容追加到data.txt文件中[root@node1~]# hdfs dfs -appendToFile ./data2.txt /hello/data.txt[root@node1~]# hdfs dfs -cat /hello/data.txthello zhangsan
hello lisi
hello wangwu
aaa
bbb
ccc

4.6.7 -chmod

-chmod 给文件赋值权限,文件系统中的用法一样。

[root@node1 ~]# hdfs dfs -ls /hello/data.txt
-rw-r–r–
3root supergroup /hello/data.txt
[root@node1 ~]# hdfs dfs -chmod
777/hello/data.txt
[root@node1 ~]# hdfs dfs -ls
/hello/data.txt
-rwxrwxrwx
3root supergroup /hello/data.txt

4.6.8 -copyFromLocal:

-copyFromLocal: 从本地文件系统中拷贝文件到HDFS路径去。

[root@node1~]# vim data3.txthello zhangsan
hello lisi
hello wangwu

#将data3.txt拷贝到HDFS中[root@node1~]# hdfs dfs -copyFromLocal ./data3.txt /hello/[root@node1~]# lldata3.txt

4.6.9 -copyToLocal

-copyToLocal:从HDFS拷贝文件或者目录到本地。

[root@node1~]# hdfs dfs -copyToLocal /hello/ ./[root@node1~]# llhello

4.6.10 -cp

-cp : 从HDFS的一个路径拷贝到HDFS的另一个路径。

[root@node1 ~]# hdfs dfs -mkdir -p /hello2
[root@node1 ~]# hdfs dfs -cp
/hello/data.txt /hello2/[root@node1 ~]# hdfs dfs -ls /hello2
/hello2/data.txt

4.6.11 -mv

-mv:在HDFS目录中移动文件,将文件移动到某个HDFS目录中。

[root@node1 ~]# hdfs dfs -mkdir -p /hello3
[root@node1 ~]# hdfs dfs -mv
/hello2/data.txt /hello3/[root@node1 ~]# hdfs dfs -ls /hello3//hello3/data.txt

4.6.12 -get

-get:等同于copyToLocal,将文件从HDFS中下载文件到本地。

[root@node1~]# hdfs dfs -get /hello3 ./[root@node1~]# llhello3

4.6.13 -put

-put:等同于copyFromLocal,将本地文件复制上传到HDFS中。

[root@node1 ~]# hdfs dfs -put ./hello3.txt /

4.6.14 -getmerge

-getmerge:合并下载多个文件,比如HDFS的目录 /hello4下有多个文件:a.txt,b.txt,c.txt…,可以通过此命令,将数据合并下载到本地某个目录。

#创建hello4目录,并将对应的a.txt,b.txt,c.txt上传到此目录下[root@node1~]# hdfs dfs -mkdir /hello4[root@node1~]# hdfs dfs -put ./a.txt /hello4[root@node1~]# hdfs dfs -put ./b.txt /hello4[root@node1~]# hdfs dfs -put ./c.txt /hello4[root@node1~]# hdfs dfs -ls /hello4-rw-r–r– 3root supergroup /hello4/a.txt
-rw-r–r–
3root supergroup /hello4/b.txt
-rw-r–r–
3root supergroup /hello4/c.txt

[root@node1~]# hdfs dfs -getmerge /hello4/* ./merge.txt[root@node1~]# ll-rw-r–r–. 1root root merge.tx

4.6.15 -tail

-tail:显示一个文件最后1kb数据到控制台。

[root@node1 ~]# hdfs dfs -tail /hello4/merge.txt
aaa
bbb
ccc

4.6.16 -rm

-rm:删除文件或文件夹。可以加上 -r来递归删除目录下的所有数据。

[root@node1 ~]# hdfs dfs -rm /hello4/merge.txt
Deleted
/hello4/merge.txt
[root@node1 ~]# hdfs dfs -rm -r
/hello/Deleted /hello

4.6.17 -rmdir

-rmdir:删除空目录,目录必须是空目录才可以。

[root@node1~]# hdfs dfs -rmdir /hello2注意:目录必须是空目录

4.6.18 -du

-du:统计文件夹的大小信息。第一列标示该目录下总文件大小。第二列标示该目录下所有文件在集群上的总存储大小和你的副本数相关,副本数默认是3 ,所以第二列的是第一列的三倍(第二列内容=文件大小*副本数),第三列表示查询的目录。

[root@node1 ~]# hdfs dfs -du /hello4
412/hello4/a.txt
412/hello4/b.txt
412/hello4/c.txt

4.6.19 -setrep

-setrep:设置HDFS中文件的副本数量。

 

image.png

[root@node1 ~]# hdfs dfs -setrep 10/hello3/data.txt
Replication
10set: /hello3/data.txt

 

image.png

注意:这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台DataNode,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。

4.7 HDFS Api操作

4.7.1 Window环境准备

在window中通过IDEA编写操作HDFS的代码需要Window中配置Hadoop环境变量。首先将Hadoop3.x安装包下载到一个不包括中文、空格的路径中并解压,这里将Hadoop安装包解压到F盘下,如下:

 

image.png

然后在Window中配置Hadoop环境变量:

 

image.png

 

 

image.png

 

 

image.png

配置完Hadoop环境变量后,还需要将资料中“winutils-master\hadoop-3.3.5\bin”下的winutils.exe和hadoop.dll文件放在“F:\hadoop-3.3.6\bin”目录下。此外,hadoop.dll还要复制到“C:\Windows\System32”目录下。

后续通过Window操作HDFS集群时,为了避免用户权限问题,最好在Windows环境变量中配置“HADOOP_USER_NAME”为root:

 

image.png

4.7.2 API操作HDFS

在IDEA中创建项目,项目pom.xml导入依赖如下:

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>
1.8</maven.compiler.source>
<maven.compiler.target>
1.8</maven.compiler.target>
<hadoop.version>
3.3.6</hadoop.version>
<slf4j.version>
1.7.36</slf4j.version>
<log4j.version>
2.17.2</log4j.version>
<junit.version>
4.11</junit.version>
<
/properties><dependencies><!–操作 HDFS 所需依赖 --><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-client</artifactId><version>${hadoop.version}</version></dependency><!–slf4j&log4j 日志相关包 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-to-slf4j</artifactId><version>${log4j.version}</version></dependency></dependencies>

编写代码如下:

publicclass TestHDFS {
publicstatic FileSystem fs =null;

publicstatic void main(String[] args) throws IOException, InterruptedException {
Configuration conf =
newConfiguration(true);
//创建FileSystem对象fs =FileSystem.get(URI.create(“hdfs://node1:8020/”),conf,“root”);

//查看HDFS路径文件listHDFSPathDir(“/”);
System.out.println(
“=====================================”);

//创建目录mkdirOnHDFS(“/laowu/testdir”);
System.out.println(
“=====================================”);

//向HDFS 中上传数据writeFileToHDFS(“./data/test.txt”,“/laowu/testdir/test.txt”);
System.out.println(
“=====================================”);

//重命名HDFS文件renameHDFSFile(“/laowu/testdir/test.txt”,“/laowu/testdir/new_test.txt”);
System.out.println(
“=====================================”);

//查看文件详细信息getHDFSFileInfos(“/laowu/testdir/new_test.txt”);
System.out.println(
“=====================================”);

//读取HDFS中的数据readFileFromHDFS(“/laowu/testdir/new_test.txt”);
System.out.println(
“=====================================”);

//删除HDFS中的目录或者文件deleteFileOrDirFromHDFS(“/laowu/testdir”);
System.out.println(
“=====================================”);

//关闭fs对象fs.close();
}

privatestatic void listHDFSPathDir(String hdfsPath) throws IOException {
FileStatus[] fileStatuses =fs.listStatus(
newPath(hdfsPath));
for(FileStatus fileStatus : fileStatuses) {
System.out.println(fileStatus.getPath());
}
}

privatestatic void mkdirOnHDFS(String dirpath) throws IOException {
Path path =
newPath(dirpath);

//判断目录是否存在if(fs.exists(path)) {
System.out.println(
“目录”+dirpath +“已经存在”);
return;
}

//创建HDFS目录boolean result =fs.mkdirs(path);
if(result) {
System.out.println(
“创建目录”+dirpath +“成功”);
}
else{
System.out.println(
“创建目录”+dirpath +“失败”);
}
}

privatestatic void writeFileToHDFS(String localFilePath, String hdfsFilePath) throws IOException {
//判断HDFS文件是否存在,存在则删除Path hdfsPath =newPath(hdfsFilePath);
if(fs.exists(hdfsPath)) {
fs.delete(hdfsPath,
true);
}

//创建HDFS文件路径Path path =newPath(hdfsFilePath);
FSDataOutputStream out =fs.create(path);

//读取本地文件写入HDFS路径中FileReader fr =newFileReader(localFilePath);
BufferedReader br =
newBufferedReader(fr);
String newLine =
“”;
while((newLine =br.readLine()) !=null) {
out.write(newLine.getBytes());
out.write(
“\n”.getBytes());
}

//关闭流对象out.close();
br.close();
fr.close();

//以上代码也可以调用copyFromLocalFile方法完成//参数解释如下:上传完成是否删除原数据;是否覆盖写入;本地文件路径;写入HDFS文件路径fs.copyFromLocalFile(false,true,newPath(localFilePath),newPath(hdfsFilePath));
System.out.println(
“本地文件 ./data/test.txt 写入了HDFS中的”+hdfsFilePath+“文件中”);

}

privatestatic void renameHDFSFile(String hdfsOldFilePath,String hdfsNewFilePath) throws IOException {
fs.rename(
newPath(hdfsOldFilePath),newPath(hdfsNewFilePath));
System.out.println(
“成功将”+hdfsOldFilePath+“命名为:”+hdfsNewFilePath);
}

privatestatic void getHDFSFileInfos(String hdfsFilePath) throws IOException {
Path file =
newPath(hdfsFilePath);
RemoteIterator<LocatedFileStatus>listFilesIterator =fs.listFiles(file,
true);//是否递归while(listFilesIterator.hasNext()){
LocatedFileStatus fileStatus =listFilesIterator.next();
System.out.println(
“文件详细信息如下:”);
System.out.println(
“权限:”+fileStatus.getPermission());
System.out.println(
“所有者:”+fileStatus.getOwner());
System.out.println(
“组:”+fileStatus.getGroup());
System.out.println(
“大小:”+fileStatus.getLen());
System.out.println(
“修改时间:”+fileStatus.getModificationTime());
System.out.println(
“副本数:”+fileStatus.getReplication());
System.out.println(
“块大小:”+fileStatus.getBlockSize());
System.out.println(
“文件名:”+fileStatus.getPath().getName());

//获取当前文件block所在节点信息BlockLocation[] blks =fileStatus.getBlockLocations();
for(BlockLocation nd : blks) {
System.out.println(
“block信息:”+nd);
}
}
}

privatestatic void readFileFromHDFS(String hdfsFilePath) throws IOException {
//读取HDFS文件Path path=newPath(hdfsFilePath);
FSDataInputStream in =fs.open(path);
BufferedReader br =
newBufferedReader(newInputStreamReader(in));
String newLine =
“”;
while((newLine =br.readLine()) !=null) {
System.out.println(newLine);
}

//关闭流对象br.close();
in.close();
}

privatestatic void deleteFileOrDirFromHDFS(String hdfsFileOrDirPath) throws IOException {
//判断HDFS目录或者文件是否存在Path path =newPath(hdfsFileOrDirPath);
if(!fs.exists(path)) {
System.out.println(
“HDFS目录或者文件不存在”);
return;
}

//第二个参数表示是否递归删除boolean result =fs.delete(path, true);
if(result){
System.out.println(
“HDFS目录或者文件 “+path+” 删除成功”);
}
else{
System.out.println(
“HDFS目录或者文件 “+path+” 删除成功”);
}

}

}

 

版权声明:
作者:gww
链接:https://www.wenwugong.cn/?p=11
来源:武哥开发技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>