为一个大客户启用Node Label功能,发现启用后,使用用户提交任务后,appmaster无法启动,导致了整个任务无法启动,集群无法提交任务。
集群的版本是Hadoop 2.7.2,Node Label 把整个集群分为两部分,一部分label为core,一部分label为vvp。在客户端提交任务的时候可以指定队列,这种情况下队列信息是在提交的context里面设置,而对于不显示的指定队列的应用,设置的是默认队列,即default,这段代码可以参考YARN Runner的实现。
appContext.setApplicationId(applicationId); // ApplicationId
appContext.setQueue( // Queue name
jobConf.get(JobContext.QUEUE_NAME,
YarnConfiguration.DEFAULT_QUEUE_NAME));
ResourceManager通过RMClientService接收到提交的请求后,会放置的状态机中,通过多次的异步处理,会生成RMAppAttempt对象,并生成AppMaster的request,相关代码在RMAppManager类中。
方法是validateAndCreateResourceRequest,在这里面提交的appmaster依然使用的是default队列,并没有通过queue mapping获取到他的实际映射队列,导致了无法获取label信息,从而无法调度。
SchedulerUtils.normalizeAndValidateRequest(amReq,
scheduler.getMaximumResourceCapability(),
submissionContext.getQueue(), scheduler, isRecovery, rmContext);
在这段代码中,submissionContext的Queue依然是default,获取的是default对应的label,通常是默认的partition,而如果用户和组映射到了其他的Queue,而这个Queue需要到特定的Label运行,就会导致错误。
解决方法:
String queueName = submissionContext.getQueue();
if(scheduler instanceof CapacityScheduler) {
queueName = ((CapacityScheduler)scheduler).getQueueMappings(queueName, user);
}
SchedulerUtils.normalizeAndValidateRequest(amReq,
scheduler.getMaximumResourceCapability(),
queueName, scheduler, isRecovery, rmContext);
在Capacity Scheduler中暴露出queue mapping的方法去获取mapping的queue,从而获取到实际的Label。
在Hadoop 2.8.5中这个问题已经被fix了,fix的方法如下:
if (rmContext.getQueuePlacementManager() != null) {
// We only do queue mapping when it's a new application
rmContext.getQueuePlacementManager().placeApplication(
submissionContext, user);
}
在生成amReq之前,先根据QueueMapping重新设置queue的Name,从而获取到映射后的Queue,拿到最后的Label。
由于2.8.5这个改动远远大于2.7.3的改动,所以这次采用自己的fix方案解决这一问题。