被这个参数三杀了
最近接连排查了几个问题,居然都是同一个参数引起的,本文就通过实际案例讲述下该参数如何引发问题的,以及问题最终又是如何解决的~
【First Blood】
在我们的环境中,RM是基于HA的方式部署的,并且RM是基于容器的方式运行的,即两个RM运行在各自的容器中;同时,我们还开启了kerberos认证,因此两个RM的hostname配置的是域名。主要的配置信息如下所示:
<property> <name>yarn.resource.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.ha.automatic-failover.enable</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>rm-0.svc.cluster.local</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>rm-1.svc.cluster.local</value> </property>
在一次测试过程中,RM的其中一个(容器所在)节点异常宕机了,此后向RM提交了一个任务,但该任务的AM启动后就失败了,报错信息为:
java.lang.IllegalArgumentException: java.net.UnknownHostException: hadoop-resourcemanager-0.hadoop-resourcemanager.hncscwc-198.svc.cluster.local1 at org.apache.hadoop.security.SecurityUtil.buildTokenService(SecurityUtil.java:418) at org.apache.hadoop.yarn.client.ClientRMProxy.getTokenService(ClientRMProxy.java:153) at org.apache.hadoop.yarn.client.ClientRMProxy.getAMRMTokenService(ClientRMProxy.java:138) at org.apache.hadoop.yarn.client.ClientRMProxy.setAMRMTokenService(ClientRMProxy.java:80) at org.apache.hadoop.yarn.client.ClientRMProxy.getRMAddress(ClientRMProxy.java:99) at org.apache.hadoop.yarn.client.ConfiguredRMFailoverProxyProvider.getProxyInternal(ConfiguredRMFailoverProxyProvider.java:76) at org.apache.hadoop.yarn.client.ConfiguredRMFailoverProxyProvider.getProxy(ConfiguredRMFailoverProxyProvider.java:90) at org.apache.hadoop.io.retry.RetryInvocationHandler$ProxyDescriptor.<init>(RetryInvocationHandler.java:197) at org.apache.hadoop.io.retry.RetryInvocationHandler.<init>(RetryInvocationHandler.java:317) at org.apache.hadoop.io.retry.RetryInvocationHandler.<init>(RetryInvocationHandler.java:311) at org.apache.hadoop.io.retry.RetryProxy.create(RetryProxy.java:59) at org.apache.hadoop.yarn.client.RMProxy.createRMProxy(RMProxy.java:120) at org.apache.hadoop.yarn.client.RMProxy.createRMProxy(RMProxy.java:93) at org.apache.hadoop.yarn.client.ClientRMProxy.createRMProxy(ClientRMProxy.java:72) at org.apache.hadoop.mapreduce.v2.app.rm.RMCommunicator.createSchedulerProxy(RMCommunicator.java:311) at org.apache.hadoop.mapreduce.v2.app.rm.RMCommunicator.serviceStart(RMCommunicator.java:117) at org.apache.hadoop.mapreduce.v2.app.rm.RMContainerAllocator.serviceStart(RMContainerAllocator.java:263)
RM配置了高可用,其中一个宕机,另外一个RM也确实提升为Active了,任务也能正确提交和调度,但为什么运行就报错了呢?
顺着报错的堆栈信息,走读相关的代码,我们发现了问题的所在。
在《YARN任务运行中的token》中提到了yarn任务的AM在启动后,会从指定的文件中加载AMRMToken,而rm的客户端在初始化时需要给token设置服务端的地址,也就是rm的地址。
关键代码如下所示:
在buildTokenService中,判断如果必须使用IP(userIpForTokenService),则会对rm的域名进行解析,如果无法解析出具体的ip地址,则抛出异常;异常会逐层往上抛,最终导致程序退出。
结合实际情况来分析,由于其中一个rm出现了宕机,其域名确实无法解析出对应的ip来,因此这也就是导致任务失败的根本原因。
至于useIpForTokenService的值,是由配置项hadoop.security.token.service.use_ip来决定的,默认为true,即tokenService需要使用ip,而不是域名。
最终,将该配置参数设置为false后,再次测试验证,在同样的场景下,任务可以正确提交和运行。
【Double Kill】
在上面问题解决后的第二天,重新部署环境时,发现jobHistoryServer由于无法正确进行kerberos认证,导致启动失败,具体报错信息为:
org.apache.hadoop.yarn.exceptions.YarnRuntimeException: History Server Failed to login at org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer.serviceInit(JobHistoryServer.java:130) at org.apache.hadoop.service.AbstractService.init(AbstractService.java:163) at org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer.launchJobHistoryServer(JobHistoryServer.java:231) at org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer.main(JobHistoryServer.java:241) Caused by: java.io.IOException: Login failure for hadoop/172.16.20.18@BIGDATA.COM from keytab /home/hncscwc/hadoop/etc/hadoop/hdfs.keytab: javax.security.auth.login.LoginException: Unable to obtain password from user at org.apache.hadoop.security.UserGroupInformation.loginUserFromKeytab(UserGroupInformation.java:1144) at org.apache.hadoop.security.SecurityUtil.login(SecurityUtil.java:286) at org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer.doSecureLogin(JobHistoryServer.java:183) at org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer.serviceInit(JobHistoryServer.java:128) ... 3 more Caused by: javax.security.auth.login.LoginException: Unable to obtain password from user at com.sun.security.auth.module.Krb5LoginModule.promptForPass(Krb5LoginModule.java:901) at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:764) at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:618) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at javax.security.auth.login.LoginContext.invoke(LoginContext.java:755) at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195) at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682) at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680) at javax.security.auth.login.LoginContext.login(LoginContext.java:587) at org.apache.hadoop.security.UserGroupInformation$HadoopLoginContext.login(UserGroupInformation.java:522) at org.apache.hadoop.security.UserGroupInformation.loginUserFromKeytab(UserGroupInformation.java:1135) ... 6 more
问题出现后,先对jobHistoryServer的配置文件进行了确认,发现与之前是一样的,并没有什么不对的地方,关键配置项如下所示:
<property> <name>mapreduce.jobhistory.address</name> <value>172.16.20.18</value> </property> <property> <name>mapreduce.jobhistory.principal</name> <value>hadoop/_HOST@BIGDATA.COM</value> </property>
jobHistoryServer启动后进行kerberos登陆时,会对principal中的_HOST进行替换,替换内容为mapreduce.jobhistory.address的值,即最终以ip形式进行了替换,导致认证失败。
正常来说,向kdc登陆认证的principal应该是包含服务的主机名,而不是ip地址,并且之前在这种配置下也都没有任何问题,怎么突然就不正常了?
还是结合源码进行分析,找到了问题所在:
配置项hadoop.security.token.service.use_ip配置为true或false时,内部会产生不同的主机地址解析对象:
-
设置为true时为StandardHostResolver
-
设置为false时为QualifiedHostResolver
两者对于host地址为ipv4的解析有所不同
对于StandardHostResolver:
对于QualifiedHostResolver:
也就是说,StandardHostResolver可以通过getByName正确解析出ip对应的主机名,而后者直接将ip返回。
之前hadoop.security.token.service.use_ip配置为true,因此配置项mapreduce.jobhistory.address即便配置为ip,也能正确解析出对应的主机名,然后在principal替换_HOST时,也是正确的。
修改上面的问题后,将配置的值改为了false,就导致了该问题的出现。
最后,我们通过将配置项mapreduce.jobhistory.address的值修改为主机名解决了该问题。
【Triple Kill】
没有问题的日志维持了两三天,再次遇到问题,这次的现象是在sparkHistory节点上向hdfs上传文件失败。
首先,我们先在其它节点上进行了同样的操作,发现向hdfs上传文件是没有任何问题的,同时查看nn/dn的进程情况,结合对应日志确认nn/dn都是正常的。
接着,我们重新回到该节点上,检查了hdfs-site.xml中的相关配置项,没有发现异常的地方,然后向nn/dn的节点执行了ping操作,确认网络也没有问题,但发现hdfs相关的所有操作都失败。
最后,通过tcpdump进行了抓包分析,发现与nn建立连接时,tcp的源端地址为127.0.0.1,这导致syn发送后,根本得不到sync ack应答。而正常情况下,源ip应该是该节点自身的ip。
那么这里为什么会是127.0.0.1,是谁指定的127.0.0.1?
继续通过strace来分析,发现socket创建后,主动进行了一次bind源地址的操作,并且绑定的源端地址就是127.0.0.1。
进一步调整日志为TRACE后,发现是在hdfs的客户端进行了bind的操作。
22/04/19 17:10:50 TRACE ipc.ProtobufRpcEngine: 1: Call -> namenode.svc.cluster.local/172.16.22.234:9000: getFileInfo {src: "/"} 22/04/19 17:10:50 DEBUG ipc.Client: The ping interval is 60000 ms. 22/04/19 17:10:50 DEBUG ipc.Client: Connecting to namenode.svc.cluster.local/172.16.22.234/9000 22/04/19 17:10:50 TRACE secruity.SecruityUtil: Name lookup for spark-history.svc.cluster.local took 3ms. 22/04/19 17:10:50 DEBUG ipc.Client: Binding hadoop/spark-history.svc.cluster.local@BIGDATA.COM to spark-history.svc.cluster.local.localdomain/127.0.0.1
既然知道是hdfs客户端中对socket进行了源IP的绑定动作,那么就结合源码梳理下hdfs客户端向nn建立连接的逻辑:
在开启kerberos认证的场景中,客户端向nn建立连接的流程包括:创建socket,然后从ticket中解析出bind地址并进行bind操作,最后进行连接。
从ticket中解析出本地bind地址的具体步骤又分为:
-
从ticket中获取principal
-
从principal中获取主机名
-
如果主机名为空,则不进行bind操作
-
如果主机名非空,对主机名进行解析,如果解析后的地址非空,则进行bind操作。
对于主机名解析又分为两种情况
如果配置项"hadoop.security.token.service.use_ip"的值为true,则直接获取主机名对应的ip,如果为false,则继续按下面的逻辑解析(其本意是想要获取主机名对应的完全合规域名)
-
如果主机名为ipv4,通过ip地址获取对应的全域名
-
如果主机名以"."结尾,直接获取主机名对应的全域名
-
如果主机名包含".",先在主机名末尾加上".",并继续上一步的逻辑解析,如果解析出的域名为空,则在主机名末尾依次添加"/etc/resolve.conf"中的"search"指定的域,进行主机名的解析
以实际情况来分析:
-
sparkHistory进程kerberos登陆使用的principal为"hadoop/spark-history.svc.cluster.local@BIGDATA.COM"
-
从pincipal中解析出主机名为"spark-history.svc.cluster.local"
-
配置项"hadoop.security.token.service.use_ip"的值为false,因此进入全域名的解析流程。主机名不是完全合规(即不是以"."结尾),但又包含了".",因此先在末尾加上".",使其成为完全合规域名,并按照该域名来解析。
-
而由于sparkHistory所在的容器,配置了就绪探针,容器未就绪时,无法解析出任何地址。因此继续在"spark-history.svc.cluster.local."后再加上"/etc/resolve.conf"中search指定的域,便利进行全域名解析,如果其中任意一个能解析出地址,则退出循环
-
该节点中"/etc/resolve.conf"中的search中仅有一个localdomain,因此以"spark-history.svc.cluster.local.localdomain"来解析,解析出的ip恰好就是127.0.0.1,导致了问题的出现
该节点的/etc/resolve.conf文件中之所以只有"search localdomain",怀疑是人为进行了修改导致的。最后,修改该文件,问题得以解决。
【总结】
通过这三个问题分析定位解决,对"hadoop.security.token.service.use_ip"有了较深入的理解,同时也深刻领会源码是不会骗人的。
好了,这就是本文的全部内容,如果觉得本文对您有帮助,不要吝啬点赞在看转发,也欢迎加我微信交流~
本文分享自微信公众号 - hncscwc(gh_383bc7486c1a)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
《Mybatis 手撸专栏》第11章:流程解耦,封装结果集处理器
持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、目标 三、设计 四、实现 1. 工程结构 2. 出参参数处理 3. 查询结果封装 五、测试 1. 事先准备 2. 单元测试 六、总结 一、前言 码农,如何为自己的职业生涯续期? 上班就像打怪升级,拿着一把西瓜刀,从南天门砍到北天门。但时间长了,怪越来越凶了,西瓜刀也不得手了。咋办,在游戏里大家肯定是想办法换装备了、买武器了、学技能了,这样才能有机会打通更多的关卡。 其实我们作为程序员上班也是一样的,如果一直都以为这点技术够写写CRUD就够了,反正现在还能应付的了。但3年后呢、5年后呢,总有一天你的技术根本没法满足公司对你现阶段的要求,最简单的CRUD也早已交给了曾经年轻的另外的你。 有人说:“程序员不是技术牛就能一直行!” 但其实技术牛就是行,当你牛到一定的阶段,解决别人解决不了的问题,处理别人处理的不了的方案,蝎子粑粑独一份,谁又能拦得住你呢。在哪里工作都是你自己来定的,你只管技术牛,就能横着走。 二、目标 延续着...
- 下一篇
基于模板配置的数据可视化平台
导读:在大数据智能时代,数据分析的价值越来越重要,而数据分析可视化平台的能力要求也越来越高。本文从百度数据中心的数据可视化平台出发,介绍了配置化的数据可视化平台的应用价值,并对数据可视化平台的整体处理架构进行了拆解。基于可配置的数据可视化平台,可以高效支持复杂的数据分析场景,提升分析效率,强化数据的价值。 全文6999字,预计阅读时间13分钟。 一、背景与目标 1.1 背景分析 在数据智能时代,BI(Business Intelligence,商业智能)已经是现代数据运营的基础能力。以数据支撑业务,一方面需要我们基于数据仓库建设业务相关的全面、权威、稳定的基础数据,另一方面也需要我们建立数据分析的能力,通过图表对数据进行展示,从而提高数据展示的信息密度,然后辅以各种对比方式,让数据业务化、升华数据意义,赋能业务增长。 因此一个数据可视化平台的搭建就变得尤为重要,但由于复杂业务之间的差异使得平台的设计及开发过程难度大且复杂。考虑到人群、业务的分析习惯与业务各异的分析方法,数据可视化平台在报表的呈现、数据的计算、数据的发布效率等方面都面临着重大的挑战。 1.2 问题与目标 以一个典型的广告...
相关文章
文章评论
共有0条评论来说两句吧...