在非HA情况下,如果HDFS中RM-Restart相关的块丢失,会导致RM无法启动。
RM重启失败日志:
查看查看HDFS丢失块:
该出的块丢失之所以能影响RM的启动,是因为集群默认开启了ResourceManager Restart功能。
ResourceManager Restart
社区对RM重启功能的完善分为两个阶段:
1. 第一阶段(Non-work-preserving RM restart)
当客户端提交application时,RM会将App的元数据信息(ApplicationSubmissionContext)保存在HDFS中(非HA)或者ZooKeeper(HA集群),同时也会在应用结束时保存应用的最终状态比如完成态(failed, killed, finished)和诊断信息。此外,RM还可以将security keys, tokens保存起来。
RM重新启动时,它可以从HDFS或者ZooKooper读取这些App的状态信息。RM会对完成状态的APP(failed, killed, finished)和仍在运行中的APP对区分处理:
(1)对于完成态的App,RM仅仅是将保存的信息重新加载到内存中。这么做的典型场景就是在RM重启后,yarn的WEB UI仍然能看到历史的记录任务。
(2)对于运行中的App,RM会将该kill掉APP的ApplicationMaster和container,重新提交该App任务。RM掉线时,NodeManager会一直轮询RM,直到该进程上线。RM复活后会给所有仍在运行的ApplicationMaster,NodeManager发送re-sync命令, 接收到该命令后container和AM会被kill掉,而RM会将会根据保存的App信息,将该App重新启动。
阶段1是在Hadoop2.4.0实现,该阶段的主要问题是:一旦RM重启,所有正在运行中的任务将重新开始跑,对于耗时久的任务来说,这种行为是不可接受的。
2. 第二阶段(Work-preserving RM restart)
阶段2是在Hadoop2.6.0才开始实现的。主要功能就是在阶段1的基础上增加:RM重启后仍保证运行状态的App继续执行,App可以简单地重新与RM同步,并从停止的地方恢复。
当NM与重新启动的RM进行同步时,NM不会kill掉container ,而且会将container的状态发送给RM。 RM通过这些container的信息来重建container和对应App的调度状态。与此同时,AM需要将未完成的资源请求重新发送给RM,因为RM在关闭时可能会丢失这些未完成的请求。
ResourceManager Restart Configurations
开启RM重启功能,默认值false:
代码语言:javascript复制<property>
<description>Enable RM to recover state after starting. If true, then
yarn.resourcemanager.store.class must be specified. </description>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>false</value>
</property>
配置state-store:
代码语言:javascript复制<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore</value>
</property>
该参数属性主要使用两类:
(1)org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore,HA集群配置该存储类。
(2)org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore,非HA集群配置,将状态信息存储在HDFS。
其他参数可参考官网。
回归到文章开头的问题,在非HA集群中,RM会将App的状态信息存储在${hadoop.tmp.dir}/yarn/system/rmstore路径下,在EMR中该路径就是/data/emr/hdfs/tmp/yarn/system/rmstore/。由于该路径下的/data/emr/hdfs/tmp/yarn/system/rmstore/FSRMStateRoot/EpochNode的块丢失,导致了重启失败。
如下代码,重启时要从这些路径去读取App状态。
代码语言:javascript复制 protected void serviceStart() throws Exception {
RMStateStore rmStore = rmContext.getStateStore();
// The state store needs to start irrespective of recoveryEnabled as apps
// need events to move to further states.
rmStore.start();
if(recoveryEnabled) {
try {
LOG.info("Recovery started");
rmStore.checkVersion();
if (rmContext.isWorkPreservingRecoveryEnabled()) {
读取 /data/emr/hdfs/tmp/yarn/system/rmstore/FSRMStateRoot/EpochNode
rmContext.setEpoch(rmStore.getAndIncrementEpoch());
}
读取/data/emr/hdfs/tmp/yarn/system/rmstore/FSRMStateRoot/RMAppRoot
RMState state = rmStore.loadState();
recover(state);
LOG.info("Recovery ended");
} catch (Exception e) {
// the Exception from loadState() needs to be handled for
// HA and we need to give up master status if we got fenced
LOG.error("Failed to load/recover state", e);
throw e;
}
}
super.serviceStart();
}
解决方案:
删除这些坏块,RM就能重新启动。HDFS中产生丢失块的大部分原因是没有使用HDFS的命令删除文件,因此为了避免丢失块请使用HDFS的删除命令。