共用方式為


Azure HDInsight 中 Apache Spark 的 OutOfMemoryError 例外狀況

本文說明在 Azure HDInsight 叢集中使用 Apache Spark 元件時,疑難排解步驟和可能的解決方案。

案例:Apache Spark 的 OutOfMemoryError 例外狀況

問題

您的 Apache Spark 應用程式因 OutOfMemoryError 未處理的例外狀況而失敗。 您可能會收到類似下列的錯誤訊息:

ERROR Executor: Exception in task 7.0 in stage 6.0 (TID 439)

java.lang.OutOfMemoryError
    at java.io.ByteArrayOutputStream.hugeCapacity(Unknown Source)
    at java.io.ByteArrayOutputStream.grow(Unknown Source)
    at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source)
    at java.io.ByteArrayOutputStream.write(Unknown Source)
    at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
    at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:44)
    at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:101)
    at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:239)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
ERROR SparkUncaughtExceptionHandler: Uncaught exception in thread Thread[Executor task launch worker-0,5,main]

java.lang.OutOfMemoryError
    at java.io.ByteArrayOutputStream.hugeCapacity(Unknown Source)
    ...

原因

此例外狀況最可能發生的原因,就是未配置足夠的堆積記憶體給 Java 虛擬機器 (JVM)。 這些 JVM 會當作 Apache Spark 應用程式一部分的執行程式或驅動程式啟動。

解決方法

  1. 決定 Spark 應用程式可處理的資料大小上限。 您可以依輸入資料的大小上限、由轉換輸入資料所產生的中繼資料,以及進一步轉換中繼資料所產生的輸出資料粗略估計。 如果初始估計值不足,請稍微增加大小,並逐一查看直到記憶體錯誤消失為止。

  2. 請確定要使用的 HDInsight 叢集具有足夠的記憶體資源及核心,才能採用 Spark 應用程式。 這可以透過檢視叢集 YARN UI 的「叢集計量」一節來確定已使用記憶體記憶體總計,以及已使用的 VCoreVCore 總計的值。

    YARN 核心記憶體檢視。

  3. 將下列 Spark 組態設定為適當的值。 在應用程式需求與叢集中可用的資源之間取得平衡。 這些值不應超過 YARN 所檢視可用記憶體和核心的 90%,同時應滿足 Spark 應用程式的最小記憶體需求:

    spark.executor.instances (Example: 8 for 8 executor count)
    spark.executor.memory (Example: 4g for 4 GB)
    spark.yarn.executor.memoryOverhead (Example: 384m for 384 MB)
    spark.executor.cores (Example: 2 for 2 cores per executor)
    spark.driver.memory (Example: 8g for 8GB)
    spark.driver.cores (Example: 4 for 4 cores)
    spark.yarn.driver.memoryOverhead (Example: 384m for 384MB)
    

    所有執行程式使用的記憶體總計 =

    spark.executor.instances * (spark.executor.memory + spark.yarn.executor.memoryOverhead) 
    

    驅動程式使用的記憶體總計 =

    spark.driver.memory + spark.yarn.driver.memoryOverhead
    

案例:嘗試開啟 Apache Spark 歷程記錄伺服器時發生 JAVA 堆積空間錯誤

問題

您在 Spark 歷程記錄伺服器中開啟事件時收到下列錯誤:

scala.MatchError: java.lang.OutOfMemoryError: Java heap space (of class java.lang.OutOfMemoryError)

原因

此問題通常是因為開啟大型 Spark 事件檔案時缺少資源所造成。 Spark 堆積大小預設為 1 GB,但大型 Spark 事件檔案可能需要更大的容量。

如果您想要驗證嘗試載入的檔案大小,可執行下列命令:

hadoop fs -du -s -h wasb:///hdp/spark2-events/application_1503957839788_0274_1/
**576.5 M**  wasb:///hdp/spark2-events/application_1503957839788_0274_1

hadoop fs -du -s -h wasb:///hdp/spark2-events/application_1503957839788_0264_1/
**2.1 G**  wasb:///hdp/spark2-events/application_1503957839788_0264_1

解決方法

您可以編輯 Spark 組態中的 SPARK_DAEMON_MEMORY 屬性,並重新啟動所有服務,以增加 Spark 歷程記錄伺服器記憶體。

您可以從 Ambari 瀏覽器 UI 中選取 Spark2/Config/Advanced spark2-env 區段來執行此動作。

進階 spark2-env 區段。

新增下列屬性,將 Spark 歷程記錄伺服器記憶體從 1g 變更為 4g:SPARK_DAEMON_MEMORY=4g

Spark 屬性。

請務必從 Ambari 重新啟動所有受影響的服務。


案例:無法在 Apache Spark 叢集上啟動 Livy 伺服器

問題

無法在 Linux 上的 Apache Spark [(Linux (HDI 3.6) 上的 Spark 2.1] 上啟動 Livy 伺服器。 嘗試重新啟動會導致 Livy 記錄中的下列錯誤堆疊:

17/07/27 17:52:50 INFO CuratorFrameworkImpl: Starting
17/07/27 17:52:50 INFO ZooKeeper: Client environment:zookeeper.version=3.4.6-29--1, built on 05/15/2017 17:55 GMT
17/07/27 17:52:50 INFO ZooKeeper: Client environment:host.name=10.0.0.66
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.version=1.8.0_131
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.vendor=Oracle Corporation
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.home=/usr/lib/jvm/java-8-openjdk-amd64/jre
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.class.path= <DELETED>
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.library.path= <DELETED>
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.io.tmpdir=/tmp
17/07/27 17:52:50 INFO ZooKeeper: Client environment:java.compiler=<NA>
17/07/27 17:52:50 INFO ZooKeeper: Client environment:os.name=Linux
17/07/27 17:52:50 INFO ZooKeeper: Client environment:os.arch=amd64
17/07/27 17:52:50 INFO ZooKeeper: Client environment:os.version=4.4.0-81-generic
17/07/27 17:52:50 INFO ZooKeeper: Client environment:user.name=livy
17/07/27 17:52:50 INFO ZooKeeper: Client environment:user.home=/home/livy
17/07/27 17:52:50 INFO ZooKeeper: Client environment:user.dir=/home/livy
17/07/27 17:52:50 INFO ZooKeeper: Initiating client connection, connectString=<zookeepername1>.cxtzifsbseee1genzixf44zzga.gx.internal.cloudapp.net:2181,<zookeepername2>.cxtzifsbseee1genzixf44zzga.gx.internal.cloudapp.net:2181,<zookeepername3>.cxtzifsbseee1genzixf44zzga.gx.internal.cloudapp.net:2181 sessionTimeout=60000 watcher=org.apache.curator.ConnectionState@25fb8912
17/07/27 17:52:50 INFO StateStore$: Using ZooKeeperStateStore for recovery.
17/07/27 17:52:50 INFO ClientCnxn: Opening socket connection to server 10.0.0.61/10.0.0.61:2181. Will not attempt to authenticate using SASL (unknown error)
17/07/27 17:52:50 INFO ClientCnxn: Socket connection established to 10.0.0.61/10.0.0.61:2181, initiating session
17/07/27 17:52:50 INFO ClientCnxn: Session establishment complete on server 10.0.0.61/10.0.0.61:2181, sessionid = 0x25d666f311d00b3, negotiated timeout = 60000
17/07/27 17:52:50 INFO ConnectionStateManager: State change: CONNECTED
17/07/27 17:52:50 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/07/27 17:52:50 INFO AHSProxy: Connecting to Application History server at headnodehost/10.0.0.67:10200
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
  at java.lang.Thread.start0(Native Method)
  at java.lang.Thread.start(Thread.java:717)
  at com.cloudera.livy.Utils$.startDaemonThread(Utils.scala:98)
  at com.cloudera.livy.utils.SparkYarnApp.<init>(SparkYarnApp.scala:232)
  at com.cloudera.livy.utils.SparkApp$.create(SparkApp.scala:93)
  at com.cloudera.livy.server.batch.BatchSession$$anonfun$recover$2$$anonfun$apply$4.apply(BatchSession.scala:117)
  at com.cloudera.livy.server.batch.BatchSession$$anonfun$recover$2$$anonfun$apply$4.apply(BatchSession.scala:116)
  at com.cloudera.livy.server.batch.BatchSession.<init>(BatchSession.scala:137)
  at com.cloudera.livy.server.batch.BatchSession$.recover(BatchSession.scala:108)
  at com.cloudera.livy.sessions.BatchSessionManager$$anonfun$$init$$1.apply(SessionManager.scala:47)
  at com.cloudera.livy.sessions.BatchSessionManager$$anonfun$$init$$1.apply(SessionManager.scala:47)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
  at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
  at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:47)
  at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
  at scala.collection.AbstractTraversable.map(Traversable.scala:105)
  at com.cloudera.livy.sessions.SessionManager.com$cloudera$livy$sessions$SessionManager$$recover(SessionManager.scala:150)
  at com.cloudera.livy.sessions.SessionManager$$anonfun$1.apply(SessionManager.scala:82)
  at com.cloudera.livy.sessions.SessionManager$$anonfun$1.apply(SessionManager.scala:82)
  at scala.Option.getOrElse(Option.scala:120)
  at com.cloudera.livy.sessions.SessionManager.<init>(SessionManager.scala:82)
  at com.cloudera.livy.sessions.BatchSessionManager.<init>(SessionManager.scala:42)
  at com.cloudera.livy.server.LivyServer.start(LivyServer.scala:99)
  at com.cloudera.livy.server.LivyServer$.main(LivyServer.scala:302)
  at com.cloudera.livy.server.LivyServer.main(LivyServer.scala)
  
  ## using "vmstat" found  we had enough free memory

原因

java.lang.OutOfMemoryError: unable to create new native thread 醒目提示作業系統無法將更多原生執行緒指派給 JVM。 已確認此例外狀況是因為違反個別處理序的執行緒計數限制所造成。

當 Livy 伺服器意外終止時,Spark 叢集的所有連線也會終止,這表示所有作業和相關資料都會遺失。 在先前引進的 HDP 2.6 工作階段復原機制中,Livy 會將工作階段詳細資料儲存在 Zookeeper 中,以在 Livy 伺服器恢復之後復原。

透過 Livy 提交大量作業時,Livy 伺服器的高可用性會將這些工作階段狀態儲存在 HDInsight 叢集上的 ZK 中,並在重新啟動 Livy 服務時復原這些工作階段。 在非預期終止之後的重新啟動時,Livy 會為每個工作階段建立一個執行緒,而這會累積一些待復原工作階段,進而導致建立太多執行緒。

解決方法

使用下列步驟刪除所有項目。

  1. 使用下列指令碼取得 Zookeeper 節點的 IP 位址

    grep -R zk /etc/hadoop/conf  
    
  2. 上述命令會列出叢集的所有 Zookeeper

    /etc/hadoop/conf/core-site.xml:      <value><zookeepername1>.lnuwp5akw5ie1j2gi2amtuuimc.dx.internal.cloudapp.net:2181,<zookeepername2>.lnuwp5akw5ie1j2gi2amtuuimc.dx.internal.cloudapp.net:2181,<zookeepername3>.lnuwp5akw5ie1j2gi2amtuuimc.dx.internal.cloudapp.net:2181</value>
    
  3. 使用 ping 取得 Zookeeper 節點的所有 IP 位址,或者也可以使用 zookeeper 名稱從前端節點連線到 zookeeper

    /usr/hdp/current/zookeeper-client/bin/zkCli.sh -server <zookeepername1>:2181
    
  4. 連線到 Zookeeper 之後,請執行下列命令,以列出嘗試重新啟動的所有工作階段。

    1. 大部分情況下,這個清單可能會包含超過 8000 個工作階段####

      ls /livy/v1/batch
      
    2. 下列命令是移除所有要復原的工作階段。 #####

      rmr /livy/v1/batch
      
  5. 等候上述命令完成,然後游標返回提示,接著從 Ambari 重新啟動 Livy 服務,此操作應該會成功。

注意

完成執行之後,請對 Livy 工作階段執行 DELETE。 依據設計,當 Spark 應用程式完成時,將不會自動刪除 Livy 批次工作階段。 Livy 工作階段是由 POST 要求針對 Livy Rest 伺服器所建立的實體。 需要透過 DELETE 呼叫才能刪除該實體。 或者,我們應該等候 GC 啟動。


下一步

如果您沒有看到您的問題,或無法解決您的問題,請瀏覽下列其中一個管道以取得更多支援: