それぞれサイズがm3.xlarge(1マスター4スレーブ)のAWS EMRで5ノードSparkクラスターを実行しています。 146Mbのbzip2圧縮CSVファイルを正常に実行し、完全に集約された結果になりました。
今、私はこのクラスターで〜5GBのbzip2 CSVファイルを処理しようとしていますが、このエラーを受け取っています:
16/11/23 17:29:53警告TaskSetManager:ステージ6.0でタスク49.2を失いました(TID xxx、xxx.xxx.xxx.compute.internal):ExecutorLostFailure(実行中のタスクの1つが原因でエグゼキューター16が終了しました)理由:コンテナーメモリの制限を超えたためにYARNによって殺されました。 10.4 GBの10.4 GBの物理メモリが使用されます。 spark.yarn.executor.memoryOverheadをブーストすることを検討してください。
〜75GBクラスターで〜10.5GBのメモリ制限(3m.xlargeインスタンスごとに15GB)を取得している理由について混乱しています...
これが私のEMR設定です:
[
{
"classification":"spark-env",
"properties":{
},
"configurations":[
{
"classification":"export",
"properties":{
"PYSPARK_PYTHON":"python34"
},
"configurations":[
]
}
]
},
{
"classification":"spark",
"properties":{
"maximizeResourceAllocation":"true"
},
"configurations":[
]
}
]
私が読んだことから、maximizeResourceAllocation
プロパティを設定すると、EMRがSparkを構成して、クラスターで利用可能なすべてのリソースを完全に利用するようになります。つまり、〜75GBのメモリが使用可能になっているはずです。..〜10.5GBのメモリ制限エラーが発生するのはなぜですか?私が実行しているコードは次のとおりです。
def sessionize(raw_data, timeout):
# https://www.dataiku.com/learn/guide/code/reshaping_data/sessionization.html
window = (pyspark.sql.Window.partitionBy("user_id", "site_id")
.orderBy("timestamp"))
diff = (pyspark.sql.functions.lag(raw_data.timestamp, 1)
.over(window))
time_diff = (raw_data.withColumn("time_diff", raw_data.timestamp - diff)
.withColumn("new_session", pyspark.sql.functions.when(pyspark.sql.functions.col("time_diff") >= timeout.seconds, 1).otherwise(0)))
window = (pyspark.sql.Window.partitionBy("user_id", "site_id")
.orderBy("timestamp")
.rowsBetween(-1, 0))
sessions = (time_diff.withColumn("session_id", pyspark.sql.functions.concat_ws("_", "user_id", "site_id", pyspark.sql.functions.sum("new_session").over(window))))
return sessions
def aggregate_sessions(sessions):
median = pyspark.sql.functions.udf(lambda x: statistics.median(x))
aggregated = sessions.groupBy(pyspark.sql.functions.col("session_id")).agg(
pyspark.sql.functions.first("site_id").alias("site_id"),
pyspark.sql.functions.first("user_id").alias("user_id"),
pyspark.sql.functions.count("id").alias("hits"),
pyspark.sql.functions.min("timestamp").alias("start"),
pyspark.sql.functions.max("timestamp").alias("finish"),
median(pyspark.sql.functions.collect_list("foo")).alias("foo"),
)
return aggregated
spark_context = pyspark.SparkContext(appName="process-raw-data")
spark_session = pyspark.sql.SparkSession(spark_context)
raw_data = spark_session.read.csv(sys.argv[1],
header=True,
inferSchema=True)
# Windowing doesn't seem to play nicely with TimestampTypes.
#
# Should be able to do this within the ``spark.read.csv`` call, I'd
# think. Need to look into it.
convert_to_unix = pyspark.sql.functions.udf(lambda s: arrow.get(s).timestamp)
raw_data = raw_data.withColumn("timestamp",
convert_to_unix(pyspark.sql.functions.col("timestamp")))
sessions = sessionize(raw_data, SESSION_TIMEOUT)
aggregated = aggregate_sessions(sessions)
aggregated.foreach(save_session)
基本的に、データを集計するためのウィンドウ処理とgroupBy以外は何もありません。
それらのエラーのいくつかから始まり、同じエラーの量の増加を止めることに向かっています。
-conf spark.yarn.executor.memoryOverheadでspark-submitを実行しようとしましたが、それでも問題は解決しないようです。
あなたの痛みが分かります..
YARNでSparkを使用するとメモリ不足になるという同様の問題がありました。 5つの64GB、16コアのVMがあり、spark.yarn.executor.memoryOverhead
の設定に関係なく、これらのタスクに十分なメモリを確保できませんでした。これは、これを引き起こした比較的単純なSparkアプリケーションとして。
VMでの物理メモリ使用量は非常に少ないが、仮想メモリ使用量は非常に高いことがわかりました(ログがphysicalメモリについて不平を言っているにもかかわらず)。 yarn.nodemanager.vmem-check-enabled
のyarn-site.xml
をfalse
に設定すると、コンテナは強制終了されなくなり、アプリケーションは期待どおりに動作するように見えました。
さらに調査を行って、ここでこれが起こる理由に対する答えを見つけました: https://www.mapr.com/blog/best-practices-yarn-resource-management
Centos/RHEL 6では、OSの動作により積極的に仮想メモリが割り当てられるため、仮想メモリチェッカーを無効にするか、yarn.nodemanager.vmem-pmem-ratioを比較的大きな値に増やす必要があります。
そのページには、IBMの非常に役立つページへのリンクがありました: https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
要約すると、glibc> 2.10はメモリ割り当てを変更しました。割り当てられている膨大な量の仮想メモリは世界の終わりではありませんが、YARNのデフォルト設定では機能しません。
yarn.nodemanager.vmem-check-enabled
をfalseに設定する代わりに、MALLOC_ARENA_MAX
環境変数をhadoop-env.sh
の小さい数値に設定して遊ぶこともできます。
両方のページを読むことをお勧めします-情報は非常に便利です。
spark-submit
を使用しておらず、 Duff で言及されているyarn.nodemanager.vmem-check-enabled
パラメーターを指定する別の方法を探している場合、次の2つの方法があります。
JSON設定ファイル(AWS CLIまたはboto3スクリプトに渡す)を使用している場合、次の設定を追加する必要があります。
[{
"Classification": "yarn-site",
"Properties": {
"yarn.nodemanager.vmem-check-enabled": "false"
}
}]
EMRコンソールを使用する場合は、次の構成を追加します。
classification=yarn-site,properties=[yarn.nodemanager.vmem-check-enabled=false]
見る、
現在作業している巨大なクラスターでも同じ問題が発生しました。この問題は、ワーカーにメモリを追加しても解決されません。プロセス集約では、sparkが現在よりも多くのメモリを使用し、sparkジョブがオフヒープメモリを使用し始める場合があります。
1つの簡単な例は次のとおりです。
reduceByKey
に必要なデータセットがある場合、1つのワーカーで他のデータよりも多くのデータを集約することがあり、このデータが1つのワーカーのメモリを超えた場合、そのエラーメッセージが表示されます。
オプションspark.yarn.executor.memoryOverhead
を追加すると、ワーカーに使用されるメモリの50%を設定する場合に役立ちます(テストのためだけで、機能するかどうかを確認するには、テストを追加して追加できます)。
ただし、Sparkがクラスター内のメモリ割り当てでどのように機能するかを理解する必要があります。
メモリの割り当てに関する良い点の1つは、実行でキャッシュを使用しない場合、sparkを設定して、そのストレージスペースを使用して実行を処理し、OOMエラーを部分的に回避できます。 sparkのドキュメントでこれを見ることができます:
この設計により、いくつかの望ましい特性が保証されます。まず、キャッシングを使用しないアプリケーションは、スペース全体を実行に使用できるため、不必要なディスク流出を回避できます。第二に、キャッシングを使用するアプリケーションは、データブロックが排除されない最小ストレージスペース(R)を予約できます。最後に、このアプローチは、メモリを内部で分割する方法に関するユーザーの専門知識を必要とせずに、さまざまなワークロードに対してすぐに使用できる合理的なパフォーマンスを提供します。
しかし、それをどのように使用できますか?
いくつかの構成を変更し、MemoryOverhead
構成をジョブ呼び出しに追加できますが、これも追加することを検討してください:spark.memory.fraction
を0.8または0.85に変更し、spark.memory.storageFraction
を0.35または0.2に減らします。
他の構成も役立ちますが、ケースを確認する必要があります。これらすべての構成を設定します ここ 。
さて、私の場合に役立つもの。
2.5Kのワーカーと2.5TBのRAMを持つクラスターがあります。そして、あなたのようなOOMエラーに直面していました。 spark.yarn.executor.memoryOverhead
を2048に増やすだけです。そして、 動的割り当て を有効にします。そして、ジョブを呼び出すとき、ワーカーにメモリを設定せず、Sparkが決定するためにそれを残します。オーバーヘッドを設定するだけです。
しかし、小さなクラスターの一部のテストでは、実行サイズとストレージメモリを変更します。これで問題は解決しました。
再分割してみてください。私の場合はうまくいきます。
データフレームは、write.csv()
でロードされた当初はそれほど大きくありませんでした。 executorの各処理タスクに対して合計で数100 MBのメモリが必要となる場合があるため、データファイルは10 MB程度になりました。パーティションの数が2になるようにチェックしました。その後、次の操作が他のテーブルと結合し、新しい列を追加する間、Snowballのように成長しました。そして、特定のステップでメモリ制限を超える問題に遭遇しました。パーティションの数を確認しましたが、まだ2であり、私が推測した元のデータフレームから導き出されました。だから私は最初からそれを再分割しようとしましたが、もう問題はありませんでした。
SparkおよびYARNについての資料をまだ読んでいません。私が知っていることは、ノードにエグゼキューターがあることです。エグゼキュータは、リソースに応じて多くのタスクを処理できます。私の推測では、1つのパーティションが1つのタスクにアトミックにマップされます。そして、その量によってリソースの使用量が決まります。 Sparkは、1つのパーティションが大きくなりすぎるとスライスできませんでした。
合理的な戦略は、ノードとコンテナメモリを最初に決定することです(10GBまたは5GB)。理想的には、両方が時間の問題で、あらゆるデータ処理ジョブに役立つことができます。 5GBのメモリ設定を考えると、1つのパーティションの妥当な行は、たとえばテスト後は1000(処理中にステップが失敗しない)であるため、次の擬似コードとして実行できます。
RWS_PER_PARTITION = 1000
input_df = spark.write.csv("file_uri", *other_args)
total_rows = input_df.count()
original_num_partitions = input_df.getNumPartitions()
numPartitions = max(total_rows/RWS_PER_PARTITION, original_num_partitions)
input_df = input_df.repartition(numPartitions)
それが役に立てば幸い!
spark 2.3.1で比較的小さなジョブを実行している小さなクラスターでも同じ問題が発生しました。ジョブは寄木細工のファイルを読み取り、groupBy/agg/firstを使用して重複を削除してから、新しい寄木細工を並べ替えて書き込みます。 4ノード(4 vcore、32Gb RAM)で51 GBの寄木細工ファイルを処理しました。
ジョブは集約段階で常に失敗していました。 bashスクリプトウォッチエグゼキューターのメモリ使用量を書いたところ、ステージの途中で1つのランダムなエグゼキューターが数秒間ダブルメモリを使用し始めることがわかりました。この瞬間の時間をGCログと相関させると、大量のメモリを空にするフルGCと一致します。
最後に、この問題が何らかの形でGCに関係していることを理解しました。 ParallelGCとG1はこの問題を常に引き起こしますが、ConcMarkSweepGCは状況を改善します。この問題は、少量のパーティションでのみ発生します。 OpenJDK 64-Bit (build 25.171-b10)
がインストールされているEMRでジョブを実行しました。問題の根本原因はわかりませんが、JVMまたはオペレーティングシステムに関連している可能性があります。しかし、私の場合、これは明らかにヒープまたはオフヒープの使用とは関係ありません。
PDATE1
Oracle HotSpotを試したところ、問題が再現されました。