주요 컨텐츠로 이동

Databricks의 지능형 Kubernetes 부하 분산

Kubernetes의 내부 및 Ingress 트래픽을 위한 실시간 클라이언트 측 부하 분산

Intelligent Kubernetes Load Balancing at Databricks

Published: September 30, 2025

공학1분 이내 소요

Summary

  • 특히 Databricks 규모에서 gRPC와 같은 처리량이 많은 영구 연결에 대해 Kubernetes의 기본 부하 분산이 부족한 이유.
  • 맞춤형 rpc 클라이언트와 xDS를 사용하여 클라이언트 측, 컨트롤 플레인 기반 로드 밸런싱 시스템을 구축한 방법.\n
  • 헤드리스 서비스 및 Istio와 같은 대안적 접근 방식의 장단점, 그리고 저희가 경량의 클라이언트 주도 모델을 선택한 이유.

소개

Databricks에서 Kubernetes는 내부 시스템의 핵심입니다. 단일 Kubernetes 클러스터 내에서는 ClusterIP 서비스, CoreDNS, kube-proxy와 같은 기본 네트워킹 프리미티브로도 충분한 경우가 많습니다. Kubernetes는 서비스 트래픽을 라우팅하는 간단한 추상화를 제공합니다. 하지만 성능과 안정성이 중요해지면 이러한 기본 설정의 한계가 드러나기 시작합니다.

이 게시물에서는 트래픽 분산을 개선하고, 테일 레이턴시를 줄이고, 서비스 간 통신의 복원력을 높이기 위해 지능형 클라이언트 측 부하 분산 시스템을 구축한 방법을 공유합니다.

Databricks 사용자라면 이 블로그를 이해하지 않아도 플랫폼을 최대한 활용할 수 있습니다. 하지만 내부 구조를 살짝 들여다보고 싶다면, 저희가 작업해 온 멋진 내용들을 계속 읽어보세요!

문제 제기

Kubernetes에서 고성능 서비스 간 통신은 여러 가지 과제를 안고 있으며, 특히 Databricks에서 gRPC와 함께 사용하는 것과 같이 영구적인 HTTP/2 연결을 사용할 때 더욱 그렇습니다.

Kubernetes가 기본적으로 요청을 라우팅하는 방법

  • 클라이언트는 서비스 이름(예: my-service.default.svc.cluster.local)을 확인합니다. CoreDNS를 통해 서비스의 ClusterIP(가상 IP)를 반환받습니다.
  • 클라이언트는 ClusterIP를 대상으로 간주하고 요청을 보냅니다.
  • 노드에서 iptables, IPVS 또는 eBPF 규칙(kube-proxy에 의해 구성됨)이 패킷을 가로챕니다. 커널은 라운드 로빈과 같은 기본 로드 밸런싱을 기반으로 대상 IP를 백엔드 Pod IP 중 하나로 다시 작성하고 패킷을 전달합니다.
  • 선택된 Pod가 요청을 처리하고 응답이 클라이언트로 다시 전송됩니다.

이 모델은 일반적으로 작동하지만, 성능에 민감한 환경에서는 빠르게 무너져 상당한 한계를 초래합니다.

한계

Databricks에서는 각 Kubernetes 클러스터 내에서 gRPC를 통해 통신하는 수백 개의 스테이트리스(stateless) 서비스를 운영합니다. 이러한 서비스는 종종 처리량이 높고, 지연 시간에 민감하며, 상당한 규모로 실행됩니다.

기본 로드 밸런싱 모델은 다음과 같은 몇 가지 이유로 이 환경에서 부족합니다.

  • 높은 테일 레이턴시: gRPC는 HTTP/2를 사용하며, 이는 클라이언트와 서비스 간에 오래 지속되는 TCP 연결을 유지합니다. Kubernetes 로드 밸런싱은 Layer 4에서 발생하므로 백엔드 Pod는 연결당 한 번만 선택됩니다. 이로 인해 일부 파드(pod)가 다른 파드보다 훨씬 더 많은 로드를 수신하는 트래픽 쏠림 현상이 발생합니다. 결과적으로 테일 레이턴시가 증가하고 부하가 걸리면 성능이 일관되지 않게 됩니다.
  • 비효율적인 리소스 사용: 트래픽이 고르게 분산되지 않으면 용량 요구사항을 예측하기 어려워집니다. 일부 Pod는 CPU나 메모리가 부족해지는 반면 다른 Pod는 유휴 상태가 됩니다. 이는 과도한 프로비저닝과 낭비로 이어집니다.
  • 제한된 로드 밸런싱 전략: kube-proxy는 라운드 로빈이나 무작위 선택과 같은 기본 알고리즘만 지원합니다. 다음과 같은 전략은 지원하지 않습니다.

이러한 한계로 인해 저희는 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를 지속적으로 모니터링하는 경량 컨트롤 플레인을 운영합니다. 영역, 준비 상태, 샤드 라벨과 같은 메타데이터를 포함하여 모든 서비스의 모든 백엔드 포드에 대한 최신 뷰를 유지합니다.

RPC 클라이언트 통합

Databricks의 전략적 이점은 대부분 Scala로 작성된 내부 서비스 전반에 걸쳐 서비스 통신을 위한 공통 프레임워크가 널리 채택되었다는 점이었습니다. 이 공유된 기반 덕분에 클라이언트 측 서비스 검색 및 로드 밸런싱 로직을 프레임워크에 직접 내장할 수 있었고, 이를 통해 팀 전체에서 커스텀 구현 노력 없이 쉽게 채택할 수 있었습니다.

각 서비스는 연결 설정 중에 의존하는 서비스에 대한 컨트롤 플레인의 업데이트를 구독하는 저희의 커스텀 클라이언트와 통합됩니다. 클라이언트는 영역이나 샤드와 같은 메타데이터를 포함하여 정상적인 엔드포인트의 동적 목록을 유지 관리하고, 컨트롤 플레인이 변경 사항을 푸시하면 자동으로 업데이트됩니다.

클라이언트는 DNS 확인 및 kube-proxy를 완전히 우회하므로 항상 서비스 토폴로지에 대한 실시간의 정확한 뷰를 가집니다. 이를 통해 모든 내부 서비스에 걸쳐 일관되고 효율적인 부하 분산 전략을 구현할 수 있습니다.

클라이언트의 고급 부하 분산

rpc 클라이언트는 다음과 같은 전략을 사용하여 요청 인식 부하 분산을 수행합니다.

  • P2C(Power of Two Choices): 대부분의 서비스에서 간단한 P2C(Power of Two Choices) 알고리즘 이 매우 효과적인 것으로 입증되었습니다. 이 전략은 두 개의 백엔드 서버를 무작위로 선택한 다음 활성 연결 수가 더 적거나 부하가 더 낮은 서버를 선택하는 방식입니다. Databricks의 경험에 따르면 P2C는 성능과 구현의 단순성 사이에서 훌륭한 균형을 이루며 엔드포인트 전반에 걸쳐 일관되게 균일한 트래픽 분산을 유도합니다.
  • 영역 선호도 기반: 이 시스템은 영역 선호도 기반 라우팅과 같은 더 발전된 전략도 지원합니다. 이 기능은 특히 지리적으로 분산된 Kubernetes 클러스터에서 영역 간 네트워크 홉을 최소화하는 데 필수적이며, 이를 통해 네트워크 지연 시간과 관련 데이터 전송 비용을 크게 줄일 수 있습니다.

    또한, 시스템은 영역의 용량이 부족하거나 과부하가 걸리는 시나리오도 고려합니다. 이러한 경우 라우팅 알고리즘은 가능한 한 로컬 선호도를 선호하면서도 부하를 분산하기 위해 다른 정상 영역으로 트래픽을 지능적으로 분산시킵니다. 이를 통해 영역 간 용량 분포가 고르지 않은 경우에도 고가용성과 일관된 성능을 보장합니다.
  • 플러거블 지원: 아키텍처가 유연하므로 필요에 따라 추가적인 로드 밸런싱 전략을 플러거블 방식으로 지원할 수 있습니다.

영역 인식 라우팅과 같은 더 고급 전략에는 서비스 토폴로지, 트래픽 패턴, 장애 모드에 대한 신중한 조정과 더 깊은 컨텍스트가 필요했습니다. 이 주제는 별도의 후속 게시물에서 다룰 예정입니다.

접근 방식의 효과를 보장하기 위해 광범위한 시뮬레이션, 실험, 실제 측정 항목 분석을 실행했습니다. 부하가 고르게 분산되고 테일 레이턴시, 오류율, 영역 간 트래픽 비용과 같은 주요 메트릭이 목표 임계값 내에 유지되는지 검증했습니다. 서비스별로 전략을 조정할 수 있는 유연성은 유용했지만, 실제로는 단순하고 일관되게 유지하는 것이 가장 효과적이었습니다.

Envoy와의 xDS 통합

당사의 컨트롤 플레인은 내부 서비스 간 통신을 넘어 유용성을 확장합니다. 이 컨트롤 플레인은 클라이언트가 최신 구성(예: 클러스터, 엔드포인트, 라우팅 규칙)을 동적으로 가져올 수 있게 해주는 검색 프로토콜인 Envoy에 xDS API를 전달하여 외부 트래픽을 관리하는 데 중요한 역할을 합니다. 특히, ClusterLoadAssignment 리소스를 프로그래밍하여 백엔드 엔드포인트에 대한 일관되고 최신 메타데이터를 Envoy에 제공하기 위해 EDS(엔드포인트 검색 서비스)를 구현합니다. 이를 통해 게이트웨이 수준 라우팅(예: 인그레스 또는 외부 공개 트래픽)이 내부 클라이언트에서 사용하는 동일한 단일 진실 공급원(source of truth)과 일치하도록 보장합니다.

요약

이 아키텍처는 DNS 및 kube-proxy의 한계에서 서비스 검색을 분리하는 동시에 라우팅 동작에 대한 세분화된 제어를 제공합니다. 주요 내용은 다음과 같습니다.

  1. 클라이언트는 항상 엔드포인트와 그 상태에 대한 최신 상태의 정확한 뷰를 가집니다.
  2. 부하 분산 전략을 서비스별로 맞춤화하여 효율성과 테일 레이턴시를 개선할 수 있으며,
  3. 내부 및 외부 트래픽이 모두 동일한 단일 진실 공급원을 공유하므로 플랫폼 전반의 일관성이 보장됩니다.

Impact

클라이언트 측 부하 분산 시스템을 배포한 후 성능과 효율성 모두에서 상당한 개선을 확인했습니다.

  • 균일한 요청 분산
    서버 측 QPS가 모든 백엔드 파드에 고르게 분산되었습니다. 일부 파드는 과부하 상태이고 다른 파드는 충분히 활용되지 않던 이전 설정과 달리 이제 트래픽이 예측 가능하게 분산됩니다. 위 차트는 EDS 이전 의 분포를, 아래 차트는 EDS 이후의 균형 잡힌 분포를 보여줍니다.
  • 안정적인 지연 시간 프로필
    포드 전반의 지연 시간 편차가 눈에 띄게 감소했습니다. 포드 전반에서 지연 시간 측정 항목이 개선 및 안정화되어 gRPC 워크로드의 롱테일 동작이 감소했습니다. 아래 다이어그램은 클라이언트 측 부하 분산이 활성화된 후 P90 지연 시간이 어떻게 더 안정화되었는지 보여줍니다.
    안정적인 레이턴시 프로필
  • 리소스 효율성
    더 예측 가능한 레이턴시와 균형 잡힌 부하 덕분에 과도하게 프로비저닝된 용량을 줄일 수 있었습니다. 여러 서비스에 걸쳐 파드 수가 약 20% 감소했으며, 안정성을 저해하지 않으면서 컴퓨팅 리소스를 확보할 수 있었습니다.

과제와 교훈

출시는 분명한 이점을 가져왔지만, 그 과정에서 몇 가지 과제와 인사이트를 발견하기도 했습니다.

  • 서버 콜드 스타트: 클라이언트 측 부하 분산 이전에는 대부분의 요청이 오래 지속되는 연결을 통해 전송되었기 때문에 기존 연결이 재활용될 때까지 새 파드가 요청을 받는 경우는 거의 없었습니다. 전환 후 새로운 pod가 즉시 트래픽을 수신하기 시작했으며, 이로 인해 완전히 준비(warm up)되기 전에 요청을 처리하는 콜드 스타트 문제가 발생했습니다. 느린 시작 램프업을 도입하고 관찰된 오류율이 더 높은 파드에서 트래픽을 다른 곳으로 보내는 방식으로 이 문제를 해결했습니다. 이러한 교훈은 또한 전용 워밍업 프레임워크의 필요성을 더욱 강화했습니다.
  • 메트릭 기반 라우팅: 처음에는 CPU와 같은 리소스 사용량 신호를 기반으로 트래픽을 편중시키는 실험을 했습니다. 개념적으로는 매력적이었지만 이 접근 방식은 신뢰할 수 없는 것으로 판명되었습니다. 모니터링 시스템은 서빙 워크로드와 다른 SLO를 가졌고, CPU와 같은 메트릭은 실시간 용량 신호라기보다는 후행 지표인 경우가 많았습니다. 결국 저희는 이 모델에서 벗어나 서버 상태와 같이 더 신뢰할 수 있는 신호에 의존하기로 결정했습니다.
  • 클라이언트 라이브러리 통합: 클라이언트 라이브러리에 부하 분산을 직접 구축하면 강력한 성능 이점을 얻을 수 있었지만, 일부 피할 수 없는 격차도 발생했습니다. 라이브러리가 없는 언어나 여전히 인프라 부하 분산 장치에 의존하는 트래픽 흐름은 클라이언트 측 분산의 범위 밖에 있습니다.

고려된 대안

클라이언트 측 부하 분산 접근 방식을 개발하면서 다른 대안 솔루션도 평가했습니다. 저희가 궁극적으로 이러한 대안을 선택하지 않은 이유는 다음과 같습니다.

헤드리스 서비스

Kubernetes 헤드리스 서비스(clusterIP: None)는 DNS를 통해 직접 파드 IP를 제공하므로 클라이언트와 프록시(예: Envoy)가 자체적으로 부하 분산을 수행할 수 있습니다. 이 접근 방식은 kube-proxy의 연결 기반 분산의 한계를 우회하고 Envoy가 제공하는 고급 부하 분산 전략(예: 라운드 로빈, 일관된 해싱, 최소 연결 라운드 로빈)을 활성화합니다.

이론적으로 기존 ClusterIP 서비스를 헤드리스 서비스로 전환하거나 동일한 셀렉터를 사용하여 추가 헤드리스 서비스를 생성하면 클라이언트에 직접적인 엔드포인트 가시성을 제공하여 연결 재사용 문제를 완화할 수 있습니다. 하지만 이 접근 방식에는 현실적인 한계가 있습니다.

  • 엔드포인트 가중치 부족: 헤드리스 서비스만으로는 엔드포인트에 가중치를 할당하는 것을 지원하지 않으므로 세분화된 부하 분산 제어를 구현하는 데 제약이 따릅니다.
  • DNS 캐싱 및 오래된 정보(Staleness): 클라이언트는 DNS 응답을 자주 캐시하므로 오래되거나 비정상적인 엔드포인트로 요청을 보낼 수 있습니다.
  • 메타데이터 미지원: DNS 레코드는 엔드포인트(예: 영역, 리전, 샤드)에 대한 추가 메타데이터를 전달하지 않습니다. 이로 인해 영역 인식 또는 토폴로지 인식 라우팅과 같은 전략을 구현하기가 어렵거나 불가능합니다.

헤드리스 서비스가 ClusterIP 서비스에 비해 일시적인 개선을 제공할 수는 있지만, 현실적인 과제와 한계로 인해 Databricks 규모의 장기적인 해결책으로는 부적합했습니다.

서비스 메시(예: Istio)

Istio는 모든 pod에 주입된 Envoy 사이드카를 사용하여 강력한 Layer 7 로드 밸런싱 기능을 제공합니다. 이러한 프록시는 라우팅, 재시도, 서킷 브레이킹 등을 처리하며, 모두 컨트롤 플레인을 통해 중앙에서 관리됩니다.

이 모델은 많은 기능을 제공하지만 몇 가지 이유로 Databricks 환경에는 적합하지 않다고 판단했습니다.

  • 운영 복잡성: 수천 개의 사이드카와 컨트롤 플레인 구성 요소를 관리하는 것은 특히 업그레이드나 대규모 출시 시 상당한 오버헤드를 추가합니다.
  • 성능 오버헤드: 사이드카는 pod당 추가적인 CPU, 메모리, 지연 시간 비용을 발생시키며, 이는 저희 규모에서는 상당한 수준입니다.
  • 제한된 클라이언트 유연성: 모든 라우팅 로직이 외부에서 처리되므로 애플리케이션 계층 컨텍스트에 의존하는 요청 인식 전략을 구현하기가 어렵습니다.

Istio의 Ambient Mesh도 평가했습니다. Databricks는 이미 인증서 배포와 같은 기능을 위한 독점 시스템을 갖추고 있었고 라우팅 패턴도 비교적 정적이었기 때문에, 풀 메시(full mesh)를 도입하는 데 따르는 추가적인 복잡성이 이점보다 더 컸습니다. 주로 Scala 코드베이스를 지원하는 소규모 인프라 팀의 경우 특히 그랬습니다.

사이드카 기반 메시의 가장 큰 장점 중 하나는 언어에 구애받지 않는다는 점(language-agnosticism)입니다. 즉, 팀은 모든 곳에서 클라이언트 라이브러리를 유지 관리할 필요 없이 다양한 언어로 작성된(polyglot) 서비스 전반에 걸쳐 복원력과 라우팅을 표준화할 수 있습니다. 하지만 Databricks 환경은 Scala 기반이며, 모노리포와 빠른 CI/CD 문화 덕분에 프록시 없는 클라이언트 라이브러리 접근 방식이 훨씬 더 실용적입니다. 사이드카의 운영 부담을 감수하는 대신, 저희는 라이브러리와 인프라 구성 요소에 직접 최고 수준의 로드 밸런싱을 구축하는 데 투자했습니다.

향후 방향 및 탐색 분야

현재의 클라이언트 측 로드 밸런싱 접근 방식은 내부 서비스 간 통신을 크게 개선했습니다. 하지만 Databricks가 계속 확장됨에 따라 시스템을 더욱 향상시키기 위해 몇 가지 고급 분야를 탐색하고 있습니다.

클러스터 및 리전 간 로드 밸런싱: 여러 리전에 걸쳐 수천 개의 Kubernetes 클러스터를 관리하므로, 개별 클러스터를 넘어 지능형 로드 밸런싱을 확장하는 것이 매우 중요합니다. 플랫 L3 네트워킹 및 서비스 메시 솔루션과 같은 기술을 탐색하고 있으며, 다중 리전 EDS(엔드포인트 검색 서비스) 클러스터와 원활하게 통합하고 있습니다. 이를 통해 강력한 클러스터 간 트래픽 관리, 내결함성, 전역적으로 효율적인 리소스 활용이 가능해질 것입니다.

AI 사용 사례를 위한 고급 부하 분산 전략: 고급 AI 워크로드를 더 잘 지원하기 위해 가중 부하 분산과 같은 더 정교한 전략을 도입할 계획입니다. 이러한 전략은 특정 애플리케이션 특성에 기반하여 더 세분화된 리소스 할당과 지능적인 라우팅 결정을 가능하게 하여, 궁극적으로 성능, 리소스 소비 및 비용 효율성을 최적화할 것입니다.

이와 같은 대규모 분산 인프라 과제에 관심이 있다면 채용 중입니다. 저희와 함께 만들어가요 — Databricks의 채용 공고를 살펴보세요!

 

(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)

게시물을 놓치지 마세요

관심 있는 카테고리를 구독하고 최신 게시물을 받은편지함으로 받아보세요