pile·
인프라 / DevOps·토스 SLASH토스 SLASH·

Spark Connect on Kubernetes #1: 견고한 Spark Connect 만들기

토스증권 Data Infra팀이 Spark Connect를 Kubernetes 위에서 멀티테넌트 서비스로 운영하며 맞닥뜨린 구조적 문제 3가지(Driver SPOF, 리소스 경합, 고정 스케일)와 그 해결책을 다룬다. 단일 SparkContext를 공유하는 구조에서 한 사용자의 OOM 쿼리가 전체 세션을 종료하는 문제를, Executor 실패 카운터 재설계와 멀티 Replica 아키텍처로 극복했다.

핵심 포인트
  • Spark Connect는 Driver를 미리 띄운 gRPC 서버로 전환해 클라이언트를 경량화하지만, 모든 세션이 SparkContext를 공유해 단일 장애점이 생긴다.
  • spark.executor.maxNumFailures는 글로벌 카운터라 한 사용자의 실패가 타 세션까지 종료시킨다 — failuresValidityInterval과 쌍으로 설정해 범위를 쿼리 단위로 좁혔다.
  • 멀티 Replica로 장애 범위를 한 대로 제한하고, 자체 Gateway가 EWMA 기반 부하 점수로 새 세션을 배분한다.
  • Istio consistent hash는 새 세션이 바쁜 서버에 계속 배치되는 문제가 있어 상태 인식 Gateway를 직접 개발했다.
  • Control Plane(Kubernetes Operator)과 Data Plane(Gateway)을 Redis를 통해 분리해 Operator 장애 시에도 라우팅이 계속된다.
상세 정리
  • Spark Connect 개념: Spark 3.4에 포함된 gRPC 기반 아키텍처로, 클라이언트는 DataFrame 연산을 Unresolved Logical Plan → Protocol Buffer로 인코딩해 보내고, 서버가 분석·실행 후 Arrow 형식으로 결과를 스트리밍한다.
  • SPOF 문제 원인: ExecutorPodsLifecycleManager가 numFailedExecutors > maxNumFailures 조건 충족 시 sys.exit(11) 호출 — 기본값은 max(3, 2×executor수)이고 시간 경과에도 자동 초기화되지 않는다.
  • SPOF 해결: spark.executor.maxNumFailures를 사실상 무한대로 올리고, failuresValidityInterval로 누적 실패 기록을 주기 초기화하며, spark.task.maxFailures와 spark.stage.maxConsecutiveAttempts로 개별 쿼리를 빠르게 종료시킨다.
  • Fair Scheduler 한계: Spark Connect에서는 spark.scheduler.pool이 thread-local로 설정되지만 클라이언트-서버 분리 탓에 서버 실행 스레드에 전파되지 않아 Pool 격리가 무력화된다.
  • 메모리 경합: spark.driver.maxResultSize(기본 1GB)가 쿼리 단위로 적용되므로 동시 쿼리가 많으면 Driver 메모리가 누적 압박받는다 — 동시 쿼리 수를 감안해 보수적으로 낮춰야 한다.
  • 멀티 Replica 설계: 각 Replica는 독립 SparkContext·Driver·Executor를 보유하고, Dynamic Resource Allocation과 결합해 유휴 Replica는 Driver만 유지한다.
  • Gateway 부하 점수: REST API 폴링으로 active(실행 중 Task), pending(대기 Task), maxTasks(전체 슬롯)를 수집해 0.9×utilization + 0.1×min(active_session수/cap, 1) 공식으로 산출하며, utilization은 EWMA로 순간 스파이크를 제외한다.
  • 세션 라우팅 규칙: 새 세션은 점수 최저 서버로, 기존 세션은 Driver 메모리에 상태가 있으므로 원래 서버로 고정한다.
  • Istio를 버린 이유: DestinationRule consistent hash는 동일 세션 고정은 되지만 새 세션 배치 시 서버 부하를 무시해 바쁜 서버에 계속 쏠리는 문제가 있었다.
  • Control/Data Plane 분리: Kubernetes Operator가 K8S watch로 서버 상태를 감지해 Redis에 TTL 포함 Publish하고, Gateway는 Redis만 폴링해 ServerPool을 캐시한다 — Operator 장애 시에도 라우팅이 유지된다.
  • 시리즈 예고: 2편에서 고정 스케일 해결(Control Plane, 무중단 교체), 3편에서 인증·인가 기반 사용자 식별과 데이터 접근 제어를 다룰 예정이다.
왜 읽나Spark Connect를 단순 실습이 아닌 멀티테넌트 프로덕션으로 안정 운영하려는 데이터 엔지니어·인프라팀에게 실전 설정값과 Gateway 아키텍처 레퍼런스.
토스 SLASH
토스 SLASH 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 인프라 / DevOps·vercel-blogVercel Blog·

    Vercel CLI 드라이런 배포로 실제 배포 전 구성 미리 확인하기

    Vercel CLI v54.17.2부터 vercel deploy --dry 명령으로 실제 파일 업로드 없이 배포 구성을 미리 검사할 수 있다. 프레임워크 감지 결과, 포함/제외 파일 목록, 디렉터리 크기 분포, 콘텐츠 해시까지 사전에 확인하고 나서 배포를 결정할 수 있어 의도치 않은 배포 실패를 예방한다.

    #deployment#ci-cd#vercel-cli+1