Apache Spark를 사용하면서 Driver와 Executor의 역할을 명확히 이해하는 것은 매우 중요합니다. 특히 YARN을 클러스터 매니저로 사용할 때, Driver와 Executor가 어디에서 실행되는지, 어떤 코드가 어디에서 수행되는지 혼동하기 쉽습니다. 이번 글에서는 Driver와 Executor의 역할에 대해서 복습하고 Spark on YARN의 실행 구조와 환경변수를 올바르게 설정하는 방법까지 정리해보겠습니다.
Spark의 핵심 컴포넌트: Driver와 Executor
Driver란?
Driver는 Spark 애플리케이션을 관리하는 컨트롤러 역할을 합니다. 사용자가 spark-submit을 실행하면 Driver가 시작되며, 다음과 같은 역할을 수행합니다.
- SparkSession (SparkContext) 생성: 애플리케이션이 Spark 클러스터와 통신할 수 있도록 함.
- Job 및 Task 스케줄링: 실행할 연산을 DAG(Directed Acyclic Graph)로 변환하여 실행 계획을 수립.
- Executors와 통신: Master(YARN이나 Kubernetes)에서 NodeManager에 Executor를 요청하고, 실행할 작업을 분배.
- 결과 수집 및 반환: Executor에서 연산을 수행한 후 최종 결과를 수집하여 사용자에게 반환.
📌Driver는 단순히 애플리케이션을 시작하는 것이 아니라, 애플리케이션 실행이 끝날 때까지 Executor와 지속적으로 통신하며 작업을 조율하는 역할을 합니다.
Executor란?
Executor는 실제 데이터를 처리하는 Spark의 워커 프로세스입니다. Driver의 요청을 받아 연산을 수행하며, 다음과 같은 역할을 합니다.
- 데이터 연산 수행: filter(), join(), groupBy() 등의 Transformation을 실행.
- Task 실행 및 결과 반환: Driver가 분배한 Task를 실행하고, 결과를 Driver에게 전달.
- 캐싱 및 데이터 저장: persist(), cache()를 사용하여 데이터 저장 및 재사용.
📌 Executor는 독립적인 프로세스로 실행되며, Driver의 지시에 따라 데이터를 병렬로 처리합니다.
Executor의 스케줄링 및 모니터링
Executor의 실행과 관리는 **Cluster Manager(클러스터 매니저)**가 담당합니다. Spark는 여러 종류의 Cluster Manager를 지원합니다.
- YARN: Hadoop 기반 클러스터 매니저로, 리소스 할당 및 Executor 배포를 담당합니다.
- Kubernetes(K8s): 컨테이너 기반의 클러스터 매니저로, Executor를 Pod로 실행합니다.
- Standalone: Spark 자체 제공 클러스터 매니저로, 작은 규모의 클러스터에 적합합니다.
📌 Cluster Manager는 Executors를 할당하고 스케줄링하지만, Driver는 Executor의 Task 실행을 관리하고 결과를 모니터링합니다.
Driver가 실행되는 위치
--master 옵션에 따라 Driver 실행 위치가 달라집니다.
--master 설정 예시 | Driver 실행 위치 |
local[N] | 로컬 머신 |
spark://<master-ip>:7077 | Client 또는 Cluster |
yarn | Client 또는 Cluster |
k8s://<k8s-api-server> | Cluster |
--master 옵션을 yarn으로 설정한다면 Application이 실행되는 방식은 크게 두 가지입니다.
Cluster 모드 (--deploy-mode=cluster)
- Driver가 YARN 클러스터에서 실행됨.
- 사용자가 spark-submit을 실행하면 YARN이 새로운 컨테이너를 생성하고 Driver를 실행합니다.
- Driver가 Executor를 요청하고, YARN은 여러 Worker 노드에서 Executors를 실행합니다.
Client 모드 (--deploy-mode=client)
- Driver가 사용자의 로컬 머신(또는 제출한 노드)에서 실행됨.
- YARN은 Executor만 클러스터에 배치하고, Driver는 외부에서 실행되므로 네트워크 연결이 중요합니다.
- Pod 내부에서 Spark Application의 로깅과 상태를 직접 관리하고자 하는 경우 사용될 수 있습니다.
- 만약 로컬 머신의 리소스가 부족할 경우 Driver가 OOM(Out of Memory)로 종료될 위험이 있습니다.
cluster에서 driver가 수행이 선호되는 이유는 cluster 모드에서는 Driver가 YARN 클러스터에서 실행되므로 안정적입니다. 또한 클러스터에서 작업을 스케줄링하기 때문에, 작업 노드에 가깝게 실행되는 것이 좋으며, 가급적이면 동일한 로컬 영역 네트워크상에서 실행되는 것이 권장됩니다.
Driver와 Executor에서 실행되는 코드 구분하기
Spark Application에서 코드가 Driver에서 실행되는지, Executor에서 실행되는지 구분하는 것이 매우 중요합니다.
Driver에서 실행되는 코드
- Python 일반 실행
- Spark Action 함수
코드 유형 | 설명 |
SparkSession.builder.getOrCreate() | Spark 애플리케이션 초기화 |
if, for, print() 같은 일반 Python 코드 | 실행 흐름을 제어하는 로직 |
df.show(), df.count(), df.collect() | 최종 결과를 수집하는 코드 |
os.environ.get() | 환경변수를 직접 참조하는 코드 |
spark.read.csv() | 파일을 읽어들이는 작업 (메타데이터 분석은 Driver에서 수행됨) |
from pyspark.sql import SparkSession
import os
spark = SparkSession.builder.appName("MySparkApplication").getOrCreate()
spark.read.parquet("hdfs:///my_data")
if os.environ.get("custom_value") == "test":
print("Test ETL result")
Executor에서 실행되는 코드
- Spark Transformation 함수
코드 유형 | 설명 |
df.filter(), df.groupBy(), df.agg() | DataFrame 변환 (Transformations) |
rdd.map(), rdd.reduce() | RDD 연산 |
df.write.parquet() | 데이터를 저장하는 연산 |
UDF(User Defined Function) 실행 | 데이터 처리를 위한 사용자 정의 함수 |
df = spark.read.parquet("hdfs://namenode:8020/my_data") # Driver에서 실행
filtered_df = df.filter(df.age > 30) # Executor에서 실행
result = filtered_df.groupBy("city").count() # Executor에서 실행
result.write.parquet("hdfs://namenode:8020/result") # Executor에서 실행
- Point
- Python의 일반적인 실행 흐름 (if, for, print())은 Driver에서 실행.
- Spark Transformation(filter(), join(), groupBy())는 Executor에서 실행.
- show(), collect() 등 결과를 가져오는 액션은 Driver에서 실행.
환경변수 전달에 대한 삽질과 복습
Spark 애플리케이션을 실행할 때, Driver에서 실행되는 로직이 환경변수를 어떻게 참조하는지를 명확히 이해하는 것이 중요합니다. 특히, if 문과 같은 분기 로직은 Driver에서 실행되므로 환경변수는 Driver에서 접근할 수 있도록 설정해야 합니다.
환경변수 설정 방식 정리
Driver가 YARN에서 실행될 때 (--deploy-mode cluster)
- Driver는 YARN의 Application Master 역할을 수행하며, YARN에서 실행됨.
- 따라서, 환경변수는 spark.yarn.appMasterEnv.<변수명> 옵션을 통해 설정해야 함.
환경변수 설정 방법
spark-submit \
--master yarn \
--deploy-mode cluster \
--conf spark.yarn.appMasterEnv.custom_value="etl" \
my_spark_application.py
🔨 삽질 포인트
Pod 내부에서 Kubernetes 환경변수를 (K8SVars) 설정해두고 spark-submit 커맨드로 Job을 제출했는데, Spark 애플리케이션에서 custom_value 환경변수가 None으로 처리되는 점이 예상과 달랐습니다.
➡ 원인: Driver는 YARN 클러스터에서 실행되기 때문에, Pod에서 설정한 환경변수를 참조할 수 없습니다.
➡ 해결 방법: spark.yarn.appMasterEnv를 사용하여 환경변수를 YARN에 추가
Driver가 로컬에서 실행될 때 (--deploy-mode client)
- Application Master는 YARN이 관리하는 작은 프로세스로 생성됨.
- 환경변수는 Driver가 실행되는 로컬 환경에서 직접 설정해야 함.
환경변수 설정 방법
export CUSTOM_VALUE=test
spark-submit \
--master yarn \
--deploy-mode client \
my_spark_application.py
📌 핵심 정리:
- Driver가 YARN에서 실행되면 spark.yarn.appMasterEnv를 사용해야 함.
- Driver가 로컬에서 실행되면 일반 환경변수 (export VAR=VALUE) 설정이 필요함.
마무리
Spark의 실행 구조를 제대로 이해하지 못하면 불필요한 시행착오를 겪을 수 있습니다. 환경변수를 설정하는 과정에서 Driver와 Executor의 실행 위치를 확인하며 Spark의 동작 방식을 다시 복습하고 글을 작성하게 되었습니다.
Driver는 단순한 컨트롤러가 아니라, Executors와 지속적으로 통신하며 작업을 조율하고 최종 결과를 반환하는 핵심 역할을 수행합니다. 반면, Cluster Manager는 Executors의 스케줄링과 리소스 할당을 담당하지만, 개별 Task의 실행을 직접 제어하지는 않습니다.
이러한 구조를 명확히 이해하면, 환경변수를 올바르게 설정하는 방법도 자연스럽게 정리됩니다. Spark Job 설정에서 어떤 옵션값이 어느 컴포넌트에 적용되는지 더 잘 파악할 수 있었습니다.
'DataEngineering' 카테고리의 다른 글
dbt sql modeling (0) | 2025.03.02 |
---|---|
Trino 아키텍처와 에러 파악하기 (3) | 2024.11.10 |
Spark tuning (0) | 2024.03.17 |
[Spark] Spark run in Cluster Mode-YARN (1) | 2024.02.04 |
[Airflow] Operators (0) | 2024.01.21 |