Spark at Scale

대규모 엔터프라이즈 환경에서 Apache Spark를 운영할 때의 아키텍처와 최적화 기법


핵심 개념

Apache Spark는 대규모 데이터 처리의 사실상 표준이지만, 엔터프라이즈 규모에서는 인프라 비용, 셔플 성능, 장애 복구, 멀티테넌시 등 운영 과제가 발생한다. 자동 튜닝과 인프라 최적화가 핵심이다.

Informatica CDI — Spark++ on Kubernetes

Informatica(현 Salesforce)의 대규모 데이터 통합 플랫폼:

  • 단일 노드 실행 엔진에서 Spark++ on Kubernetes로 마이그레이션
  • 그래픽 매핑을 유지하면서 분산 처리 도입
  • 행 단위 장애 격리: 개별 레코드 오류가 전체 파이프라인을 중단시키지 않음
  • 임시 VPC 바운드 클러스터: 보안 격리
  • 자동 FinOps 튜너: 과거 워크로드 기반으로 인프라와 Spark 파라미터 자동 최적화
  • 규모: 5,500 고객, 일 250,000 태스크, 인프라 비용 1.65x 절감, 컨트롤 플레인 99.9% 가용성

S3 Shuffle 최적화

S3 기반 Spark 셔플의 프로덕션 문제와 해결:

문제

  • GET 요청의 2차 스케일링 (맵 태스크 × 리듀스 태스크)
  • S3 프리픽스 스로틀링 (프리픽스당 5,500 GET/s 제한)
  • ConcurrentHashMap 레이스 컨디션으로 executor 행(hang)과 데드락

해결

  • 맵 태스크 통합(coalescing): GET 요청 수를 크게 감소
  • S3 프리픽스 확장: 10개 → 500개로 스로틀링 회피
  • 원자적 락킹: ConcurrentHashMap 기반 레이스 컨디션 제거
  • 프리페치 이터레이터 타임아웃: 데드락 방지
  • 결과: 셔플 비용 95% 절감, 스테이지당 API 비용 0

Notion Spot Balancer — Spot 인스턴스 안전한 활용

Notion이 AWS와 함께 개발한 Spot Balancer로 Spark on Kubernetes 비용을 60-90% 절감:

  • 동적 프로비저닝: Karpenter + EKS Auto Mode로 CPU/메모리 요건만 선언 후 동적 노드 할당
  • Bin-packing: MostAllocated 스케줄러로 동일 노드에 여러 잡 executor 집약
  • Spot Balancer 웹훅: executor Pod 생성을 인터셉트, 잡별 Spot/온디맨드 비율에 따라 레이블 부여
  • 잡별 RELIABILITY_OVER_COST 등 named stability weight로 트레이드오프 추상화
  • 오픈소스: https://github.com/aws-samples/sample-spot-balancer-spark-eks

Microsoft Fabric Native Execution Engine — JVM 오버헤드 제거

Velox + Apache Gluten 기반 C++ 네이티브 실행 레이어:

  • 코드 변경 없이 기존 Spark 워크로드에 적용, Spark의 모든 최적화(AQE 등) 보존
  • TPC-DS SF1000 기준 오픈소스 Spark 대비 최대 6배 성능, ~83% 컴퓨트 비용 절감
  • 자세한 내용: Columnar Execution Engine

Pinterest — Auto Memory Retries로 OOM 96% 감소

Spark OOM 장애의 대부분은 메모리 부족이 아닌 단편화와 태스크 스큐에 의해 발생:

  • 점진적 재시도 프로파일 (2x → 3x → 4x): 실패한 특정 태스크에만 적용
  • CPU 코어 2배 증가만으로 대부분의 OOM 해결 (메모리 확장 전 단계)
  • 단일 스테이지 내 이기종(heterogeneous) 리소스 프로파일의 태스크 공존
  • 일일 90,000+ Spark 작업에서 OOM 장애 96% 감소, 전체 실패의 4.6%가 OOM

Halodoc — Apache YuniKorn으로 Spark 스케줄링 최적화

K8s 기본 스케줄러의 한계를 Apache YuniKorn으로 해결한 사례:

  • 문제: Driver/Executor를 독립 Pod로 처리 → 부분 스케줄링 데드락, 멀티테넌시 리소스 쟁탈, Karpenter의 과도한 스케일아웃
  • Gang Scheduling: Driver+Executor를 단일 그룹으로 all-or-nothing 스케줄링
  • Hierarchical Queues: 큐별 리소스 상한 → 통제된 경합, 불필요한 스케일아웃 방지
  • Bin Packing: 기존 노드를 96%+ 활용 후 확장 → EC2 비용 ~10% 절감
  • 큐 초과 시 기본 K8s 스케줄러로 자동 폴백하여 무한 대기 방지

Executor 튜닝 — Fat vs Thin vs Optimal

Spark executor 크기 설정은 성능에 직접적 영향을 미치는 핵심 튜닝 파라미터다:

3가지 전략

전략특징장점단점
Fat워커당 1개, 전체 리소스 사용높은 데이터 로컬리티, 태스크 병렬성리소스 미활용, 낮은 장애 복원력
Thin최소 코어/메모리높은 장애 복원력네트워크 트래픽 증가, 낮은 로컬리티
Optimal규칙 기반 적정 크기균형잡힌 성능계산 필요

Optimal Executor 사이징 규칙

  1. 워커당 OS용 1 CPU 코어 + 1GB RAM 예약
  2. 클러스터 수준에서 리소스 관리용 1 executor(또는 1코어+1GB) 제거
  3. executor 메모리의 MAX(384MB, 10%) 를 내부 프로세스 오버헤드로 제외

연관 개념


Source: Inside Informaticas Spark-Based Data Integration Platform, Taming S3 Shuffle at Scale, Notion - Balancing Cost and Reliability for Spark on Kubernetes, Microsoft Fabric - Under the Hood - Native Execution Engine, Drastically Reducing Out-of-Memory Errors in Apache Spark at Pinterest, Implementing Apache Yunikorn on EMR on EKS at Halodoc, Spark Executor Tuning