Kubernetes에서 Spark Connect 안정성 높이기
분석가와 엔지니어가 복잡한 설정 없이 Spark를 사용하도록 Kubernetes에서 Spark Connect를 서비스로 운영하며 안정성 확보를 최우선 목표로 삼음
Spark Connect는 Driver를 애플리케이션마다 새로 띄우는 대신 미리 띄워둔 서버로 분리하여 가벼운 클라이언트와 즉시 세션 생성 이점 제공
공유 Driver의 단일 장애점(SPOF)과 리소스 경합 문제를 해결하기 위해 글로벌 Executor 실패 카운터 비활성화 및 Replica 기반 고가용성(HA) 구성을 적용함
새 세션은 부하 기반 라우팅으로 한가한 서버에 배치하고, 기존 세션은 세션 고정(Session Affinity)으로 원래 서버에 유지하여 리소스 경합 문제 해결
Gateway는 Kubernetes API를 직접 보지 않고 Redis를 통해 서버 상태를 파악하여 안정적인 라우팅을 보장함
Spark Connect 아키텍처와 기존 Spark의 차이점
기존 Spark는 Driver와 Executor가 애플리케이션 생명주기와 함께 생성 및 소멸되는 클라이언트/클러스터 모드를 사용했습니다. 이 방식은 애플리케이션마다 새로운 Driver를 띄워야 하므로 초기화 오버헤드(Initialization Overhead)가 크고, 클라이언트 환경에 Spark 라이브러리 및 JVM 의존성이 요구되었습니다. 반면 Spark Connect는 미리 상주하는 서버형 Driver를 통해 이 문제를 해결합니다. 클라이언트는 DataFrame/SQL 연산을 Protocol Buffer로 인코딩하여 gRPC로 전송하고, 서버가 이를 받아 분석·최적화·실행을 담당합니다. 이 서버-클라이언트 분리(Server-Client Separation)는 클라이언트의 경량화, 언어/플랫폼 독립성, 즉시 세션 생성, 클라이언트 장애 내성 확보라는 이점을 제공합니다.
공유 Driver의 단일 장애점(SPOF) 극복 전략
단일 Spark Connect 서버에 여러 세션이 붙을 때 발생하는 단일 장애점(Single Point of Failure, SPOF) 문제를 해결하기 위해, 먼저 글로벌 Executor 실패 카운터(Global Executor Failure Counter)를 비활성화하여 개별 쿼리 실패가 전체 서버 종료로 이어지는 것을 방지했습니다. `spark.executor.maxNumFailures`를 사실상 무한대로 설정하고 `spark.executor.failuresValidityInterval`을 통해 실패 기록을 주기적으로 초기화합니다. 또한, `spark.task.maxFailures`와 `spark.stage.maxConsecutiveAttempts`를 조정하여 문제 쿼리만 빠르게 종료시키고, Replica 기반의 고가용성(High Availability, HA) 구성을 통해 특정 서버 장애 시에도 다른 Replica가 세션을 이어받도록 설계했습니다.
리소스 경합 및 스케줄링 한계 해결 방안
여러 세션이 공유하는 Spark Connect 서버에서 발생하는 리소스 경합(Resource Contention) 문제는 Driver의 기본 FIFO 스케줄링 정책(Default FIFO Scheduling Policy)과 CPU·메모리 격리 부재에서 기인합니다. 이를 해결하기 위해, Spark Connect 서버는 사용자별 스케줄러 풀(Scheduler Pool)을 직접 할당하여 쿼리 우선순위를 관리합니다. 또한, CPU·메모리 격리를 위해 Gateway에서 부하 기반 라우팅(Load-based Routing)을 구현했습니다. 새 세션은 활성 Task 수, 대기 Task 수, 총 Task 슬롯 용량(Utilization) 및 활성 세션 수를 기반으로 산출된 부하 점수가 가장 낮은 서버에 배치됩니다. 반면, 기존 세션은 세션 고정(Session Affinity)을 통해 원래 서버로만 요청을 보내 안정적인 세션 유지를 보장합니다.
Gateway 기반의 지능형 라우팅 구현
기존 Istio의 `consistent-hash` 로드밸런싱은 세션 고정은 가능했으나, 새 세션을 서버 부하(Server Load)에 따라 동적으로 배치하는 기능이 부족했습니다. 이를 해결하기 위해, Spark Connect 서버의 부하 상태(active, pending tasks, maxTasks)를 REST API로 폴링하고 지수 가중 이동 평균(EWMA)으로 정제하여 부하 점수를 산출하는 커스텀 Gateway를 개발했습니다. Gateway는 Kubernetes API를 직접 호출하는 대신, Controller가 관리하는 Redis의 서버 상태 정보를 주기적으로 폴링하여 ServerPool을 유지합니다. 이 아키텍처는 Data Plane(Gateway)과 Control Plane(Controller)의 역할을 명확히 분리하여 안정적인 라우팅을 보장하고 Gateway의 복잡성을 낮춥니다.
Spark Connect 운영을 위한 핵심 설정값 조정
멀티세션 환경에서 Spark Connect를 안정적으로 운영하기 위해 기존 Spark의 기본 설정을 조정했습니다. 특히 `spark.driver.maxResultSize`를 쿼리 단위가 아닌 서버 전체의 위험을 고려하여 보수적으로 설정하고, `spark.executor.maxNumFailures`를 비활성화하여 개별 실패가 전체 서버를 중단시키지 않도록 했습니다. 또한, `spark.task.maxFailures`와 `spark.stage.maxConsecutiveAttempts`를 조정하여 문제 쿼리를 신속하게 종료시키고, `spark.sql.autoBroadcastJoinThreshold`와 같은 값들도 워크로드 특성에 맞게 최적화했습니다. 이러한 설정 조정은 안정적인 서비스 운영과 리소스 효율성 증대에 기여합니다.