问题产生
Spark集群,即可以基于Mesos或YARN来部署,也可以用自带的集群管理器,部署于standalone模式下。笔者在部署standalone模式时,
首先,通过如下命令,启动了Master。
./sbin/start-master.sh
成功启动后,可以通过http://master_ip:8080/连接到Master的UI,并在页面中找到URL为spark://host_name:7077。
然后,通过如下命令,启动Slave。
./sbin/start-slave.sh spark://host_name:7077
但Slave无法连接Master,查它的Log,可以得到如下错误。
17/02/20 15:31:12 INFO Worker: Retrying connection to master (attempt # 2)
17/02/20 15:31:12 INFO Worker: Connecting to master 10.xx.xx.xx:7077...
17/02/20 15:31:12 WARN Worker: Failed to connect to master 10.xx.xx.xx:7077
org.apache.spark.SparkException: Exception thrown in awaitResult
at org.apache.spark.rpc.RpcTimeout$$anonfun$1.applyOrElse(RpcTimeout.scala:77)
at org.apache.spark.rpc.RpcTimeout$$anonfun$1.applyOrElse(RpcTimeout.scala:75)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
at org.apache.spark.rpc.RpcTimeout$$anonfun$addMessageIfTimeout$1.applyOrElse(RpcTimeout.scala:59)
at org.apache.spark.rpc.RpcTimeout$$anonfun$addMessageIfTimeout$1.applyOrElse(RpcTimeout.scala:59)
at scala.PartialFunction$OrElse.apply(PartialFunction.scala:167)
at org.apache.spark.rpc.RpcTimeout.awaitResult(RpcTimeout.scala:83)
at org.apache.spark.rpc.RpcEnv.setupEndpointRefByURI(RpcEnv.scala:88)
at org.apache.spark.rpc.RpcEnv.setupEndpointRef(RpcEnv.scala:96)
at org.apache.spark.deploy.worker.Worker$$anonfun$org$apache$spark$deploy$worker$Worker$$tryRegisterAllMasters$1$$anon$1.run(Worker.scala:216)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Failed to connect to /10.xx.xx.xx:7077
at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:228)
at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:179)
at org.apache.spark.rpc.netty.NettyRpcEnv.createClient(NettyRpcEnv.scala:197)
at org.apache.spark.rpc.netty.Outbox$$anon$1.call(Outbox.scala:191)
at org.apache.spark.rpc.netty.Outbox$$anon$1.call(Outbox.scala:187)
... 4 more
Caused by: java.net.ConnectException: Connection refused: /10.xx.xx.xx:7077
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:224)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:289)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
... 1 more
这是一个很常见的问题,上网一搜也能发现很多人遇到过。具体怎么解决这个问题呢,请看下文。
问题解决过程
这到底是网络连接的问题,还是Spark连接的问题呢?下面就是笔者解决这个问题的思路历程。
关闭防火墙
防火墙有时候会阻止一些连接或端口。再我们不确定哪些服务有用时,最好的办法就是先将防火墙关闭,确定是防火墙导致的问题,再来调整防火墙的策略。笔者用的是Ubuntu Server,关闭防火墙的命令如下:
ufw disable
然后可以用如下命令来检查防火墙的状态:
ufw status verbose
对于这个问题,关了防火墙,发现还是报相同的错误信息。继续思考其它方案。
修改hostname
笔者Master所在的Linux,hostname是root,太简单,连接spark://host_name:7077会不会被DNS解析到其它主机。在Master主机修改hostname,需要修改两个文件,命令如下。
vim /etc/hostname
vim /etc/hosts
修改后,启动Master,再启动Slave连接spark://new_host_name:7077,依然报错。猜测是连接不到new_host_name,改为启动Slave连接spark://master_ip:7077,依然报错。猜测是连接不到new_host_name,改为启动Slave连接spark
用nmap确认能连接Master主机端口7077
那么,是不是Master的7077端口不能连接呢?下载nmap工具(apt install nmap
),在Slave主机用如下命令测试:
nmap -p 7077 master_ip
确认7077端口是可以连接的。
python环境
笔者环境中默认是python版本是2.7,而笔者用virtualenv将Spark运行在Python3.5环境中。
这里无论是用python2.7还是Python3.5的virtualenv,都不能解决问题。
JDK配置
笔者的JAVA_HOME,CLASSPATH,PATH都写在~/.bashrc,这里用source命令重新使能这几个环境变量。依然无法解决问题。
修改Spark配置文件
在*上,笔者还查到了大量修改conf/spark-env.sh的做法,如这里。这些修改都分别在Master和Slave都做了,还是没法解决问题。
添加master启动参数
最后,在*上发现了一个很小众的答案,修改了Master的启动方法:
./sbin/start-master.sh -h master_ip
成功启动后,可以通过http://master_ip:8080/连接到Master的UI,并在页面中找到URL为spark://master_ip:7077。注意这里的URL已经由spark://host_name:7077变为spark://master_ip:7077。
然后,通过如下命令,成功启动了Slave!!
./sbin/start-slave.sh spark://master_ip:7077
结论
Best Practice
:用-h参数启动Master,连接到Master的UI,确保URL是spark://master_ip:7077,而非spark://host_name:7077。这样Slave连接Master的问题不会再出现。