1. Spark on YARN 개요
Hadoop의 리소스 매니저인 YARN이 Spark의 드라이버와 익스큐터를 컨테이너로 띄워주는 실행 방식임
1.1 YARN을 사용하는 방식
- 리소스 위임 : Spark가 직접 리소스를 관리하지 않고 YARN에 CPU·메모리 할당을 맡김
- master 값 : --master yarn으로 지정하며 클러스터 위치는 HADOOP_CONF_DIR의 설정 파일에서 읽어옴
- 주 사용처 : 온프레미스 Hadoop 환경, AWS EMR 등
YARN은 Spark 전용 매니저가 아니라 Hadoop 클러스터 전체의 범용 리소스 매니저이므로 Hive, MapReduce, Spark 작업이 같은 클러스터를 공유함
2. YARN 구성 요소
ResourceManager, NodeManager, ApplicationMaster, Container 네 가지가 핵심 구성 요소임
2.1 구성 요소별 역할
| 구성 요소 |
위치 |
역할 |
| ResourceManager (RM) |
마스터 노드 |
클러스터 전체 리소스를 관리하고 컨테이너를 할당함 |
| NodeManager (NM) |
워커 노드 |
노드의 컨테이너를 실행하고 리소스 사용량을 모니터링함 |
| ApplicationMaster (AM) |
컨테이너 내부 |
애플리케이션별로 하나씩 뜨며 RM에 리소스를 요청하고 작업을 조율함 |
| Container |
워커 노드 |
CPU·메모리가 할당된 실행 단위이며 Spark 익스큐터가 여기서 동작함 |
2.2 ResourceManager 내부
- Scheduler : 큐 정책에 따라 컨테이너를 할당하며 실제 작업 모니터링은 하지 않음
- ApplicationsManager : 애플리케이션 제출을 받고 첫 번째 컨테이너(AM)를 띄우며 AM 실패 시 재시작을 담당함
3. 실행 흐름
spark-submit 제출부터 익스큐터 실행, 종료까지의 과정임
3.1 cluster 모드
- spark-submit --master yarn --deploy-mode cluster로 제출함
- 클라이언트가 RM에 애플리케이션을 등록하고 Spark jar와 설정을 HDFS에 업로드함
- RM이 NodeManager 한 곳에 첫 컨테이너를 할당하고 ApplicationMaster가 실행됨
- Spark 드라이버가 ApplicationMaster 안에서 실행됨
- AM이 RM에 익스큐터용 컨테이너를 요청함
- 각 NodeManager에서 익스큐터가 실행되어 드라이버와 통신하며 태스크를 처리함
- 작업이 끝나면 컨테이너가 반환되고 애플리케이션이 종료됨
3.2 client 모드
- 드라이버 위치 : spark-submit을 실행한 머신에서 실행됨
- AM 역할 : 드라이버 실행 없이 익스큐터 리소스 요청만 담당함
- 네트워크 조건 : 익스큐터가 드라이버로 접속해야 하므로 클러스터에서 제출 머신으로의 통신이 열려 있어야 함
3.3 client 모드와 cluster 모드 비교
| 구분 |
client |
cluster |
| 드라이버 위치 |
제출 머신 |
클러스터 내부 (AM 컨테이너) |
| AM 역할 |
리소스 요청만 |
드라이버 실행 + 리소스 요청 |
| 로그 확인 |
터미널에 바로 출력됨 |
yarn logs -applicationId <id> |
| 제출 머신 종료 시 |
드라이버가 죽어 작업 실패 |
영향 없음 |
| 용도 |
spark-shell, 노트북, 디버깅 |
운영 배치 작업 |
4. 리소스 설정
익스큐터 수, 코어, 메모리 설정과 YARN 컨테이너 크기의 관계를 이해해야 함
4.1 제출 옵션
spark-submit \
--master yarn \
--deploy-mode cluster \
--queue etl \
--num-executors 10 \
--executor-cores 4 \
--executor-memory 8g \
--driver-memory 4g \
app.py
- --num-executors : 요청할 익스큐터 수
- --executor-cores : 익스큐터당 코어 수
- --executor-memory : 익스큐터 JVM 힙 크기
- --queue : 사용할 YARN 큐
4.2 컨테이너 메모리 구조
- 컨테이너 크기 : executor-memory + memoryOverhead의 합으로 YARN에 요청됨
- spark.executor.memoryOverhead : 힙 외 메모리(off-heap, 스레드 스택, 파이썬 프로세스 등)이며 기본값은 힙의 10%(최소 384MB)임
- PySpark 사용 시 : 파이썬 워커 메모리가 overhead에 포함되어 부족해지기 쉬우므로 값을 여유 있게 잡아야 함
- YARN 상한 : 요청한 컨테이너 크기가 yarn.scheduler.maximum-allocation-mb를 넘으면 제출 단계에서 실패함
| YARN 설정 |
의미 |
| yarn.nodemanager.resource.memory-mb |
노드 하나가 컨테이너에 내줄 수 있는 총 메모리 |
| yarn.scheduler.minimum-allocation-mb |
컨테이너 최소 할당 단위 (요청값이 이 단위로 올림됨) |
| yarn.scheduler.maximum-allocation-mb |
컨테이너 하나의 최대 크기 |
컨테이너 크기는 최소 할당 단위의 배수로 올림되므로 요청한 값보다 실제 점유 메모리가 더 클 수 있음
5. 스케줄러와 큐
YARN은 스케줄러 정책에 따라 여러 팀과 작업이 클러스터를 나눠 쓰도록 제어함
5.1 스케줄러 종류
| 스케줄러 |
특징 |
| FIFO |
먼저 제출된 작업부터 처리함. 단순하지만 공유 환경에는 부적합 |
| Capacity |
큐별로 용량을 보장하고 남는 리소스를 다른 큐가 빌려 쓸 수 있음. Apache Hadoop의 기본값 |
| Fair |
실행 중인 애플리케이션에 리소스를 균등하게 분배함 |
5.2 큐 운영
- 멀티 테넌시 : 팀이나 워크로드별로 큐를 나눠 리소스를 격리함
- ACCEPTED 상태 지속 : 큐에 여유 리소스가 없을 때 발생하며 작업이 RUNNING으로 넘어가지 못하고 대기함
- Preemption : 보장 용량을 넘겨 쓰는 큐에서 컨테이너를 회수해 다른 큐에 돌려주는 기능임
6. Dynamic Allocation
부하에 따라 익스큐터 수를 자동으로 늘리고 줄이는 기능임
6.1 설정
- spark.dynamicAllocation.enabled : 기능 활성화 여부
- minExecutors / maxExecutors : 익스큐터 수의 하한과 상한
- 셔플 데이터 보존 : 익스큐터가 제거되어도 셔플 파일을 읽을 수 있어야 하므로 아래 방식 중 하나가 필요함
| 방식 |
설명 |
| External Shuffle Service |
NodeManager의 aux-service로 셔플 파일을 대신 서빙함 (spark.shuffle.service.enabled=true) |
| Shuffle Tracking |
셔플 데이터를 가진 익스큐터를 유지하는 방식 (spark.dynamicAllocation.shuffleTracking.enabled=true, Spark 3.0 이상) |
7. 의존성 배포
클러스터 노드에 Spark와 파이썬 라이브러리가 없어도 실행되도록 패키징해서 전달함
7.1 방법
- spark.yarn.archive / spark.yarn.jars : Spark 라이브러리를 HDFS에 미리 올려 두면 제출할 때마다 업로드하지 않아 시작이 빨라짐
- --py-files : 파이썬 모듈이나 zip 파일을 함께 배포함
- --archives : venv-pack 등으로 만든 가상환경 압축 파일을 배포해 라이브러리 버전을 고정함
- --jars / --packages : 추가 JAR 의존성을 지정함
모든 노드에 같은 라이브러리를 설치하는 방식은 관리가 어려워서 가상환경 아카이브로 배포하는 쪽이 일반적임
8. 모니터링과 로그
애플리케이션 상태와 로그는 YARN 명령어와 웹 UI로 확인함
8.1 상태 확인과 종료
yarn application -list
yarn application -status <applicationId>
yarn application -kill <applicationId>
8.2 로그 확인
- yarn logs : yarn logs -applicationId <id>로 드라이버와 익스큐터 로그를 모아서 봄
- 로그 집계 : yarn.log-aggregation-enable=true여야 종료 후에도 로그가 HDFS에 보관됨
- Spark UI : 실행 중에는 RM 웹 UI의 ApplicationMaster 링크로 접근함
- Spark History Server : 종료된 애플리케이션의 이벤트 로그를 기반으로 UI를 다시 보여줌
8.3 애플리케이션 상태
| 상태 |
의미 |
| ACCEPTED |
RM이 접수했으나 AM 컨테이너를 아직 못 받은 상태 |
| RUNNING |
AM이 실행 중인 상태 |
| FINISHED |
정상 종료 (최종 상태는 SUCCEEDED / FAILED로 구분됨) |
| KILLED |
사용자 또는 관리자가 강제 종료한 상태 |
9. 자주 만나는 문제
메모리 초과와 리소스 대기가 가장 흔한 장애 유형임
9.1 문제별 원인과 해결
| 증상 |
원인 |
해결 |
| Container killed by YARN for exceeding memory limits |
힙 외 메모리 사용량이 컨테이너 한도 초과 |
memoryOverhead 증가 |
| ACCEPTED 상태에서 진행 안 됨 |
큐 또는 클러스터 리소스 부족 |
큐 상태 확인, 요청 리소스 축소 |
| 제출 단계 실패 (Required executor memory is above the max threshold) |
요청 크기가 maximum-allocation-mb 초과 |
메모리 요청 축소 또는 YARN 설정 상향 |
| AM 반복 실패 |
드라이버 오류 또는 AM 컨테이너 종료 |
yarn logs 확인, spark.yarn.maxAppAttempts 조정 |
9.2 AM 재시도
- spark.yarn.maxAppAttempts : AM 실패 시 재시도 횟수이며 기본값은 YARN 설정(yarn.resourcemanager.am.max-attempts)을 따름
- 주의점 : 재시도되면 애플리케이션 전체가 처음부터 다시 실행되므로 멱등성이 없는 작업은 별도 처리가 필요함
10. 장단점
기존 Hadoop 자산을 활용하는 환경에서는 안정적이지만 클라우드 네이티브 환경에서는 운영 부담이 있음
| 장점 |
단점 |
| 기존 Hadoop 클러스터와 리소스를 공유함 |
Hadoop 환경을 따로 구축·운영해야 함 |
| 큐 기반 멀티 테넌시와 자원 격리가 성숙함 |
의존성 패키징이 컨테이너 이미지 방식보다 불편함 |
| HDFS와 같은 노드에서 돌아 데이터 로컬리티에 유리함 |
클라우드 네이티브 환경에서는 Kubernetes로 이동하는 추세임 |
요약
| 항목 |
핵심 내용 |
| 구조 |
RM이 컨테이너를 할당하고 AM이 익스큐터를 조율함 |
| 배포 모드 |
운영은 cluster, 대화형 작업은 client |
| 메모리 |
컨테이너 크기는 executor-memory + memoryOverhead이며 YARN 상한을 넘으면 제출 실패 |
| 리소스 공유 |
Capacity/Fair 스케줄러와 큐로 팀 간 자원을 격리함 |
| Dynamic Allocation |
External Shuffle Service 또는 Shuffle Tracking 필요 |
| 로그 |
로그 집계를 켜고 yarn logs로 확인함 |
| 대표 장애 |
memoryOverhead 부족, 큐 리소스 부족(ACCEPTED 지속) |
Spark on YARN은 RM과 AM이 컨테이너 단위로 리소스를 할당하는 구조이며, 컨테이너 메모리 구성과 큐 설정을 이해하면 대부분의 장애를 해결할 수 있음