Spark를 YARN에 수행할 때 메모리 세팅 추천 #1
<개요>
YARN은 현재 CPU 와 Memory에 대해서만 스케쥴링을 수행한다.
따라서 Spark를 YARN Cluster 혹은 Client로 수행할때에도 해당 리소스의 제어를 받게 되는데 이 때 최적의 세팅값을 찾는 법을 확인해본다.
<내용>
<그림 1 : Spark와 Yarn Containter간의 메모리 관계>
A. 위의 그림을 참고로 해서 Spark와 YARN Configuration값을 살펴보면
1) Sparak Executor
--executor-cores 와 spark.executor.cores 는 Spark Executor가 동시에 수행할 수 있는 tasks의 수이다.
--executor-memory 와 spark.executor.memory 는 Spark Executor의 heap size이다.
세팅된 두 값은 모든 Spark Executor가 같은 값으로 동작한다.
2) num of executors
--num-executors 와 spark.executor.instances 는 Spark Executor의 수를 의미하지만
spark.dynamicAllocation.enabled 옵션을 통해서 동적으로 제어된다. (CDH 5.4/Spark 1.3)
3) YARN
yarn.nodemanager.resource.memory-mb 은 각 노드에서 컨테이너들이 사용할 수 있는 메모리의 총합이고
yarn.nodemanager.resource.cpu-vcores 은 각 노드에서 컨테이너들이 사용할 수 있는 core의 총합이다.
4) 보정수치
각 컴포넌트의 관계로 인해서 각 설정값은 정확히 일치할 수 없고 여유의 값을 두어야 한다. 예를 들면 다음과 같다.
- --executor-memory/spark.executor.memory 는 executor의 heap size를 결정하지만 JVM은 off heap 부분에 대해서도 사용을 하기 때문에 Yarn container는 이를 감안하여 공간을 설정해야 한다. spark.yarn.executor.memoryOverhead 값을 통해서 추가공간을 확보하며 default 는 0.07 * spark.executor.memory 이다
- yarn.scheduler.minimum-allocation-mb 과 yarn.scheduler.increment-allocation-mb 를 통해서
B. 예제를 통해서 살펴보자
각각 16 cores 와 64GB 의 메모리를 가진 노드가 6개 있는 cluster 인경우
yarn.nodemanager.resource.memory-mb = 63 * 1024
yarn.nodemanager.resource.cpu-vcores = 15
를 사용할 수 있다.
Hadoop에서 자체적으로 띄우는 데몬들이나 OS에서 사용하는 리소스들이 있기 때문에 최소 1G와 1개의 Core는 남겨둔 상태이며 필요에 따라서는 여유를 더 많이 둬야 한다.
두가지 설정값을 예시로 비교해보면
1) --num-executors 6 --executor-cores 15 --executor-memory 63G 로 세팅한다면??
단순히 생각하면 이상적인 수치이다. 각 노드에서 Executor를 하나씩 띄우고,
각 Executor는 63G의 메모리와 15 core를 Full로 사용해서 작업한다.
그러나 다음과 같은 이유로 사실은 비효율적인 세팅이 된다.
- 그림1에서 보는 것처럼 executor의 메모리는 Yarn Nodemanager resource memory 와 Container 보다 작아야 한다.
- AM이 기동되어야 하기 때문에 해당노드에서는 executor가 15 cores 를 사용할 수 없다.
- executor가 15 cores를 사용할 경우 HDFS I/O로 인해 성능의 병목이 생긴다.
2) --num-executors 17 --executor-cores 5 --executor-memory 19G 가 더 나을 듯 하다.
이유는 다음과 같다.
- 각 노드에서 3개씩 executors가 기동되고 하나의 노드에서는 2개가 기동되어 AM을 위한 공간이 확보된다.
- 각 노드에서 3개의 executors가 기동되기 때문에 63 / 3 = 21 이 되며 위에서 설명한 여유공간을 위해서 21 * 0.07 = 1.47 을 제외한 만큼 19G로 executor-memory 로 설정한다.
<기타>
실제로 YARN Application에서 모니터링을 해보면 spark.executor.memory로 설정한 값을 모두 사용하지는 않는다. 그 이유는 그림1에서 보는 것처럼 fraction수치를 코드내에서 곱해주도록 되어있기 때문이다.
<참고>
http://blog.cloudera.com/blog/2015/03/how-to-tune-your-apache-spark-jobs-part-2/