Oozie运行含HiveContext的Spark Scala应用报PermGen内存溢出错误
环境信息
- CDH5.13 Quickstart VM(20G内存),已将Java 7升级至Java 8
- Spark 1.6.0,Scala 2.10.5
- Oozie集成Spark运行,其他无HiveContext的Spark应用可正常执行
应用代码与配置
Scala应用代码
import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.rdd.RDD object ThirdApp { def main(args: Array[String]) { val conf = new SparkConf().setAppName("Third Application") val sc = new SparkContext(conf) val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc) import sqlContext.implicits._ sqlContext.sql("CREATE TABLE IF NOT EXISTS default.src (key INT, value STRING)") } }
sbt配置文件
name := "Third Project" version := "1.0" scalaVersion := "2.10.5" libraryDependencies ++= Seq( "org.apache.spark" %% "spark-core" % "1.6.0", "org.apache.spark" %% "spark-hive" % "1.6.0" )
Oozie Workflow配置(workflow.xml)
<workflow-app name="spark-scala" xmlns="uri:oozie:workflow:0.5"> <start to="spark-5a6a"/> <kill name="Kill"> <message>Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message> </kill> <action name="spark-5a6a"> <spark xmlns="uri:oozie:spark-action:0.2"> <job-tracker>${jobTracker}</job-tracker> <name-node>${nameNode}</name-node> <master>local</master> <mode>client</mode> <name>MySpark</name> <class>ThirdApp</class> <jar>third-project_2.10-1.0.jar</jar> <file>/user/cloudera/oozie-spark/third-project_2.10-1.0.jar#third-project_2.10-1.0.jar</file> </spark> <ok to="End"/> <error to="Kill"/> </action> <end name="End"/> </workflow-app>
job.properties配置
oozie.use.system.libpath=True send_email=False dryrun=False nameNode=hdfs://quickstart.cloudera:8020 jobTracker=quickstart.cloudera:8032 security_enabled=False
问题描述
该应用通过Spark Shell提交可正常运行,能成功创建Hive表,但通过Oozie运行时抛出PermGen空间内存溢出错误。此前运行不含HiveContext的Spark应用在Oozie中均无异常。
已尝试的解决方法:
- 在mapred-site.xml中增加Map/Reduce内存配置
- 在Cloudera Manager中调整Java堆内存
- 设置Gateway默认组
- 在workflow中添加
--driver-memory 5G参数
但以上方法均未解决问题,请求帮助。
首先得提一句:你已经升级到Java 8了,但Java 8里早就用Metaspace替代了PermGen,不过Oozie的Spark Action可能默认还带着老的PermGen配置,再加上HiveContext要加载一大堆类,这就触发了内存溢出的问题。试试下面这些步骤:
1. 给Spark Action加上JVM元空间参数
直接在workflow.xml的<spark>节点里加<spark-opts>标签,把Metaspace的大小配置上——因为你用Java 8,重点调这个:
<spark xmlns="uri:oozie:spark-action:0.2"> <!-- 保留你原来的所有配置 --> <job-tracker>${jobTracker}</job-tracker> <name-node>${nameNode}</name-node> <master>local</master> <mode>client</mode> <name>MySpark</name> <class>ThirdApp</class> <jar>third-project_2.10-1.0.jar</jar> <file>/user/cloudera/oozie-spark/third-project_2.10-1.0.jar#third-project_2.10-1.0.jar</file> <!-- 新增这行JVM参数配置 --> <spark-opts>--driver-java-options "-XX:MaxMetaspaceSize=1G -XX:MetaspaceSize=512M" --executor-java-options "-XX:MaxMetaspaceSize=1G -XX:MetaspaceSize=512M"</spark-opts> </spark>
要是你的环境里某些组件还在依赖PermGen(比如CDH的老配置),就换成PermGen的参数:
<spark-opts>--driver-java-options "-XX:MaxPermSize=1G -XX:PermSize=512M" --executor-java-options "-XX:MaxPermSize=1G -XX:PermSize=512M"</spark-opts>
2. 检查Oozie的共享库是否完整
你设置了oozie.use.system.libpath=True,得确保Oozie的Spark共享库包含了Hive的相关依赖。可以去Cloudera Manager里查Oozie的Spark共享库状态,或者手动把Hive的JAR包放到Oozie的Spark lib目录(一般是HDFS上的/user/oozie/share/lib/lib_<timestamp>/spark路径)。
3. 把Driver内存和JVM参数一起配置
你之前试过--driver-memory 5G,但最好把它和元空间参数放在同一个<spark-opts>里,确保参数能正确传递给Spark:
<spark-opts>--driver-memory 5G --driver-java-options "-XX:MaxMetaspaceSize=1G" --executor-memory 2G --executor-java-options "-XX:MaxMetaspaceSize=1G"</spark-opts>
4. 调整Oozie自身的JVM配置
在Cloudera Manager里找到Oozie服务的配置,找到Oozie Server Advanced Configuration Snippet (Safety Valve) for oozie-env.sh,加上这行:
export CATALINA_OPTS="-XX:MaxMetaspaceSize=1G -XX:MetaspaceSize=512M $CATALINA_OPTS"
这是给Oozie自己的JVM加元空间,避免它调度任务时自己先内存不够。
为啥只有HiveContext会出这问题?
因为HiveContext要加载超多类——Hive客户端、JDBC驱动、各种序列化类,这些都会占满元空间(或者PermGen)。直接用Shell跑的时候,Spark Shell的默认JVM参数已经做了优化,所以没问题;但Oozie调用Spark时,默认的JVM参数没考虑这么多类的加载需求,就溢出了。
内容的提问来源于stack exchange,提问作者El Mehdi OUAFIQ

