虽然1.2.1版本也已经出来了,估计还是有很多人在用1.1.0或者1.0.0 版本。所以把编译和使用1.1.0版本时遇到的一些问题和解决思路写在这里,供参考。
因为我们对cdh版本的hadoop做了一些生产环境相关的修改,所以每次升级spark都需要基于源码自己进行编译。编译方法很简单,而且我在这篇文章 http://blog.csdn.net/amber_amber/article/details/41041787 也写过了,所以就不说了。
其实不管任何技术的学习,都是个逐步上手的过程。一步一步来,越来越熟悉的时候,你遇到的问题也就越来越少,遇到问题也知道应该从何处入手了。所以现在我们的环境已经升级到spark 1.2.1版本了,遇到的问题却显然没有当时钻研1.1.0时那么多了,而且也顺手了很多。
=========================================================
1. 重新编译spark(spark1.1.0-hadoop2.3.0)后,从client提交job,无法成功提交,报错如下:
- <span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;"> 14/12/24 10:58:39 INFO ConfiguredRMFailoverProxyProvider: Failing over to rm2
- 14/12/24 10:59:18 INFO ConfiguredRMFailoverProxyProvider: Failing over to rm1
- 14/12/24 10:59:18 WARN RetryInvocationHandler: Exception while invoking class org.apache.hadoop.yarn.api.impl.pb.client.ApplicationClientProtocolPBClientImpl.getClusterMetrics. Not retrying because failovers (30) exceeded maximum allowed (30)
- java.net.ConnectException: Call From sjs/x.x.x.x to 0.0.0.0:8032 failed on connection exception: java.net.ConnectException: Connection refused; For more details see: http://wiki.apache.org/hadoop/ConnectionRefused
- at sun.reflect.GeneratedConstructorAccessor3.newInstance(Unknown Source)
- at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
- at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
- at org.apache.hadoop.net.NetUtils.wrapWithMessage(NetUtils.java:783)
- at org.apache.hadoop.net.NetUtils.wrapException(NetUtils.java:730)
- at org.apache.hadoop.ipc.Client.call(Client.java:1413)
- at org.apache.hadoop.ipc.Client.call(Client.java:1362)
- at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:206)
- at com.sun.proxy.$Proxy7.getClusterMetrics(Unknown Source)
- at org.apache.hadoop.yarn.api.impl.pb.client.ApplicationClientProtocolPBClientImpl.getClusterMetrics(ApplicationClientProtocolPBClientImpl.java:152)
- at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:606)
- at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:186)
- at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)
- at com.sun.proxy.$Proxy8.getClusterMetrics(Unknown Source)
- at org.apache.hadoop.yarn.client.api.impl.YarnClientImpl.getYarnClusterMetrics(YarnClientImpl.java:294)
- at org.apache.spark.deploy.yarn.Client.logClusterResourceDetails(Client.scala:101)
- at org.apache.spark.deploy.yarn.Client.runApp(Client.scala:60)
- at org.apache.spark.deploy.yarn.Client.run(Client.scala:96)
- at org.apache.spark.deploy.yarn.Client$.main(Client.scala:182)
- at org.apache.spark.deploy.yarn.Client.main(Client.scala)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:606)
- at org.apache.spark.deploy.SparkSubmit$.launch(SparkSubmit.scala:328)
- at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:75)
- at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
- Caused by: java.net.ConnectException: Connection refused
- at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
- at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:739)
- at org.apache.hadoop.net.SocketIOWithTimeout.connect(SocketIOWithTimeout.java:206)
- at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:529)
- at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:493)
- at org.apache.hadoop.ipc.Client$Connection.setupConnection(Client.java:604)
- at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:699)
- at org.apache.hadoop.ipc.Client$Connection.access$2800(Client.java:367)
- at org.apache.hadoop.ipc.Client.getConnection(Client.java:1461)
- at org.apache.hadoop.ipc.Client.call(Client.java:1380)
- ... 23 more
- Call From sjs/x.x.x.x to 0.0.0.0:8032 failed on connection exception: java.net.ConnectException: Connection refused; For more details see: http://wiki.apache.org/hadoop/ConnectionRefused</span></span>
【SOLUTION】
这是因为无法获取到resourcemanager.namenode的缘故,所以客户端试图连接的是0.0.0.0作为rm,所以连接失败。如果出现这个问题,可能是由于yarn本身的bug造成的,因为我改用spark1.1.0-cdh5.1.0后就不再出现这个问题。另外,看到一篇文章中介绍说在spark-env.sh中配置spark-yarn的环境变量,如果改用cdh没有奏效的话也可以试试这个方法:
export SPARK_YARN_USER_ENV="CLASSPATH=/search/hadoop/etc/hadoop"
2. spark on yarn-client模式提交作业,从client端连接时报错如下:
- <span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;"> 15/01/07 10:24:03 INFO util.Utils: Successfully started service 'sparkYarnAM' on port 43505.
- 15/01/07 10:24:04 INFO yarn.ExecutorLauncher: Registering the ApplicationMaster with appUIAddress: sjs:4040
- 15/01/07 10:24:04 INFO yarn.ExecutorLauncher: Waiting for Spark driver to be reachable.
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:04 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...
- 15/01/07 10:24:05 ERROR yarn.ExecutorLauncher: Failed to connect to driver at sjs:40900, retrying ...</span></span>
【SOLUTION】
这是因为yarn-client模式中,driver进程是在客户端启动的。而客户端机器一般是一些虚拟机,hostname是简单的一些名字(类似my_machine这样的),其他服务器无法用这个hostname准确连接客户端机器。所以yarn集群无法正确解析driver机器的域名,无法正确连接,所以报错。解决方法: spark中给出了对driver的DNS或者IP配置的一个配置变量,在提交作业之前执行如下命令:
export SPARK_JAVA_OPTS="-Dspark.driver.host=x.x.x.x"
** 注意,这个系统属性不可以永久性地配置在spark-default.conf里,因为在yarn-cluster模式中,driver是运行在yarn集群的AM中的,这个指定会强制将driver的hostname指向所配置的域名,从而导致yarn-cluster模式执行报错。
update: 最近使用1.2.1时又出现这个问题,想着改源码来永久性解决这个问题。然后在源码中发现一个参数配置 SPARK_LOCAL_HOSTNAME,获取hostname时会首先去取这个环境配置。如果client的hostname不可用的,可以在spark-env.sh中把这个参数配置成ip地址,然后client端的任务提交,不管是yarn-cluster还是yarn-client都没有问题。但不知道为什么,这个配置貌似没有在官网中给出,不读源码还真发现不了啊。
3. spark on yarn-client模式,作业实际执行失败了的,console显示的失败原因是第一个stage中的一个task尝试失败次数超过4次所以整个application退出。但是在yarn中却显示这个作业是执行成功了的。
【SOLUTION】目前还没有解决,看看1.2.1版本是不是解决了这个问题。
4. spark on yarn的UI界面无法访问,报错如下:
- <span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;"> HTTP ERROR 500
- Problem accessing /proxy/application_1420532637225_0054/. Reason:
- Connection refused
- Caused by:
- java.net.ConnectException: Connection refused
- at java.net.PlainSocketImpl.socketConnect(Native Method)
- at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
- at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
- at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
- at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
- at java.net.Socket.connect(Socket.java:579)
- at java.net.Socket.connect(Socket.java:528)
- at java.net.Socket.<init>(Socket.java:425)
- at java.net.Socket.<init>(Socket.java:280)
- at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
- at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
- at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
- at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
- at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
- at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
- at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:346)
- at org.apache.hadoop.yarn.server.webproxy.WebAppProxyServlet.proxyLink(WebAppProxyServlet.java:186)
- at org.apache.hadoop.yarn.server.webproxy.WebAppProxyServlet.doGet(WebAppProxyServlet.java:338)
- at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
- at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
- at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
- at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1221)
- at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:66)
- at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:900)
- at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:834)
- at org.apache.hadoop.yarn.server.resourcemanager.webapp.RMWebAppFilter.doFilter(RMWebAppFilter.java:84)
- at com.sun.jersey.spi.container.servlet.ServletContainer.doFilter(ServletContainer.java:795)
- at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
- at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
- at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
- at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113)
- at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
- at org.apache.hadoop.http.HttpServer2$QuotingInputFilter.doFilter(HttpServer2.java:1183)
- at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
- at org.apache.hadoop.http.NoCacheFilter.doFilter(NoCacheFilter.java:45)
- at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
- at org.apache.hadoop.http.NoCacheFilter.doFilter(NoCacheFilter.java:45)
- at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
- at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:399)
- at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
- at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
- at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
- at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
- at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
- at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
- at org.mortbay.jetty.Server.handle(Server.java:326)
- at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
- at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
- at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
- at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
- at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
- at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
- at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)</span></span>
【SOLUTION】
首先,发现1.0.2中不存在这个问题,1.1.0里存在,而我的1.0.2是基于hadoop2.2.0编译的,1.1.0是基于cdh5.1.0编译的,所以第一步需要定位是hadoop的问题还是spark的问题。所以又编译了1.1.0-hadoop2.2.0发现是可以正常访问spark UI的,所以问题是和cdh5.1.0有关。
接下来,看看是不是protobuf的问题,但是对比两个jar包中的文件,以及编译过程中的输出,发现on hadoop2.2.0和on cdh5.1.0的编译中,使用的protobuf版本是完全一致的,所以排除protobuf的问题。
然后查看sparkUI的源码,在源码中加print打印,一步步查看sparkUI的启动过程,发现SPARKUI的实际启动地址是类似http://slave machine:37483这样的URL。然后访问这个URL,发现在on hadoop2.2.0的版本上,这个URL是可以正常访问的,但是在on cdh5.1.0版本中,这个URL不能正常访问,报错和上面的一致。
进一步推测问题可能是spark UI没有正常启动。继续看sparkUI的源码和启动过程,以及把两个版本的job执行的日志进行对照,并没有发现异常,也就是说sparkUI没有抛出异常,也就是很有可能已经正常启动了。
那么就很有可能是访问权限的问题了。
所以接下来看了看spark的安全权限控制,并试着用各种方法配置了下spark的web访问,发现都没有用.spark.ui.filters这个参数的值更是已经被写死在代码里了,也不是官方文档中介绍的默认为none,给databricks跪了。。。。。
然后发现,这时候只剩下一个proxy没有啃了,而hadoop为了防攻击,web端的访问都是通过proxy的,也就是说我用http://slave machine:37483这个URL访问sparkUI,也会经过proxy跳转。所以开始看proxy。果然发现,在返回HTTP 500错误的时候,hadoop的日志中会报connection refused的错误。继续看proxy相关的文档和介绍,官方介绍说,proxy默认是作为RM的一个子进程启动的,除非通过hadoop.web-proxy.address单独配置并启动。为了方便排查,我就修改了集群的proxy配置,把proxy配置成了独立启动的服务。然后,就这样,这个问题就解决了。。。。
最终推测,应该还是要总结为spark和yarn的交互的问题吧,应该是proxyserver作为yarn的一个组件,和作为单独的server启动,运行机制不同,而spark很有可能是更支持作为一个单独server的proxy代理的。这只是推测,后续进一步看看proxy server相关的源码和运行机制。
最终solution: 把proxy独立成proxy server进行提供(hadoop.web-proxy.address),然后对spark做一些限制,关闭sparkUI能kill application的功能(spark.ui.killEnabled)。
http://blog.csdn.net/amber_amber/article/details/44019725