토스증권 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 아키텍처 레퍼런스.