Databricks에서 Kubernetes는 내부 시스템의 핵심입니다. 단일 Kubernetes 클러스터 내에서는 ClusterIP 서비스, CoreDNS, kube-proxy와 같은 기본 네트워킹 프리미티브로도 충분한 경우가 많습니다. Kubernetes는 서비스 트래픽을 라우팅하는 간단한 추상화를 제공합니다. 하지만 성능과 안정성이 중요해지면 이러한 기본 설정의 한계가 드러나기 시작합니다.
이 게시물에서는 트래픽 분산을 개선하고, 테일 레이턴시를 줄이고, 서비스 간 통신의 복원력을 높이기 위해 지능형 클라이언트 측 부하 분산 시스템을 구축한 방법을 공유합니다.
Databricks 사용자라면 이 블로그를 이해하지 않아도 플랫폼을 최대한 활용할 수 있습니다. 하지만 내부 구조를 살짝 들여다보고 싶다면, 저희가 작업해 온 멋진 내용들을 계속 읽어보세요!
Kubernetes에서 고성능 서비스 간 통신은 여러 가지 과제를 안고 있으며, 특히 Databricks에서 gRPC와 함께 사용하는 것과 같이 영구적인 HTTP/2 연결을 사용할 때 더욱 그렇습니다.
이 모델은 일반적으로 작동하지만, 성능에 민감한 환경에서는 빠르게 무너져 상당한 한계를 초래합니다.
Databricks에서는 각 Kubernetes 클러스터 내에서 gRPC를 통해 통신하는 수백 개의 스테이트리스(stateless) 서비스를 운영합니다. 이러한 서비스는 종종 처리량이 높고, 지연 시간에 민감하며, 상당한 규모로 실행됩니다.
기본 로드 밸런싱 모델은 다음과 같은 몇 가지 이유로 이 환경에서 부족합니다.
이러한 한계로 인해 저희는 Kubernetes 클러스터 내에서 서비스 간 통신을 처리하는 방법을 재고하게 되었습니다.
Kubernetes의 kube-proxy 및 기본 서비스 라우팅의 한계를 해결하기 위해, 우리는 프록시가 없고 완전히 클라이언트 주도적인 로드 밸런싱 시스템을 커스텀 서비스 검색 컨트롤 플레인을 기반으로 구축했습니다.
핵심 요구사항은 애플리케이션 레이어에서 로드 밸런싱을 지원하고 주요 경로에서 DNS에 대한 종속성을 제거하는 것이었습니다. kube-proxy와 같은 Layer 4 로드 밸런서는 지속적인 연결을 활용하는 Layer 7 프로토콜(예: gRPC)에 대해 요청별로 지능적인 결정을 내릴 수 없습니다. 이러한 아키텍처상의 제약은 병목 현상을 유발하며, 트래픽 관리에 더 지능적인 접근 방식을 필요로 합니다.
다음 표는 주요 차이점과 클라이언트 측 접근 방식의 이점을 요약한 것입니다.
표 1: 기본 Kubernetes LB와 Databricks의 클라이언트 측 LB 비교
| 피처/속성 | 기본 Kubernetes 로드 밸런싱(kube-proxy) | Databricks의 클라이언트 측 로드 밸런싱 |
|---|---|---|
| 로드 밸런싱 레이어 | Layer 4 (TCP/IP) | 7계층(애플리케이션/gRPC) |
| 결정 빈도 | TCP 연결당 한 번 | 요청당 |
| 서비스 검색 | CoreDNS + kube-proxy(가상 IP) | xDS 기반 컨트롤 플레인 + 클라이언트 라이브러리 |
| 지원되는 전략 | 기본(라운드 로빈, 무작위) | 고급(P2C, 영역 선호도, 플러그형) |
| 테일 레이턴시 영향 | 높음(영구 연결에서의 트래픽 쏠림 현상으로 인해) | 감소(균등한 분배, 동적 라우팅) |
| 리소스 활용률 | 비효율적(과잉 프로비저닝) | 효율적(균형 잡힌 부하) |
| DNS/프록시 의존성 | 높음 | 최소/최소, 중요 경로에 없음 |
| 운영 제어 | 제한적 | 세분화 |
이 시스템은 DNS나 Layer 4 네트워킹에 대한 의존도를 최소화하면서 지능적이고 최신 상태의 요청 라우팅을 가능하게 합니다. 이는 클라이언트가 실시간 토폴로지 및 상태 데이터를 기반으로 정보에 입각한 결정을 내릴 수 있는 능력을 부여합니다.
그림은 저희의 커스텀 Endpoint Discovery Service가 작동하는 모습을 보여줍니다. 이 서비스는 Kubernetes API에서 서비스 및 엔드포인트 데이터를 읽어 xDS 응답으로 변환합니다. Armeria 클라이언 트와 API 프록시 모두 이 서비스로 요청을 스트리밍하고 실시간 엔드포인트 메타데이터를 수신하며, 이는 애플리케이션 서버에서 폴백 클러스터를 백업으로 사용하는 지능형 라우팅에 사용됩니다.”
저희는 Services 및 EndpointSlices의 변경 사항에 대해 Kubernetes API를 지속적으로 모니터링하는 경량 컨트롤 플레인을 운영합니다. 영역, 준비 상태, 샤드 라벨과 같은 메타데이터를 포함하여 모든 서비스의 모든 백엔드 포드에 대한 최신 뷰를 유지합니다.
Databricks의 전략적 이점은 대부분 Scala로 작성된 내부 서비스 전반에 걸쳐 서비스 통신을 위한 공통 프레임워크가 널리 채택되었다는 점이었습니다. 이 공유된 기반 덕분에 클라이언트 측 서비스 검색 및 로드 밸런싱 로직을 프레임워크에 직접 내장할 수 있었고, 이를 통해 팀 전체에서 커스텀 구현 노력 없이 쉽게 채택할 수 있었습니다.
각 서비스는 연결 설정 중에 의존하는 서비스에 대한 컨트롤 플레인의 업데이트를 구독하는 저희의 커스텀 클라이언트와 통합됩니다. 클라이언트는 영역이나 샤드와 같은 메타데이터를 포함하여 정상적인 엔드포인트의 동적 목록을 유지 관리하고, 컨트롤 플레인이 변경 사항을 푸시하면 자동으로 업데이트됩니다.
클라이언트는 DNS 확인 및 kube-proxy를 완전히 우회하므로 항상 서비스 토폴로지에 대한 실시간의 정확한 뷰를 가집니다. 이를 통해 모든 내부 서비스에 걸쳐 일관되고 효율적인 부하 분산 전략을 구현할 수 있습니다.
rpc 클라이언트는 다음과 같은 전략을 사용하여 요청 인식 부하 분산을 수행합니다.
영역 인식 라우팅과 같은 더 고급 전략에는 서비스 토폴로지, 트래픽 패턴, 장애 모드에 대한 신중한 조정과 더 깊은 컨텍스트가 필요했습니다. 이 주제는 별도의 후속 게시물에서 다룰 예정입니다.
접근 방식 의 효과를 보장하기 위해 광범위한 시뮬레이션, 실험, 실제 측정 항목 분석을 실행했습니다. 부하가 고르게 분산되고 테일 레이턴시, 오류율, 영역 간 트래픽 비용과 같은 주요 메트릭이 목표 임계값 내에 유지되는지 검증했습니다. 서비스별로 전략을 조정할 수 있는 유연성은 유용했지만, 실제로는 단순하고 일관되게 유지하는 것이 가장 효과적이었습니다.
당사의 컨트롤 플레인은 내부 서비스 간 통신을 넘어 유용성을 확장합니다. 이 컨트롤 플레인은 클라이언트가 최신 구성(예: 클러스터, 엔드포인트, 라우팅 규칙)을 동적으로 가져올 수 있게 해주는 검색 프로토콜인 Envoy에 xDS API를 전달하여 외부 트래픽을 관리하는 데 중요한 역할을 합니다. 특히, ClusterLoadAssignment 리소스를 프로그래밍하여 백엔드 엔드포인트에 대한 일관되고 최신 메타데이터를 Envoy에 제공하기 위해 EDS(엔드포인트 검색 서비스)를 구현합니다. 이를 통해 게이트웨이 수준 라우팅(예: 인그레스 또는 외부 공개 트래픽)이 내부 클라이언트에서 사용하는 동일한 단일 진실 공급원(source of truth)과 일치하도록 보장합니다.
이 아키텍처는 DNS 및 kube-proxy의 한계에서 서비스 검색을 분리하는 동시에 라우팅 동작에 대한 세분화된 제어를 제공합니다. 주요 내용은 다음과 같습니다.
클라이언트 측 부하 분산 시스템을 배포한 후 성능과 효율성 모두에서 상당한 개선을 확인했습니다.

출시는 분명한 이점을 가져왔지만, 그 과정에서 몇 가지 과제와 인사이트를 발견하기도 했습니다.
클라이언트 측 부하 분산 접근 방식을 개발하면서 다른 대안 솔루션도 평가했습니다. 저희가 궁극적으로 이러한 대안을 선택하지 않은 이유는 다음과 같습니다.
Kubernetes 헤드리스 서비스(clusterIP: None)는 DNS를 통해 직접 파드 IP를 제공하므로 클라이언트와 프록시(예: Envoy)가 자체적으로 부하 분산을 수행할 수 있습니다. 이 접근 방식은 kube-proxy의 연결 기반 분산의 한계를 우회하고 Envoy가 제공하는 고급 부하 분산 전략(예: 라운드 로빈, 일관된 해싱, 최소 연결 라운드 로빈)을 활성화합니다.
이론적으로 기존 ClusterIP 서비스를 헤드리스 서비스로 전환하거나 동일한 셀렉터를 사용하여 추가 헤드리스 서비스를 생성하면 클라이언트에 직접적인 엔드포인트 가시성을 제공하여 연결 재사용 문제를 완화할 수 있습니다. 하지만 이 접근 방식에는 현실적인 한계가 있습니다.
헤드리스 서비스가 ClusterIP 서비스에 비해 일시적인 개선을 제공할 수는 있지만, 현실적인 과제와 한계로 인해 Databricks 규모의 장기적인 해결책으로는 부적합했습니다.
Istio는 모든 pod에 주입된 Envoy 사이드카를 사용하여 강력한 Layer 7 로드 밸런싱 기능을 제공합니다. 이러한 프록시는 라우팅, 재시도, 서킷 브레이킹 등을 처리하며, 모두 컨트롤 플레인을 통해 중앙에서 관리됩니다.
이 모델은 많은 기능을 제공하지만 몇 가지 이유로 Databricks 환경에는 적합하지 않다고 판단했습니다.
Istio의 Ambient Mesh도 평가했습니다. Databricks는 이미 인증서 배포와 같은 기능을 위한 독점 시스템을 갖추고 있었고 라우팅 패턴도 비교적 정적이었기 때문에, 풀 메시(full mesh)를 도입하는 데 따르는 추가적인 복잡성이 이점보다 더 컸습니다. 주로 Scala 코드베이스를 지원하는 소규모 인프라 팀의 경우 특히 그랬습니다.
사이드카 기반 메시의 가장 큰 장점 중 하나는 언어에 구애받지 않는다는 점(language-agnosticism)입니다. 즉, 팀은 모든 곳에서 클라이언트 라이브러리를 유지 관리할 필요 없이 다양한 언어로 작성된(polyglot) 서비스 전반에 걸쳐 복원력과 라우팅을 표준화할 수 있습니다. 하지만 Databricks 환경은 Scala 기반이며, 모노리포와 빠른 CI/CD 문화 덕분에 프록시 없는 클라이언트 라이브러리 접근 방식이 훨씬 더 실용적입니다. 사이드카의 운영 부담을 감수하는 대신, 저희는 라이브러리와 인프라 구성 요소에 직접 최고 수준의 로드 밸런싱을 구축하는 데 투자했습니다.
현재의 클라이언트 측 로드 밸런싱 접근 방식은 내부 서비스 간 통신을 크게 개선했습니다. 하지만 Databricks가 계속 확장됨에 따라 시스템을 더욱 향상시키기 위해 몇 가지 고급 분야를 탐색하고 있습니다.
클러스터 및 리전 간 로드 밸런싱: 여러 리전에 걸쳐 수천 개의 Kubernetes 클러스터를 관리하므로, 개별 클러스터를 넘어 지능형 로드 밸런싱을 확장하는 것이 매우 중요합니다. 플랫 L3 네트워킹 및 서비스 메시 솔루션과 같은 기술을 탐색하고 있으며, 다중 리전 EDS(엔드포인트 검색 서비스) 클러스터와 원활하게 통합하고 있습니다. 이를 통해 강력한 클러스터 간 트래픽 관리, 내결함성, 전역적으로 효율적인 리소스 활용이 가능해질 것입니다.
AI 사용 사례를 위한 고급 부하 분산 전략: 고급 AI 워크로드를 더 잘 지원하기 위해 가중 부하 분산과 같은 더 정교한 전략을 도입할 계획입니다. 이러한 전략은 특정 애플리케이션 특성에 기반하여 더 세분화된 리소스 할당과 지능적인 라우팅 결정을 가능하게 하여, 궁극적으로 성능, 리소스 소비 및 비용 효율성을 최적화할 것입니다.
이와 같은 대규모 분산 인프라 과제에 관심이 있다면 채용 중입니다. 저희와 함께 만들어가요 — Databricks의 채용 공고를 살펴보세요!
(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)
