본문으로 이동
문서

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

박지원 · 토스 · 토스 기술 블로그

원본 보기

소개

Spark Connect 서버에 세션이 몰리면, 무거운 작업이 다른 사용자까지 느리게 만들고 그 서버가 죽는 순간 모두가 실패합니다. 이 문제들을 어떻게 풀었는지 공유합니다.

AI 핵심 요약

여러 사용자가 하나의 장기 실행 Spark Connect 서버를 공유하면 Executor 실패가 누적돼 모든 세션이 종료되거나, 무거운 작업이 Task 슬롯을 점유해 다른 사용자의 쿼리까지 지연될 수 있습니다. 이를 줄이기 위해 글로벌 Executor 실패 종료 조건을 사실상 비활성화하고 Task·Stage 단위 실패 처리와 `spark.driver.maxResultSize`를 조정해 문제 쿼리의 영향을 제한했습니다. 남는 장애와 리소스 경합은 각자 SparkContext를 가진 여러 Replica로 분산하고, Spark REST API의 active·pending Task와 슬롯 수를 바탕으로 부하를 계산하는 Gateway를 만들어 새 세션은 한가한 서버에, 기존 세션은 원래 서버에 라우팅했습니다. Controller가 Kubernetes에서 파악한 서버 상태를 Redis에 게시하고 Gateway가 이를 캐싱하도록 역할을 나눠, Spark Connect 운영에서는 기본 Spark 설정의 전제를 재검토하고 세션 고정과 부하 기반 배치를 함께 설계해야 한다는 점을 보여줍니다.

  • Spark Connect의 장기 실행 서버에서는 앱 전체의 Executor 실패 카운터가 여러 세션의 실패를 합산하므로, 글로벌 종료 조건과 Task·Stage 단위 재시도 설정을 함께 점검해야 합니다.
  • spark.driver.maxResultSize는 쿼리별 한도이므로, 동시 쿼리가 많다면 Driver 메모리를 고려해 보수적으로 설정해야 합니다.
  • Fair Scheduler는 Task 슬롯 배정 순서를 조정할 뿐 CPU·메모리를 격리하지 않으며, Spark Connect에서는 요청 실행 스레드에 Scheduler Pool을 직접 설정해야 할 수 있습니다.
  • 세션 고정과 신규 세션의 부하 분산은 별개의 문제입니다. 기존 세션은 같은 Driver에 유지하고, 새 세션은 서버 상태를 반영해 배치해야 합니다.
  • Data Plane이 Kubernetes API에 직접 의존하지 않도록 Controller가 서버 상태를 Redis에 게시하고 Gateway가 이를 캐싱하면, 역할과 장애 영향을 분리할 수 있습니다.