【概述】
在YARN中,不管是ApplicationMaster(后面均简称AM),还是一般的container(例如MR中的map任务、reduce任务;Spark中的executor或者Flink中的TaskManager),都有各自的启动上下文(ContainerLaunchContext)。
上下文中包含了任务启动所依赖的资源(包括jar,资源文件等)、环境变量、启动参数等。
那么启动上下文具体是怎样传递给yarn,yarn是否进行了处理,进行了怎样的处理,本文就来聊聊这些相关内容。
【参数传递流程】
对于AM而言,客户端向ResourceManager(RM)提交任务,提交的任务请求中就包含了上下文信息,RM收到请求后进行调度,最后通过NodeManager(NM)的心跳告知NM启动AM,在这个过程中将上下文信息传递给NM,NM拿到上下文信息构造出AM的启动脚本,并启动AM。
对于一般container,是由AM直接向NM发起请求,要求启动container,请求中同样携带了上下文信息。同样,NM根据这个上下文信息,构造出对应的启动脚本,然后启动container。
【示例】
先来看个实际的例子:
上图是客户端向RM提交MR任务时的抓包情况,结合protobuf文件与源码中上下文的数据结构,可以整理出,上下文信息包含了四个资源文件:
上下文中还指定了环境变量HADOOP_CLASSPATH,以及AM的启动参数。
该任务最终对应的启动脚本是这样的:
#!/bin/bash
export HADOOP_CONF_DIR="/opt/service/hadoop/etc/hadoop"
export MAX_APP_ATTEMPTS="2"
export JAVA_HOME="/usr/java/jdk1.8.0_231"
export APP_SUBMIT_TIME_ENV="1617090350054"
export NM_HOST="172.19.6.60"
export HADOOP_CLASSPATH="$PWD:job.jar/job.jar:job.jar/classes/:job.jar/lib/*:$PWD/*:/opt/service/hadoop/contrib/capacity-scheduler/*.jar"
export LD_LIBRARY_PATH="$PWD:$HADOOP_COMMON_HOME/lib/native"
export HADOOP_HDFS_HOME="/opt/service/hadoop-2.8.5"
export LOGNAME="root"
export JVM_PID="$$"
export PWD="/opt/data/hadoop/yarn/nodemanager/local/user/cache/root/appcache/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001"
export HADOOP_COMMON_HOME="opt/service/hadoop"
export LOCAL_DIRS="/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011"
export APPLICATION_WEB_PROXY_BASE="/proxy/application_1616833932566_0011"
export SHELL="/bin/bash"
export NM_HTTP_PORT="8042"
export LOG_DIRS="/opt/data/hadoop/yarn/nodemanager/log/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001"
export NM_AUX_SERVICE_mapreduce_shuffle="AAAAO+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
export NM_PORT="9100"
export USER="root"
export HADOOP_YARN_HOME="/opt/service/hadoop-2.8.5"
export CLASSPATH="$PWD:$HADOOP_CONF_DIR:$HADOOP_COMMON_HOME/share/hadoop/common/*:$HADOOP_COMMON_HOME/share/hadoop/common/lib/*:$HADOOP_HDFS_HOME/share/hadoop/hdfs/*:$HADOOP_HDFS_HOME/share/hadoop/hdfs/lib/*:$HADOOP_YARN_HOME/share/hadoop/yarn/*:$HADOOP_YARN_HOME/share/hadoop/yarn/lib/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*:job.jar/job.jar:job.jar/classes/:job.jar/lib/*:$PWD/*"
export HADOOP_TOKEN_FILE_LOCATION="/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001/container_tokens"
export HOME="/home/"
export CONTAINER_ID="container_e21_1616833932566_0011_01_000001"
export MALLOC_ARENA_MAX="4"
ln -sf "/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011/filecache/11/job.jar" "job.jar"
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
mkdir -p jobSubmitDir
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
ln -sf "/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011/filecache/12/job.split" "jobSubmitDir/job.split"
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
ln -sf "/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011/filecache/13/job.xml" "job.xml"
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
mkdir -p jobSubmitDir
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
ln -sf "/opt/data/hadoop/yarn/nodemanager/local/usercache/root/appcache/application_1616833932566_0011/filecache/10/job.splitmetainfo" "jobSubmitDir/job.splitmetainfo"
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
exec /bin/bash -c "$JAVA_HOME/bin/java -Djava.io.tmpdir=$PWD/tmp -Dlog4j.configuration=container-log4j.properties -Dyarn.app.container.log.dir=/opt/data/hadoop/yarn/nodemanager/log/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001 -Dyarn.app.container.log.filesize=0 -Dhadoop.root.logger=INFO,CLA -Dhadoop.root.logfile=syslog -Xmx1024m org.apache.hadoop.mapreduce.v2.app.MRAppMaster 1>/opt/data/hadoop/yarn/nodemanager/log/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001/stdout 2>/opt/data/hadoop/yarn/nodemanager/log/application_1616833932566_0011/container_e21_1616833932566_0011_01_000001/stderr "
hadoop_shell_errorcode=$?
if [ $hadoop_shell_errorcode -ne 0 ]
then
exit $hadoop_shell_errorcode
fi
【上下文的处理】
从上面的实例中可以看到,客户端请求中的上下文仅描述了资源名称与位置,环境变量以及具体启动的参数,那么NM是如何处理这个上下文并最终构造出启动脚本的呢?
通过源码分析,NM在启动任务前,根据上下文信息,转换成具体的启动脚本,具体处理包括:
环境变量写入脚本
所有环境变量都存放在一个map中,然后遍历该map表,逐个转换成下面的格式:
export KEY="VALUE"
整个流程下来就构成了完整的启动脚本,随后真正启动对应进程。
【总结】
本文简单总结了container启动上下文包括哪些内容,如何传递的,yarn是如何进行处理的。
这里留个引子,前面示例中客户端提交任务的上下文中,其资源文件指定的是HDFS的路径,那么这些资源文件最终是如何下载的本地的?
从NM构造的启动脚本中看到对这些资源文件都做了软链接,那么软链接的路径又是如何决定的?
这些资源文件能否给其他任务使用?资源文件什么时候删除。。。
所有这些问题都涉及NM中的资源管理,下篇文章,我们就来分析下NM中的资源管理。