13Yarn资源调度框架

0 阅读8分钟

Yarn资源调度框架

Hadoop1.x架构

在Hadoop1.x版本中只有HDFS和MapReduce两个组件组成,其中HDFS是分布式文件系统,MapReduce是计算框架。MapReduce 1.x 架构如下:

image.png

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组成。架构如下下图所示:

image.png

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运行任务整体流程

image.png

  1. 在客户端向Yarn中提交MR 任务,首先会将MR任务资源(Split、资源配置、Jar包信息)上传到HDFS中
  2. 客户端向ResourceManager申请启动ApplicationMaster
  3. ResourceManager会选择一台相对不忙的NodeManager节点,通知该节点启动ApplicationMaster(Container)
  4. ApplicationMaster启动之后,会从HDFS中下载MR任务资源信息到本地,然后向ResourceManager申请资源用于启动MR Task
  5. ResourceManager返回给ApplicationMaster资源清单
  6. ApplicationMaster进而通知对应的NodeManager启动Container
  7. Container启动之后会反向注册到ApplicationMaster中
  8. ApplicationMaster 将Task任务发送到Container 运行,Task任务执行的就是我们写的代码业务逻辑

Yarn集群搭建与启动

Yarn集群搭建只需要基于目前Hadoop进行进行配置文件配置即可,另外Yarn中ResourceManager也有HA高可用,也就是说Yarn也支持HA,下面基于目前Hadoop HDFS集群之上进行Yarn HA集群搭建

节点划分如下:

节点NNDNZKZKFCJNRMNM
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