Spark on Yarn ”爬坑“集錦(spark1.2)

一、概述

    Ha,已經有兩個月沒有更新blog了。由於近排公司需要引入Spark相關技術,我也是作爲技術攻關人員之一,在這段時間使用Spark遇到了挺多問題,跌的坑也比較多,這篇blog主要總結一下這段時間使用Spark遇到的一些問題。

二、遇到的"坑"和爬坑思路

1、SparkSql on yarn-client模式遇到找不到mysql驅動包問題。

解決方案:這個比較簡單直接編輯$SPARK_HOME/conf/spark-env.sh文件,將mysql的驅動jarexport進去,如:

export SPARK_CLASSPATH=$SPARK_CLASSPATH:/home/hadoop/hadoop/spark-1.2.0-bin-hadoop2.4/lib/mysql-connector-java-5.1.7-bin.jar:/home/hadoop/hadoop/hadoop-2.5.0/share/hadoop/common/hadoop-lzo-0.4.20-SNAPSHOT.jar


裏邊我同時也將lzo的jar包也export進去了,是因爲我需要在spark中使用lzo的壓縮輸入格式,對於這個lzo的jar包需要注意下,這個jar包是需要自己在裝好了lzo本地庫之後,自己編譯出來的。


2、SparkSql on yarn-cluster模式遇到找不到datanucleus相關jar包,具體錯誤信息看下面:

Caused by: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.metastore.HiveMetaStoreClient
 at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1412)
 at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.<init>(RetryingMetaStoreClient.java:62)
 at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.getProxy(RetryingMetaStoreClient.java:72)
 at org.apache.hadoop.hive.ql.metadata.Hive.createMetaStoreClient(Hive.java:2453)
 at org.apache.hadoop.hive.ql.metadata.Hive.getMSC(Hive.java:2465)
 at org.apache.hadoop.hive.ql.session.SessionState.start(SessionState.java:340)
 ... 7 more
Caused by: java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
 at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1410)
 ... 12 more
Caused by: javax.jdo.JDOFatalUserException: Class org.datanucleus.api.jdo.JDOPersistenceManagerFactory was not found.
NestedThrowables:
java.lang.ClassNotFoundException: org.datanucleus.api.jdo.JDOPersistenceManagerFactory
 at javax.jdo.JDOHelper.invokeGetPersistenceManagerFactoryOnImplementation(JDOHelper.java:1175)
 at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:808)
 at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:701)
 at org.apache.hadoop.hive.metastore.ObjectStore.getPMF(ObjectStore.java:310)
 at org.apache.hadoop.hive.metastore.ObjectStore.getPersistenceManager(ObjectStore.java:339)
 at org.apache.hadoop.hive.metastore.ObjectStore.initialize(ObjectStore.java:248)
 at org.apache.hadoop.hive.metastore.ObjectStore.setConf(ObjectStore.java:223)
 at org.apache.hadoop.util.ReflectionUtils.setConf(ReflectionUtils.java:73)
 at org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java:133)
 at org.apache.hadoop.hive.metastore.RawStoreProxy.<init>(RawStoreProxy.java:58)
 at org.apache.hadoop.hive.metastore.RawStoreProxy.getProxy(RawStoreProxy.java:67)
 at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.newRawStore(HiveMetaStore.java:497)
 at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.getMS(HiveMetaStore.java:475)
 at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.createDefaultDB(HiveMetaStore.java:523)
 at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.init(HiveMetaStore.java:397)
 at org.apache.hadoop.hive.metastore.HiveMetaStore$HMSHandler.<init>(HiveMetaStore.java:356)
 at org.apache.hadoop.hive.metastore.RetryingHMSHandler.<init>(RetryingHMSHandler.java:54)
 at org.apache.hadoop.hive.metastore.RetryingHMSHandler.getProxy(RetryingHMSHandler.java:59)
 at org.apache.hadoop.hive.metastore.HiveMetaStore.newHMSHandler(HiveMetaStore.java:4944)
 at org.apache.hadoop.hive.metastore.HiveMetaStoreClient.<init>(HiveMetaStoreClient.java:171)
 ... 17 more
Caused by: java.lang.ClassNotFoundException: org.datanucleus.api.jdo.JDOPersistenceManagerFactory
 at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:270)
 at javax.jdo.JDOHelper$18.run(JDOHelper.java:2018)
 at javax.jdo.JDOHelper$18.run(JDOHelper.java:2016)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.jdo.JDOHelper.forName(JDOHelper.java:2015)
 at javax.jdo.JDOHelper.invokeGetPersistenceManagerFactoryOnImplementation(JDOHelper.java:1162)
 ... 36 more


解決方案:這個問題相當坑爹,個人感覺完全是個bug來的。像這種jar應該是在$SPARK_HOME/bin/compute-classpath.sh計算出來然後export進去的,看看comput-classpath.sh的相關shell代碼(從97行往下看,spark版本爲1.2):

wKiom1SiUOvymbCIAAmuyMbxwOs146.jpg

很遺憾,在sparkSql on yarn-cluster模式這個腳本無法$SPARK_HOME/lib下的datanucleus相關包export進去。經過幾番折騰,翻了一遍spark在github上的Pull request終於找到了解決方案:在提交啓動sparkSql cli的時候使用--jar將相關datanucleus的jar包export進去就ok了,看命令:

spark-sql --master yarn-cluster \
 --jars /data1/app/spark-1.2.0-bin-hadoop2.4/lib/datanucleus-api-jdo-3.2.6.jar,/data1/app/spark-1.2.0-bin-hadoop2.4/lib/datanucleus-core-3.2.10.jar,/data1/app/spark-1.2.0-bin-hadoop2.4/lib/datanucleus-rdbms-3.2.9.jar,/data1/app/spark-1.2.0-bin-hadoop2.4/lib/mysql-connector-java-5.1.7-bin.jar  \
--driver-memory 4G  --executor-cores 32 --queue spark  --executor-memory 70G  --num-executors 7  -e "use test1;
select count(*) from st_pc_lifecycle_list tb2 left outer join
(select ip,count(*) from st_pc_lifecycle_list where dt='2014-07-16' group by ip) tb1 on(tb1.ip=tb2.ip) where tb2.dt>='2014-11-20'  limit 10;"


3、使用spark-sql on yarn-cluster模式無法連接到hive-site.xml指定的metaStore,use 相關database時候出現找不到庫錯誤。這個問題又是相當隱蔽的問題,剛剛排查的時候也是比較困難的。

詳細錯誤信息:

wKioL1SiVVuA-AGCAAorFbyZ1qY907.jpg解決問題思路:

(1)我們觀察這個錯誤,可能會隱隱約約想,這個我明顯是連接上了metastore,那麼爲什麼還找不到metastore裏邊的庫啊??呵呵,我當時也是相當鬱悶,直到我看到了這麼一條提示:metastore.MetaStoreDirectSql: MySQL check failed(上面的錯誤截圖沒有截出來),這樣我就知道了在計算節點啓動的Dirver並沒有正常的連接到hive-site.xml指定的metaStore。那麼既然driver沒有連接上hive-site.xml指定的metaStore,那麼爲什麼看dirver的日誌顯示的確實可以連接上metaStore,只是無法連接到相應的庫的?這下要搜源碼了,直接在源碼搜索"hive-site.xml",然後在sql-programming-guide.md中看到了這麼一段提示信息:

wKioL1SiXA_h__BHAARuw6oGFTw303.jpg

或者再看HiveContext代碼:

wKioL1SiXeXwGdr2AAO7lxCG4Tc908.jpg

哈哈,這麼一看聽明白了:就算用戶不指定hive-site.xml文件,也會建立一個默認的hiveContext的,這樣說的話在這個hiveContext中肯定是找不到hive-site.xml指定的庫了。現在的問題轉化成爲計算節點上的Dirver找不到hive-site.xml了。啓動作業時使用--driver-class-path,--jar,--drier-library-path指定hive-site.xml位置都不管用。直到看到Dirver界面的classpath纔有些頓悟:

wKioL1SiXq-hTm13AANOtoIIKsc323.jpg

既然hadoop的conf path已經被export到了classpath中,爲何不試試將hive-site.xml丟到hadoop的conf路徑試試呢,哈哈試了果然ok,了可以正在連接hive-site.xml指定的ip了(要將hive-site.xml丟到所有計算節點的配置文件夾中,因爲Driver可能隨機到任何一個計算節點)。呵呵,找不到hive-site.xml的問題已經解決了,但是還是連接不上metaStore,已經卡在連接階段。哈哈這個比較好解決:在hive-site.xml中將hive.metastore.uris配置上就ok了,給大家個參考:

<property>
        <name>hive.metastore.uris</name>
                <value>thrift://10.1.80.40:9083</value>
          <description>Thrift URI for the remote metastore. Used by metastore client to connect to remote metastore.</description>
</property>
<property>
        <name>hive.server2.thrift.min.worker.threads</name>
        <value>5</value>
        <description>Minimum number of Thrift worker threads</description>
</property>
<property>
        <name>hive.server2.thrift.max.worker.threads</name>
        <value>500</value>
        <description>Maximum number of Thrift worker threads</description>
</property>
<property>
        <name>hive.server2.thrift.port</name>
        <value>10000</value>
        <description>Port number of HiveServer2 Thrift interface. Can be overridden by setting $HIVE_SERVER2_THRIFT_PORT</description>
</property>
<property>
        <name>hive.server2.thrift.bind.host</name>
        <value>slave8040</value>
        <description>Bind host on which to run the HiveServer2 Thrift interface.Can be overridden by setting$HIVE_SERVER2_THRIFT_BIND_HOST</description>
</property>
<property>
        <name>hive.server2.enable.doAs</name>
        <value>true</value>
</property>
<property>
  <name>hive.metastore.warehouse.dir</name>
    <value>/user/hive/warehouse</value>
    <description>location of default database for the warehouse</description>
 </property>
<property>
  <name>hive.metastore.local</name>
  <value>hive.metastore.local</value>
  <description>location of default database for the warehouse</description>
</property>

配置好了metaStore的uri後,不要忘記了重要的一步,就是啓動metaStore服務:進入$HIVE_HOME/bin,運行nohup ./hive --server metastore &

啓動完之後看看端口是否正常:

[hadoop@slave8040 conf]$ jps 
23158 SparkSubmitDriverBootstrapper 
23510 SparkSubmit 
4442 Jps 
9866 RunJar 
[hadoop@slave8040 conf]$ ps -ef | grep 9866 
hadoop 4504 14107 0 16:25 pts/0 00:00:00 grep 9866 
hadoop 9866 1 0 Dec27 ? 00:01:54 /usr/local/jdk1.7.0_51/bin/java -Xmx3072m -Djava.net.preferIPv4Stack=true -Dhadoop.log.dir=/data2/hadoop/logs/hadoop -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=/home/hadoop/hadoop/hadoop-2.5.0 -Dhadoop.id.str=hadoop -Dhadoop.root.logger=INFO,console -Djava.library.path=/home/hadoop/hadoop/hadoop-2.5.0/lib/native -Dhadoop.policy.file=hadoop-policy.xml -Djava.net.preferIPv4Stack=true -Xmx2048m -Dhadoop.security.logger=INFO,NullAppender org.apache.hadoop.util.RunJar /home/hadoop/hadoop/apache-hive-0.13.1-bin/lib/hive-service-0.13.1.jar org.apache.hadoop.hive.metastore.HiveMetaStore 
[hadoop@slave8040 conf]$ netstat -antp| grep 9866 
(Not all processes could be identified, non-owned process info 
will not be shown, you would have to be root to see it all.) 
tcp 0 0 0.0.0.0:9083 0.0.0.0:* LISTEN 9866/java 
tcp 0 0 10.1.80.40:47635 10.1.80.40:3306 ESTABLISHED 9866/java 
tcp 0 0 10.1.80.40:47591 10.1.80.40:3306 ESTABLISHED 9866/java 
tcp 0 0 10.1.80.40:47636 10.1.80.40:3306 ESTABLISHED 9866/java 
tcp 0 0 10.1.80.40:9083 10.1.80.40:51365 ESTABLISHED 9866/java 
tcp 0 0 10.1.80.40:47590 10.1.80.40:3306 ESTABLISHED 9866/java 
tcp 0 0 10.1.80.40:9083 10.1.80.40:51367 ESTABLISHED 9866/java

再次spark-sql on yarn-cluster模式完全ok。

吐槽下:spark還有挺多不完善的東西,小bug挺多,還有官方相關文檔不全,像那個配置文檔也只是部分配置項的,這個希望以後可以繼續完善。不過spark的版本更新速度相當快,還有在github上的提問獲得的回答想相當快,這個不錯。哈哈,Spark的社區交流還是相當活躍的,呵呵繼續爬坑。

4、最後一個坑,持久代OOM問題。

錯誤信息:使用spark-sql on yarn-cluster的時候啓動driver報如下錯誤:

  1. Exception in thread "Thread-2" java.lang.OutOfMemoryError: PermGen space

哈哈,這個又是相當常見的錯誤。

解決思路:

直接增大PermGen space,編輯spark-defaults.xml添加:

spark.driver.extraJavaOptions -XX:PermSize=128M -XX:MaxPermSize=256M 

再試ok。但是這裏還有一個問題:什麼使用yarn-client運行spark-sql就不會出現這問題呢?通過一番腳本追蹤發現yarn-client模式運行時在$SPARK_HOME/bin/spark-class文件中已經設置了持久代大小,具體看spark-class的116行:JAVA_OPTS="-XX:MaxPermSize=128m $OUR_JAVA_OPTS",問題解決。Spark的各種模式的jvm的內存參數設置比較容易混淆,這裏引用http://www.aboutyun.com/thread-9425-1-1.html 裏邊的小段總結:


  總結一下Spark中各個角色的JVM參數設置:   

(1)Driver的JVM參數:
-Xmx,-Xms,如果是yarn-client模式,則默認讀取
spark-env文件中的SPARK_DRIVER_MEMORY值,-Xmx,-Xms值一樣大小;如果是yarn-cluster模式,則讀取的是spark-default.conf文件中的spark.driver.extraJavaOptions對應的JVM參數值。
PermSize,如果是yarn-client模式,則是默認讀取
spark-class文件中的JAVA_OPTS="-XX:MaxPermSize=256m $OUR_JAVA_OPTS"值;如果是yarn-cluster模式,讀取的是spark-default.conf文件中的spark.driver.extraJavaOptions對應的JVM參數值。
GC方式,如果是yarn-client模式,默認讀取的是spark-class文件中的JAVA_OPTS;如果是yarn-cluster模式,則讀取的是spark-default.conf文件中的spark.driver.extraJavaOptions對應的參數值。
以上值最後均可被spark-submit工具中的--driver-java-options參數覆蓋。


(2)Executor的JVM參數:
-Xmx,-Xms,如果是yarn-client模式,則默認讀取spark-env文件中的SPARK_EXECUTOR_MEMORY值,-Xmx,-Xms值一樣大小;如果是yarn-cluster模式,則讀取的是spark-default.conf文件中的spark.executor.extraJavaOptions對應的JVM參數值。
PermSize,兩種模式都是讀取的是
spark-default.conf文件中的spark.executor.extraJavaOptions對應的JVM參數值。
GC方式,兩種模式都是讀取的是spark-default.conf文件中的spark.executor.extraJavaOptions對應的JVM參數值。

三、總結
    在Spark的使用當中,遇到的各種問題還是挺多的,好在版本更新比較快。另外,spark1.2中將shuffle默認基於sort了,還有采用了netty方式,但是在用的過程中也遇到了一些問題,比如fetch Failure、lost Excutor等等,下篇blog總結吧。生命不斷,爬坑不止!


參考文獻:http://www.aboutyun.com/thread-9425-1-1.html 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章