1157 words
6 minutes
Yarn에서의 파일 지역성 문제
01. Overview
Airflow에서 SparkSubmitOperator를 통해 실행된 PySpark 애플리케이션이 로컬 개발 환경에서는 잘 동작하지만, YARN 클러스터 환경에서는 configparser.NoSectionError를 발생시키며 실패하는 현상을 다룸
02. 분산 환경의 파일 지역성 (File Locality)
- Driver (Airflow): 작업을 지시하는 곳. 여기에만 설정 파일(
.ini)이 존재 - Executor (YARN Node): 실제 연산을 수행하는 곳. Driver와는 물리적으로 분리된 서버이며, 별도의 조치 없이는 Driver의 로컬 파일을 볼 수 없음
03. 선행지식
- Apache Spark 아키텍처: Driver가 코드를 직렬화하여 Executor로 보내지만, 참조하는 외부 파일까지 자동으로 보내지는 않음
- Python
ConfigParser의 특성:read(path)메서드는 파일이 경로에 없으면 에러를 내지 않고 빈 설정을 반환(Silent Fail). 이후 값을 조회할 때 비로소 에러발생 - YARN Distributed Cache: 작업을 실행하기 위해 필요한 파일들을 클러스터 노드들에 효율적으로 배포하는 메커니즘.
04. Trade-off
설정 파일(INI, YAML), 인증서, 작은 룩업 테이블 등 코드에 하드코딩하기엔 자주 바뀌지만, 모든 노드가 공통적으로 참조해야 하는 작은 파일을 다룰 때 **YARN의 파일 배포 기능(--files)**을 사용
| 옵션 | 장점 | 단점 |
|---|---|---|
--files 사용 | 설정 변경 시 파일만 수정하면 됨(유연성). 코드가 깔끔함. | 매 실행 시 전송 발생(오버헤드). 하지만 설정 파일(KB 단위)에는 무시 가능. |
| Docker 이미지 포함 | 배포가 확실하고 불변성(Immutability) 보장. | 설정값 하나 바꾸려면 이미지를 다시 빌드해야 함(비효율). |
| HDFS/S3 절대경로 | 전송 오버헤드 없음. | 코드에 스토리지 의존성이 생김. 로컬 테스트가 번거로워짐. |
05. 파일 배포의 방식 --files vs sc.addFile()
spark-submit --files (인프라 레벨 배포)
- 동작: YARN이 컨테이너를 생성할 때, 파일을 cwd의 루트에 다운로드(또는 심볼릭 링크)
- 접근:
SparkContext필요 없음. 그냥 **파일 이름(Basename)**으로 접근. open('config.ini')(O)- 용도: 애플리케이션 시작 전부터 필요한 설정 파일, 혹은 코드가 아닌 라이브러리 레벨에서 읽어야 하는 파일
SparkContext.addFile() (코드 레벨 배포)
- 동작: Spark Driver가 실행된 후, 코드를 통해 Executor의 Spark 전용 샌드박스(임시 폴더)로 파일을 전송
- 접근: 파일이 숨겨진 경로에 있으므로 반드시 **
SparkFiles.get()**을 통해 절대 경로를 받아야 함. SparkFiles.get('config.ini')(O)- 용도: 런타임에 동적으로 결정되는 파일이나, URL을 통해 다운로드해야 하는 리소스
06. 구현 예제
Airflow DAG 수정
files 인자를 통해 로컬 경로의 INI 파일을 YARN 클러스터로 배포
spark_task = SparkSubmitOperator( task_id='spark_job', # Driver(Airflow)의 절대 경로를 지정 files='/opt/airflow/dags/cdp/sql/cdp_overview.ini',)Python
절대 경로(/opt/...)를 제거하고, 배포된 파일을 **상대 경로(파일명)**로 읽음
import configparserimport os
ini_filename = 'cdp_overview.ini'
# ConfigParser의 Silent Fail 방지if not os.path.exists(ini_filename): raise FileNotFoundError(f"Config file not found: {os.path.abspath(ini_filename)}")
config = configparser.ConfigParser()config.read(ini_filename) # 경로 없이 파일명만 사용Concept
- Silent Failure : ConfigParser.read()와 같이 에러를 발생시키지 않고 실패 상태를 숨기는 동작. 디버깅 시 “파일이 없는 문제”를 “파싱 문제”나 “논리 오류”로 오인하게 만드는 주범. 반드시
os.path.exists()등으로 방어 코드를 작성해야 함.
- Distributed Cache (분산 캐시) : 분산 시스템에서 데이터나 리소스(config, library, static asset)를 네트워크상의 여러 노드가 로컬에서 빠르게 접근할 수 있도록 복제 및 배포하는 메커니즘. 중앙 저장소(Remote)에 있는 원본을 각 작업 노드(Worker Node)의 로컬 디스크나 메모리로 가져와(Localization), 네트워크 I/O 비용을 줄이고 접근 속도를 높이는 것
- Working Directory (spark) :
spark-submit --files로 전달된 파일은 YARN 컨테이너의 cwd에 위치하므로, 코드 내에서 별도 경로 지정 없이 파일명(basename)만으로 접근 가능. - File Locality (파일 지역성) : 데이터나 파일이 연산이 수행되는 노드와 물리적으로 동일한 곳에 위치하는 성질. 분산 시스템에서는 코드는 전송되지만 참조 파일은 기본적으로 전송대상이 아님, 명시적인 배포 전략(Localization)을 수립해야 함
Yarn에서의 파일 지역성 문제
https://yjinheon.netlify.app/posts/02de/spark/de-spark-yarn-locality/