Yarn资源调度框架
Hadoop1.x架构
在Hadoop1.x版本中只有HDFS和MapReduce两个组件组成,其中HDFS是分布式文件系统,MapReduce是计算框架。MapReduce 1.x 架构如下:
MapReduce1.x架构是Master/Slave架构,有全局唯一的JobTracker(Master)与多个TaskTracker(Slave),两个角色作用如下:
JobTracker
JobTracker全局唯一,集群启动就会创建,其负责资源管理、作业调度、作业监控、重新调度作业等,JobTracker会将Client提交的任务分发到各个TaskTracker上执行,并会监控各个TaskTracker,当TaskTracker宕机后,JobTracker会将当前TaskTracker上运行的任务转移到其他节点上运行
TaskTracker
TaskTracker负责运行由JobTracker调度的Task任务,并周期性向JobTracker汇报自身运行情况和作业执行情况
在MapReduce1.x架构中,Client 会将MapReduce 程序提交给JobTracker,由JobTracker进行调度运行task,将对应的task优先发送到数据所在节点上执行。在这个过程中JobTracker既负责资源管理也要负责任务调度,导致JobTracker压力大,并且JobTracker只有一个节点,存在单点故障问题,当出现故障时导致整个MapReduce运行不了,此外由于MapReduce1.x架构中JobTracker耦合了资源管理和任务调度,所以导致其他的大数据计算框架无法使用JobTracker进行资源调度管理,通用性差。为了解决以上问题,Hadoop2.x版本后引入了Yarn资源管理系统
Yarn资源管理平台
Apache Hadoop Yarn(Yet Another Reasource Negotiator,另一种资源协调者)是Hadoop2.x版本后使用的资源管理器,可以为上层应用提供统一的资源管理平台,Yarn主要由ResourceManager、NodeManager、ApplicationMaster、Container组成。架构如下下图所示:
ResourceManager
ResourceManager是Yarn集群中的中央管理器,负责整个集群的资源分配与调度。ResourceManager负责监控NodeManager节点状态、汇集集群资源,处理Client提交任务的资源请求,为每个Application启动AppliationMaster并监控
NodeManager
NodeManager负责管理每个节点上的资源(如:内存、CPU等)并向ResourceManager报告。当ResourceManager向NodeManager分配一个容器(Container)时,NodeManager负责启动该容器并监控容器运行,此外,NodeManager还会接收AplicationMaster命令为每个Application启动容器(Container)
ApplicationMaster
每个运行在Yarn中的应用程序都会启动一个对应的ApplicationMaster,其负责与ResourceManager申请资源及管理应用程序任务。ApplicationMaster本质上也是一个容器,由ResourceManager进行资源调度并由NodeManager启动,ApplicationMaster启动后会向ResourceManager申请资源运行应用程序,ResourceManager分配容器资源后,ApplicationMaster会连接对应NodeManager通知启动Container并管理运行在Container上的任务
Container
Container 容器是Yarn中的基本执行单元,用于运行应用程序的任务,它是一个虚拟环境,包含应用程序代码、依赖项及运行所需资源(内存、CPU、磁盘、网络)。每个容器都由ResourceManager分配给ApplicationMaster,并由NodeManager在相应的节点上启动和管理。容器的资源使用情况由NodeManager监控,并在必要时向ResourceManager报告
Yarn核心就是将MR1中JobTracker的资源管理和任务调度两个功能分开,分别由ResourceManager和ApplicationMaster进程实现,ResourceManager负责整个集群的资源管理和调度;ApplicationMaster负责应用程序任务调度、任务监控和容错等
Yarn任务运行流程
下面以MapReduce运行在Yarn中为例,说明基于Yarn运行任务整体流程
- 在客户端向Yarn中提交MR 任务,首先会将MR任务资源(Split、资源配置、Jar包信息)上传到HDFS中
- 客户端向ResourceManager申请启动ApplicationMaster
- ResourceManager会选择一台相对不忙的NodeManager节点,通知该节点启动ApplicationMaster(Container)
- ApplicationMaster启动之后,会从HDFS中下载MR任务资源信息到本地,然后向ResourceManager申请资源用于启动MR Task
- ResourceManager返回给ApplicationMaster资源清单
- ApplicationMaster进而通知对应的NodeManager启动Container
- Container启动之后会反向注册到ApplicationMaster中
- ApplicationMaster 将Task任务发送到Container 运行,Task任务执行的就是我们写的代码业务逻辑
Yarn集群搭建与启动
Yarn集群搭建只需要基于目前Hadoop进行进行配置文件配置即可,另外Yarn中ResourceManager也有HA高可用,也就是说Yarn也支持HA,下面基于目前Hadoop HDFS集群之上进行Yarn HA集群搭建
节点划分如下:
节点 | NN | DN | ZK | ZKFC | JN | RM | NM |
---|---|---|---|---|---|---|---|
node01 | ★ | ★ | ★ | ||||
node02 | ★ | ★ | ★ | ★ | ★ | ★ | |
node03 | ★ | ★ | ★ | ★ | ★ | ||
node04 | ★ | ★ | ★ | ★ |
准备
# 所有节点
# hosts 配置
cat >> /etc/hosts << "EOF"
192.168.91.31 node01
192.168.91.32 node02
192.168.91.33 node03
192.168.91.34 node04
EOF
# JDK安装
wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/8u392-b08/openlogic-openjdk-8u392-b08-linux-x64.tar.gz
tar xf openlogic-openjdk-8u392-b08-linux-x64.tar.gz
mv openlogic-openjdk-8u392-b08-linux-x64 /usr/local/jdk8
rm -f openlogic-openjdk-8u392-b08-linux-x64.tar.gz
echo 'export JAVA_HOME=/usr/local/jdk8' >> /etc/profile
echo 'export PATH=$PATH:${JAVA_HOME}/bin' >> /etc/profile
source /etc/profile
# 关闭防火墙
systemctl stop firewalld && systemctl disable firewalld
# 关闭selinux
sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
# 时间同步配置,最小化安装系统需要安装ntpdate软件
yum -y install ntpdate
echo "0 */1 * * * ntpdate time1.aliyun.com" >> /var/spool/cron/root
systemctl enable ntpdate && systemctl start ntpdate
# 在HDFS集群搭建完成后,在Namenode HA切换进行故障转移时采用SSH方式进行,底层会使用到fuster包,有可能我们安装Centos7系统没有fuster程序包,导致不能进行NameNode HA 切换,我们可以通过安装Psmisc包达到安装fuster目的,因为此包中包含了fuster程序,安装方式如下
yum -y install psmisc
# 创建bigdata用户
useradd bigdata
passwd bigdata
# 切换到bigdata用户
su - bigdata
mkdir /home/bigdata/data
mkdir /home/bigdata/log
zookeeper集群搭建
# node02、node03、node04 bigdata用户
su - bigdata
cd ~
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz
tar xf apache-zookeeper-3.6.4-bin.tar.gz
mv apache-zookeeper-3.6.4-bin ./apache-zookeeper-3.6.4
rm -f apache-zookeeper-3.6.4-bin.tar.gz
echo 'export ZOOKEEPER_HOME=/home/bigdata/apache-zookeeper-3.6.4' >> ~/.bashrc
echo 'export PATH=$PATH:${ZOOKEEPER_HOME}/bin' >> ~/.bashrc
source ~/.bashrc
# 创建配置文件
cat > $ZOOKEEPER_HOME/conf/zoo.cfg << EOF
# 发送心跳的间隔时间,单位:毫秒
tickTime=2000
initLimit=10
syncLimit=5
# ZooKeeper保存数据的目录
dataDir=/home/bigdata/data/zookeeper
# 日志目录
dataLogDir=/home/bigdata/log/zookeeper
clientPort=2181
# 各个节点配置
server.1=node02:2881:3881
server.2=node03:2881:3881
# observer(表示对应节点不参与投票)
server.3=node04:2881:3881
EOF
# 为每个zk节点创建数据目录,并在该目录创建一个文件myid,在myid中写下当前zk的编号
# 所有zk节点
mkdir -p /home/bigdata/data/zookeeper
# node02
echo 1 > /home/bigdata/data/zookeeper/myid
# node03
echo 2 > /home/bigdata/data/zookeeper/myid
# node04
echo 3 > /home/bigdata/data/zookeeper/myid
# 在所有zk节点分别启动ZooKeeper,zkServer.sh start|stop|status
# 启动zk,这里暂不启动,后面启动hadoop时再启动
zkServer.sh start
# 停止zk
zkServer.sh stop
# 查看zk状态
zkServer.sh status
hadoop集群安装
# node01 bigdata用户
su - bigdata
cd ~
# 免密配置
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
# 启动start-dfs.sh脚本的节点需要将公钥分发给所有节点
ssh-copy-id node01
ssh-copy-id node02
ssh-copy-id node03
ssh-copy-id node04
# 在HA模式下,每一个NN身边会启动ZKFC,ZKFC会用免密的方式控制自己和其他NN节点的NN状态
# 配置NN节点之间的互相免密,node01到node02免密已经配置过了,这里配置node02到node01免密
ssh node02
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
ssh-copy-id node01
ssh-copy-id node02
exit
# hadoop部署
wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz
tar xf hadoop-3.3.6.tar.gz
rm -f hadoop-3.3.6.tar.gz
chown -R bigdata:bigdata hadoop-3.3.6
echo 'export HADOOP_HOME=/home/bigdata/hadoop-3.3.6' >> ~/.bashrc
echo 'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> ~/.bashrc
source ~/.bashrc
# hadoop配置
# hadoop-env.sh脚本中JAVA_HOME配置
sed -i 's%# export JAVA_HOME=%export JAVA_HOME=/usr/local/jdk8%' $HADOOP_HOME/etc/hadoop/hadoop-env.sh
# datanode节点配置
cat > $HADOOP_HOME/etc/hadoop/workers << "EOF"
node02
node03
node04
EOF
# start-dfs.sh和stop-dfs.sh脚本中用户配置
cat > user_tmp.txt << EOF
HDFS_DATANODE_USER=bigdata
HDFS_DATANODE_SECURE_USER=hdfs
HDFS_NAMENODE_USER=bigdata
HDFS_JOURNALNODE_USER=bigdata
HDFS_ZKFC_USER=bigdata
YARN_RESOURCEMANAGER_USER=bigdata
YARN_NODEMANAGER_USER=bigdata
EOF
sed -i '17r user_tmp.txt' $HADOOP_HOME/sbin/start-dfs.sh
sed -i '17r user_tmp.txt' $HADOOP_HOME/sbin/stop-dfs.sh
rm -f user_tmp.txt
# core-site.xml、hdfs-site.xml、mapred-site.xml和yarn-site.xml配置文件如下所示
$HADOOP_HOME/etc/hadoop/core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/bigdata/data/hadoop</value>
</property>
<!-- 指定每个zookeeper服务器的位置和客户端端口号 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>node02:2181,node03:2181,node04:2181</value>
</property>
<!-- 建立连接的重试次数,ha模式下启动hdfs时,namenode需要与JournalNode建立连接,因为是同时启动的,JournalNode还没有准备好因此需要重试。这个配置默认是10,在虚拟机中的测试环境,节点性能较差,需要将这个配置改大,否则namenode可能起不来,需要单独手动启动 -->
<property>
<name>ipc.client.connect.max.retries</name>
<value>100</value>
</property>
</configuration>
$HADOOP_HOME/etc/hadoop/hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- 指定副本的数量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 以下是一对多,逻辑到物理节点的映射 -->
<!-- 解析参数dfs.nameservices值hdfs://mycluster的地址 -->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<!-- 测试环境中可以通过这个配置,关闭权限检查,方便操作 -->
<!--
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
-->
<!-- mycluster由以下两个namenode支撑 -->
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<!-- 指定nn1地址和端口号 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node01:8020</value>
</property>
<!-- 指定nn2地址和端口号 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node02:8020</value>
</property>
<!-- 下面两个http端口的配置,配置的是默认值,可以不用配置 -->
<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>
<!-- 指定客户端查找active的namenode的策略:会给所有namenode发请求,以决定哪个是active的 -->
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 指定三台journal node服务器的地址,以及数据存储的目录 -->
<!-- JournalNode可以在不同hdfs集群之间共享,不同的hdfs集群这里配置不同的存储目录即可 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node01:8485;node02:8485;node03:8485/mycluster</value>
</property>
<!-- journal服务数据存储目录 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/bigdata/data/journal</value>
</property>
<!-- 当active nn出现故障时,免密ssh到对应的服务器,将namenode进程kill掉 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!-- ssh免密公钥路径 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/bigdata/.ssh/id_rsa</value>
</property>
<!-- 启动NN故障自动切换,即启动zkfc -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>
$HADOOP_HOME/etc/hadoop/mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- 让MapReduce任务运行时使用Yarn资源调度框架进行调度 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!-- 起MapReduce任务时需要下面的配置 -->
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=${HADOOP_HOME}</value>
</property>
</configuration>
$HADOOP_HOME/etc/hadoop/yarn-site.xml
<?xml version="1.0"?>
<configuration>
<!-- 让yarn的容器支持mapreduce的shuffle,开启shuffle服务 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<!-- NodeManager 上Container可以继承的环境变量 -->
<name>yarn.nodemanager.env-whitelist</name>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
</property>
<!-- 启用resourcemanager的HA -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 指定zookeeper集群的各个节点地址和端口号 -->
<property>
<name>hadoop.zk.address</name>
<value>node02:2181,node03:2181,node04:2181</value>
</property>
<!-- 标识集群,以确保RM不会接管另一个集群的活动,与core-site.xml中保持一致 -->
<!-- 这个会反应在zk中,可以用于在zk中隔离不同的yarn集群 -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>mycluster</value>
</property>
<!-- RM HA的两个resourcemanager的名字 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- 指定rm1的reourcemanager进程所在的主机名称 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>node03</value>
</property>
<!-- RM HTTP访问地址,不配置的话在shell中提交MR任务时会报错"org.apache.hadoop.mapreduce.v2.app.client.MRClientService: Webapps failed to start. Ignoring for now:java.lang.NullPointerException" -->
<!-- 默认为 ${yarn.resourcemanager.hostname}:8088 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>node03:8088</value>
</property>
<!-- 指定rm2的reourcemanager进程所在的主机名称 -->
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>node04</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>node04:8088</value>
</property>
<!-- 关闭虚拟内存检查,生产环境不能关闭,测试环境资源不够应该关闭,否则后面对yarn的操作可能会报内存不足 -->
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
</configuration>
# node01 bigdata用户
su - bigdata
cd ~
tar -zcvf hadoop-3.3.6-config.tar.gz hadoop-3.3.6/
for i in 2 3 4; do scp hadoop-3.3.6-config.tar.gz node0$i:`pwd`; done
# node02-node04 节点,bigdata用户
su - bigdata
cd ~
tar xf hadoop-3.3.6-config.tar.gz
rm -f hadoop-3.3.6-config.tar.gz
echo 'export HADOOP_HOME=/home/bigdata/hadoop-3.3.6' >> ~/.bashrc
echo 'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> ~/.bashrc
source ~/.bashrc
启动HA 的HDFS和YARN
# 所有操作都是在bigdata用户下
# 1.启动zookeeper集群
# node02、node03、node04
$ZOOKEEPER_HOME/bin/zkServer.sh start
# 2.格式化zookeeper
# node01
hdfs zkfc -formatZK
# 3.启动Journalnode,格式化namenode时需要先启动Journalnode
# node01、node02、node03
hdfs --daemon start journalnode
# 4.选择一个Namenode格式化,格式化Namenode,会连接journalnode创建相应目录(因此journalnode需要先启动,可以通过journalnode的日志和数据目录观察到)
# node01
hdfs namenode -format
# 5.启动这个格式化的namenode
# 在node01上启动NameNode,便于后期同步Namenode
hdfs --daemon start namenode
# 6.同步元数据
# 在node02上同步NameNode元数据
hdfs namenode -bootstrapStandby
# 7.启动hadoop集群,只有第一次启动hadoop集群时需要先执行上面的步骤,后面启动hadoop集群时只需要执行start-all.sh即可,该脚本会同时启动NN,ZKFC、JN、DN、RM、NM,但是zk还是得先启动
# node01
$HADOOP_HOME/sbin/start-all.sh
Yarn集群启动成功后,可以访问node03:8088/node04:8088查看Yarn WebUI页面,其中有一个ResourceManager为StandBy状态,访问时会直接跳转到Active状态的ResourceManager 节点上
需要注意的是在启动HDFS集群或者Yarn集群时,start-dfs.sh/start-yarn.sh/start-all.sh会将对应的进程进行启动。在Hadoop集群中,我们也可以手动在对应的节点上启动/停止对应的进程,命令如下
# 启动/停止NameNode
hdfs --daemon start namenode
hdfs --daemon stop namenode
# 启动/停止DataNode
hdfs --daemon stat datanode
hdfs --daemon stop datanode
# 启动/停止JournalNode
hdfs --daemon start journalnode
hdfs --daemon stop journalnode
# 启动/停止 ZKFC
hdfs --daemon start zkfc
hdfs --daemon stop zkfc
# 启动/停止 ResourceManager
yarn --daemon start resourcemanager
yarn --daemon stop resourcemanager
# 启动/停止NodeManager
yarn --daemon start nodemanager
yarn --daemon stop nodemanager
基于Yarn集群运行MR任务
运行MR自带任务
MapReduce自带了WordCount案例,在Yarn集群搭建完成后我们可以直接基于Yarn运行WordCount任务,按照如下步骤操作
# node01 bigdata用户
# 1.在HDFS中创建/input,并准备数据
cd ~
cat > data.txt << EOF
hello zhangsan
hello lisi
hello zhangsan
hello wangwu
lisi
hello zhangsan
EOF
hdfs dfs -mkdir /input
hdfs dfs -put ./data.txt /input/
# 2.运行MR自带的WordCount程序
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /input/data.txt /output
# /input/data.txt 是WordCount程序读取的数据,/output目录是数据结果写出目录。执行完成之后,可以查看HDFS /output下数据结果
hdfs dfs -ls /output
-rw-r--r-- 3 bigdata supergroup 0 2024-10-11 19:56 /output/_SUCCESS
-rw-r--r-- 3 bigdata supergroup 35 2024-10-11 19:56 /output/part-r-00000
hdfs dfs -cat /output/*
hello 5
lisi 2
wangwu 1
zhangsan 3