flink on yarn的一则jar冲突问题,你遇到过没?

2021-08-18 10:49:07 浏览数 (1)

有人说,没遇到过jar包冲突问题都不好意思说自己用过hadoop。那么,你遇到过没?最近使用flink on yarn提交任务时遇到过一则jar冲突问题,整个分析过程还挺有意思的,记录一下。

背景

近期准备对实时计算平台进行升级,调研阶段使用yarn client手动向yarn集群上提交flink任务时出现了一个小插曲。提交任务时,一直提示失败,来yarn的web控制台发现日志有报错信息,错误如下:

代码语言:javascript复制
Caused by: org.apache.flink.runtime.resourcemanager.exceptions.ResourceManagerException: Cannot initialize resource provider.
    at org.apache.flink.runtime.resourcemanager.active.ActiveResourceManager.initialize(ActiveResourceManager.java:124)
    at org.apache.flink.runtime.resourcemanager.ResourceManager.startResourceManagerServices(ResourceManager.java:245)
    at org.apache.flink.runtime.resourcemanager.ResourceManager.onStart(ResourceManager.java:229)
    ... 20 more
Caused by: org.apache.hadoop.yarn.exceptions.YarnRuntimeException: yarn.client.max-nodemanagers-proxies (0) can not be less than 1.
    at org.apache.hadoop.yarn.client.api.impl.ContainerManagementProtocolProxy.<init>(ContainerManagementProtocolProxy.java:74)
    at org.apache.hadoop.yarn.client.api.impl.NMClientImpl.serviceInit(NMClientImpl.java:136)
    at org.apache.hadoop.service.AbstractService.init(AbstractService.java:163)
    at org.apache.hadoop.yarn.client.api.async.impl.NMClientAsyncImpl.serviceInit(NMClientAsyncImpl.java:109)
    at org.apache.hadoop.service.AbstractService.init(AbstractService.java:163)
    at org.apache.flink.yarn.YarnResourceManagerDriver.initializeInternal(YarnResourceManagerDriver.java:186)
    at org.apache.flink.runtime.resourcemanager.active.AbstractResourceManagerDriver.initialize(AbstractResourceManagerDriver.java:81)
    at org.apache.flink.runtime.resourcemanager.active.ActiveResourceManager.initialize(ActiveResourceManager.java:122)
    ... 22 more

从报错信息中能清晰地看到,这是一个yarn参数的配置问题。要求yarn.client.max-nodemanagers-proxies的值不能小于1,而它的实际传入值为0,于是yarn Client在进行任务提交时初始化resource manager时抛出了异常。它的实际代码位置位于org.apache.hadoop.yarn.client.api.impl.ContainerManagementProtocolProxy中,查看了一下当前版本的ContainerManagementProtocolProxy中具体代码逻辑如下:

hadoop-yarn-client-2.4.1是flink-yarn的默认依赖,在这里会进行yarn.client.max-nodemanagers-proxies的配置检查,这个默值的值不能小于1,否则会抛出异常。如果没有手动配置过这个值,这里会使用500作为默认值。显然,我们没有对这个参数进行手动配置,那么为什么没有用500作为默认值呢?下面我们来分析一下。

分析

首先在实时计算平台使用yarn client进行任务提交时从来没有出现过这个异常,但是在这里使用yarn client手动提交时却出现了异常,这是什么原因呢?

首先想到的就是hadoop的版本依赖问题,从经验上来说这是最容易出问题的地方。

检查依赖版本

1.首先来查看实时计算平台上hadoop-yarn-client的依赖版本,查看后发现版本也是2.4.1,并无差异。2.由于两个地方都没有对yarn.client.max-nodemanagers-proxies进行手动配置,而上面从YarnConfiguration中获取yarn.client.max-nodemanagers-proxies的值时取到的不为空,导致默认填充值500没有生效,那么会不会是两个地方使用的YarnConfiguration中获取的值不同呢?于是添加日志打印输出(此处忽略这个过程),发现也并无差异。3.先是有点不太能理解了,转念一想,是不是使用了不同版本的YarnConfiguration,不同版本的有不同实现呢?查看一遍,果然如此。实时计算平台上使用的是2.4.1版本的YarnConfiguration,手动提交时使用的是2.7.4版本的YarnConfiguration。

具体原因分析

YarnConfiguration#getInt方法实际上继承自父类org.apache.hadoop.conf.Configuration#getInt方法,我们直接来看org.apache.hadoop.conf.Configuration#getInt方法的具体实现,代码如下:

代码语言:javascript复制
  public int getInt(String name, int defaultValue) {
    String valueString = getTrimmed(name);
    if (valueString == null)
      return defaultValue;
    String hexString = getHexDigits(valueString);
    if (hexString != null) {
      return Integer.parseInt(hexString, 16);
    }
    return Integer.parseInt(valueString);
  }

这个方法的大致逻辑是先去配置上下文中获取指定的key对应的配置值,如果没有配置的话则返回默认值。简单一看,好像并无大问题。我们进入到getTrimmed方法来看:

代码语言:javascript复制
  public String getTrimmed(String name) {
    String value = get(name);
    if (null == value) {
      return null;
    } else {
      return value.trim();
    }
  }

这个方法的注释简单翻译一下,大概是这样子的:该方法用于获取name属性对应的值,如果不存在该属性则为空。如果该key已弃用,它将返回取代已弃用的key所对应的那个key所对应的值。这个逻辑要进入到get(name)方法来看才比较清晰:

代码语言:javascript复制
  public String get(String name) {
    String[] names = handleDeprecation(deprecationContext.get(), name);
    String result = null;
    for(String n : names) {
      result = substituteVars(getProps().getProperty(n));
    }
    return result;
  }

在handleDeprecation方法内部会先查找当前需要查找的key是不是在被废弃上下文列表中,如果不在列表中但直接返回原始key值;如果在废弃列表中,则返回代替废弃key的新key。

这也就是说,如果我们查看一下yarn.client.max-nodemanagers-proxies参数在2.4.1和2.7.4两个版本中的差异就能找到为啥都没有配置该参数的情况下,一个会为空进而使用默认值,另一个不为空而没有使用默认值的原因了。

版本差异分析

我们先来看一下org.apache.hadoop.conf.Configuration#deprecationContext属性值:

它默认初始化的是defaultDeprecations数组中的一些废弃参数与新的替代参数的映射,另外其他地方可以调用addDeprecations方法来添加新的映射值。

我们直接来看2.7.4版本的YarnConfiguration:

可以清楚地看到,它会向defaultDeprecations数组中添加一组新的映射值,即废弃掉yarn.client.max-nodemanagers-proxies,用yarn.client.max-cached-nodemanagers-proxies代替,而且对新参数的默认值也有设置:

也就是说在都没有配置yarn.client.max-nodemanagers-proxies时,使用2.7.4版本的YarnConfiguration来获取yarn.client.max-nodemanagers-proxies值会返回yarn.client.max-cached-nodemanagers-proxies的值0;而使用2.4.1版本的YarnConfiguration时返回为null会在2.4.1版本的ContainerManagementProtocolProxy中使用默认值500。

当然在2.7.4版本的ContainerManagementProtocolProxy中已经做了新老key值的逻辑替换:

总结

归其原因是因为手动提交时使用的hadoop依赖不一致,使用了2.4.1版本的hadoop-yarn-api(ContainerManagementProtocolProxy依赖)却使用了2.7.4版本的hadoop-common(YarnConfiguration在这个依赖包中)。2.7.4为较新的版本,其YarnConfiguration中将yarn.client.max-nodemanagers-proxies纳入了废弃参数,并会用yarn.client.max-cached-nodemanagers-proxies(0)代替,而在2.4.1版本的ContainerManagementProtocolProxy中去获取该参数时就会取到0,从而触发异常。

解决办法也很简单,全部用2.4.1版本的hadoop依赖或者全部用2.7.4版本的依赖。

0 人点赞