0%

Hadoop学习笔记

Hadoop是由Common,HDFS,YARN,MapReduce以及Ozone这些模块构成的开源软件,和其他相关项目,比如Spark,Hive以及HBase等统一称为Hadoop生态系统,本文主要介绍从存储模型架构模型版本区别以及集群搭建来介绍Hadoop

存储模型

HDFS

化整为零,并行计算的思想决定了Hadoop的存储模型——HDFS(Hadoop Distributed File System,分布式文件系统),化整为零指将大文件分成许多小文件HDFS,即分布式文件系统,并行计算指将多个小文件发送到多台服务器同时进行计算

服务器

塔式服务器

台式计算机是由机箱和显示器组成,去掉显示器的机箱就叫做塔式服务器(Tower Server)

机架服务器

放在柜子里面的服务器扁平类型的服务器叫机架服务器(Rack Server),也叫机柜服务器,一般一个机柜里面可以放16到32个机架服务器

刀片服务器

由一条条主板和位于底侧的母板构成的服务器叫刀片服务器(Blade Server),其中主板上布有cpu和内存;刀片服务器的成本虽然高,但是算力强,可以作为单点服务器

Block

将文件上传HDFS的时候会被分割成多个Block并均匀地分散到各个服务器上,假设有10个Block,那么如果10个服务器,每个Block就会分别上传到一台服务器上,如果有5台服务器,那么每两个Block就会传到一台服务器上。Block的发送是无序的,每个Block默认大小是128M

分割

上传文件变成为二进制并按照字节进行线性分割成大小一致的Block,但是针对占3个字节的utf-8编码的汉字可能会造成部分切割的问题,但是hadoop已将该问题解决,同一个文件被切成的多个Block大小一致,但多个文件之间的Block大小以及Block个数可能不一致

副本

每个Block默认有3个副本,Block副本有两种分配策略

  1. 如果在DataNode上传文件,会在该节点创建第一个副本,然后在与第一个副本不同机柜的某个服务器创建第二

    个副本,然后在与第二个副本同一个机柜的不同服务器上创建第三个副本,如果

  2. 若上传的客户端不再集群节点上,那上传之后会找一个服务器存放第一个副本,然后在与第一个副本不同机柜的某个服务器创建第二

    个副本,然后在与第二个副本同一个机柜的不同服务器上创建第三个副本,如果

注:每个服务器上的副本个数最多为1

补充

HDFS中的文件不可修改,因为每个Block都有相应的偏移量,如果修改其中某个Block,就会发生蝴蝶效应,即其相邻的block以及其他block的偏移量都需要进行修改,进而需要将整个集群进行修改。但是可以对HDFS进行append操作来保持之前数据的一致性

架构模型

文件数据分为元数据(描述数据的数据)数据本身,由于大量的数据需要一次写入多次读取,因此为了更加快速高效地查询数据,产生了Hadoop的主从架构模型,接下来分六点分别介绍主从节点以及之间的关系

  1. NameNode节点(主节点)是单节点,负责保存元数据,
  2. DataNode节点(从节点)是多节点,负责存储block数据
  3. NameNode与DataNode要保持心跳,即DataNode动态统计上传给NameNode可用的block列表信息
  4. HDFS Client与NameNode交互元数据信息,比如要存储文件的时候,需要判断存储路径是否存在,如果不存在需要新建路径,再比如要查询文件的时候需要确认文件在哪些节点上
  5. HDFS Client与DataNode交互文件block数据
  6. DataNode利用服务器本地文件系统直接存储block,即将block转成文件存储在某个目录直线,可以看出HDFS的所用成本很低

3.x与2.x的区别

  1. classpath隔离,不会造成不同版本的jar包冲突
  2. 支持hdfs的擦除编码(erasure encoding),进而节省了50%的空间
  3. datanode内部新增负载均衡,比如新增datenode之后,其余datenode会将部分数据导入新增的datenode之中
  4. shell重写,3.x与2.x有三分之一的区别,并且大部分区别都在shell重写
  5. mr支持内内存参数推断
  6. cgroup将内存和磁盘隔离(dock就是cgroup和namespace的二次开发)

集群搭建

伪分布式

Step1. 安装jdk1.8

Step2./usr/hadoop-3.1.4/etc/hadoop/hadoop-env.sh的末尾新增环境变量以及节点启动用户

# 集群启动之后会从该脚本下寻找java环境变量,而不是/etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_261
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root

Step3./usr/hadoop-3.1.4/etc/hadoop/core-site.sh中配置NameNode节点,元数据以及block数据保存路径

<configuration> 
<property>
<name>fs.defaultFS</name>
<value>hdfs://node:9820</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/hadoop/pseudo</value>
</property>
</configuration>

Step4./usr/hadoop-3.1.4/etc/hadoop/hdfs-site.sh中配置SecondaryNameNode以及副本信息

<configuration> 
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>node01:9868</value>
</property>
</configuration>

Step5./usr/hadoop-3.1.4/etc/hadoop/workers中配置从节点信息,相当于Hadoop2.x中的slaves

node01

Step6./usr/hadoop-3.1.4格式化namenode并启动

# 格式化namenode之后会在 hadoop.tmp.dir/dfs/name/current下面产生四个文件,其中有两个image文件以及1个VERSION文件,VERSION里面包含了集群的唯一编号clusterID
./bin/hdfs namenode -format

# 可以通过jps查看NameNode,DataNode以及SecondaryNameNode节点是否启动成功
./sbin/start-dfs.sh

Step7. 在本地浏览器通过node01:9870访问NameNode信息

# 如果忘记端口号,可以通过下述命令查看监听的端口信息
ss -nal

# 通过浏览器访问之前需要关闭防火墙
systemctl stop firewalld.service # 关闭防火墙
systemctl disable firewalld.service # 开机不启动防火墙
systemctl status firewalld.service # 查看防火墙状态

全分布式

假设现在有4个节点,每个节点的角色如下

NameNode SecondaryNameNode DataNode
node01 ✔︎
node02 ✔︎ ✔︎
node03 ✔︎
node04 ✔︎

Step1. 在4个节点上安装jdk1.8

Step2./usr/hadoop-3.1.4/etc/hadoop/hadoop-env.sh的末尾新增环境变量以及节点启动用户

# 集群启动之后会从该脚本下寻找java环境变量,而不是/etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_261
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root

Step3./usr/hadoop-3.1.4/etc/hadoop/core-site.sh中配置namenode节点,元数据以及Block数据保存路径

<configuration> 
<property>
<name>fs.defaultFS</name>
<value>hdfs://node:9820</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/hadoop/full</value>
</property>
</configuration>

Step4./usr/hadoop-3.1.4/etc/hadoop/hdfs-site.sh中配置SecondaryNameNode以及副本信息,其中SecondaryNameNode的作用是负责文件合并以及持久化,并不是namenode的备用节点

<configuration> 
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>node02:9868</value>
</property>
</configuration>

Step5./usr/hadoop-3.1.4/etc/hadoop/workers中配置从节点信息,相当于Hadoop2.x中的slaves

node02
node03
node04

Step6./etc/profile修改环境变量

export JAVA_HOME=/usr/java/jdk1.8.0_261
export HADOOP_HOME=/usr/hadoop-3.1.4
export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$JAVA_HOME/bin:$PATH

Step7./usr下将配置分发到其他节点

scp /usr/hadoop-3.1.4 node02:`pwd`
scp /usr/hadoop-3.1.4 node03:`pwd`
scp /usr/hadoop-3.1.4 node04:`pwd`

# 并在/etc下将环境变量配置发送到其他节点
scp /etc/profile node02:`pwd`
scp /etc/profile node03:`pwd`
scp /etc/profile node04:`pwd`

Step8. 在所有节点执行环境变量

source /etc/profile

Step9. 在node01上格式化namenode并启动集群

hdfs namenode -format

start-dfs

Step10. 在本地浏览器通过node01:9870访问namenode信息

# 如果忘记端口号,可以通过下述命令查看监听的端口信息
ss -nal

# 通过浏览器访问之前需要关闭防火墙
systemctl stop firewalld.service # 关闭防火墙
systemctl disable firewalld.service # 开机不启动防火墙
systemctl status firewalld.service # 查看防火墙状态

联邦

为了解决NameNode存储瓶颈问题,提出了联邦(Fedration)的思想,即多个NameNode之间相互独立,底层共用一套DataNode集。但通过指定NameNode进行了数据存储,后期只能从该NameNode进行数据查询,因此会造成NameNode指定错误的问题,大公司为了解决该问题一般会在上方搭建一个文件系统(目录树),每个目录/接口都与底层的指定的NameNode或HBSE(当数据仅仅用来存储)对应

联邦虽然解决了NameNode存储瓶颈问题并实现了扩展性,但仍然无法解决NameNode单点故障转移的问题,即其中一个NameNode挂掉了,就无法找到相应DataNode信息的问题

高可用

假设现在有4个节点,每个节点的角色如下

NN-1 NN-2 DN ZK ZKFC JNN
node01 ✔︎ ✔︎ ✔︎
node02 ✔︎ ✔︎ ✔︎ ✔︎ ✔︎
node03 ✔︎ ✔︎ ✔︎
node04 ✔︎ ✔︎

Step1. 在4个节点上安装jdk1.8

Step2./usr/hadoop-3.1.4/etc/hadoop/hadoop-env.sh的末尾新增环境变量以及节点启动用户

export JAVA_HOME=/usr/java/jdk1.8.0_261
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_ZKFC_USER=root
export HDFS_JOURNALNODE_USER=root

Step3./usr/hadoop-3.1.4/etc/hadoop/hdfs-site.sh配置其他节点的角色信息

<configuration> 
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node01:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node02:8020</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>node01:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>node02:9870</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node01:8485;node02:8485;node03:8485/mycluster</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/var/hadoop/ha/journalnode</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>

Step4./usr/hadoop-3.1.4/etc/hadoop/core-site.sh中配置NameNode节点,元数据,Block数据保存路径以及Zookeeper与客户端通信的节点信息

<configuration> 
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/var/hadoop/ha</value>
</property>
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>node02:2181,node03:2181,node04:2181</value>
</property>
</configuration>

Step5. 假设之前在4个节点搭建过全分布式,那么直接在node01中/usr/hadoop-3.1.4/etc/hadoop/下将修改过的配置分发到其余3个节点

scp hadoop-env.sh core-site.xml hdfs-site.xml node02:`pwd`
scp hadoop-env.sh core-site.xml hdfs-site.xml node03:`pwd`
scp hadoop-env.sh core-site.xml hdfs-site.xml node04:`pwd`

Step6. 在node02,node03,node04上搭建zookeeper集群,其中zookeeper集群与hadoop集群是相互独立的

Step6.1. 在node02上安装zookeeper3.4.6,并在/etc/profile配置环境变量

export JAVA_HOME=/usr/java/jdk1.8.0_261
export HADOOP_HOME=/usr/hadoop-3.1.4
export ZOOKEEPER_HOME=/usr/zookeeper-3.4.6
export PATH=$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$ZOOKEEPER_HOME/bin:$PATH

Step6.2. 将配置好的环境变量发送到其他节点上并执行

scp /etc/profile node02:/etc/profile
scp /etc/profile node03:/etc/profile
scp /etc/profile node03:/etc/profile

# 发送之后在每个节点上执行环境变量
source /etc/profile

Step6.3. 在node02上将/usr/zookeeper-3.4.6/conf/zoo_sample.cfg重命名为/usr/zookeeper-3.4.6/conf/zoo.cfg,并修改

dataDir=/var/zk

# 在结尾添加
server.1=node02:2888:3888
server.2=node03:2888:3888
server.3=node04:2888:3888

Step6.4. 在node02的/usr/下将配置好的zookeeper分发到其余节点

scp zookeeper-3.4.6 node03:`pwd`
scp zookeeper-3.4.6 node04:`pwd`

Step6.5. 根据之前的/usr/zookeeper-3.4.6/conf/zoo.cfg的配置信息在搭建zookeeper的3个节点上分别增加服务器编号文件

# node02
mkdir -p /var/zk
echo 1 > /var/zk/myid
cat /var/zk/myid

# node03
mkdir -p /var/zk
echo 2 > /var/zk/myid
cat /var/zk/myid

# node04
mkdir -p /var/zk
echo 2 > /var/zk/myid
cat /var/zk/myid

Step6.6. 分别在node02,node03,node04上启动zookeeper集群

# 启动集群
zkServer.sh start

# 查看启动状态
zkServer.sh status

补充

1) zookeeper启动遵从过半机制,即启动zookeeper服务器个数超过搭建zookeeper服务器个数的一半才可以正常启动zookeeper集群
2) 可以通过zkCli.sh来启动客户端进而对zookeeper集群进行修改,但一般是在namenode在进行状态切换的时候会对zookeeper进行修改
3) zookeeperyou选举机制,一般是根据服务器编号或者启动顺序来决定搭建zookeeper的服务器是follower还是leader

Step7. 在node01,node02,node03上分别启动jounalnode

# 可以通过jps查看node01,node02,node03的JNN是否启动
# hdfs --daemon stop journalnode是关闭journal
hdfs --daemon start journalnode

Step8. namenode同步透穿

Step8.1. 在node01上格式化namenode并启动namenode

# 也可以在namenode2上格式化namenode,但只能格式化一个namenode,另一个namenode需要通过JNN同步透穿来作standby角色
hdfs namenode -format

# 启动namenode使得另外一个namenode进行同步工作
hadoop-daemon.sh start namenode

Step8.2. 在node02上同步node01上的namenode信息

hdfs namenode -bootstrapStandby

Step9. 格式化zookeeper

# 在zookeeper创建一个2级的注册目录/mycluster/hadoop-ha,将要保存各个nanmenode节点信息
# 之后所有的namenode都会抢着想注册目录中写active或者standby状态
hdfs zkfc -format zk

Step10. 测试主备namenode节点切换

Step10.1. 在node01关闭active状态的namenode

hdfs --daemon stop namenode

通过node01:9870查看node01挂掉,通过node02:9870查看node02的状态变成active,通过zkCli.sh进入客户端,查询mycluster/hadoop-ha可以查看到两个节点的状态

在node01上重启namenode

hdfs --daemon start namenode

通过node01:9870查看node01变成了standby,通过node02:9870查看node02的状态变成active

Step10.2. 关闭node02的zkfc进程

hdfs --daemon stop zkfc

通过node01:9870查看node01变成了active,通过node02:9870查看node02的状态变成standby

补充

  1. 脑裂(brain-split):处于active状态的namenode挂掉/假死状态的时候,另外一个那么node启动,过段时间挂掉/假死状态的namenode重新启动导致两个active状态的namenode同时工作的现象较脑裂

  2. 解决脑裂方法

    使用sshfence隔离机制,即active的namenode假死之后,zkfc进程向zookeeper写入异常,standby的namenode上zkfc从zookeeper读入异常后就会通过ssh连接挂掉的active状态的namenode并将其kill掉(但显然如果真挂掉的话是连接不上的),如果收到成功kill的信息,当前standby的namenode状态就会切换到active,如果经过一段时间都没有收到kill执行成功的信息,那么当前standby的namenode就会执行自定义脚本,但是该方法导致了active的namenode挂掉之后,standby状态的namenode没有马上升级为active状态,解决该问题的办法就是在在/usr/hadoop-3.1.4/etc/hadoop/hdfs-site.sh中修改dfs.ha.fencing.methods

    <property>
    <name>dfs.ha.fencing.methods</name>
    <value>
    sshfence
    shell(/bin/true)
    </value>
    </property>

Step11. 在node01上启动HA集群

start-dfs.sh