ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kubernetes 환경에서 gRPC 로드밸런싱
    Infra 2024. 11. 17. 14:44
    728x90

    gRPC는 HTTP/2 기반으로 동작을 하고 HTTP/2는 하나의 TCP 연결에서 여러개의 스트림을 처리하도록 설계되어 클라이언트와 서버간의 연결이 오래 유지되는 Long lived connection 방식을 사용합니다.

    이러한 특성으로 인해 Kubernetes의 Connection Based 로드밸런싱이 gRPC에서는 정상적으로 동작하지 않게 되고 모든 요청이 동일한 파드에 고정되게 됩니다.

    Connection based 로드밸런싱이란?

    쿠버네티스의 기본 로드밸런싱 정책으로 L4 레이어에서 동작하며 TCP 연결이 생성될 때 로드밸런서가 특정 파드에 연결을 할당하고 연결이 종료될 때 까지 해당 파드에 트래픽을 라우팅하는 방식입니다.

    동작 방식

    1. 클라이언트가 Service의 IP(ClusterIP, NodePort, LoadBalancer 등)에 요청을 보냄.
    2. 로드밸런서가 클라이언트의 요청을 받아 연결을 초기화하고, 연결된 서버(Pod)를 선택함.
    3. 선택된 서버와 클라이언트 간 연결이 유지되는 동안, 해당 연결에서 발생하는 모든 트래픽은 동일한 서버로 전달됨.

    Kubernetes의 Service 객체는 iptables 또는 IPVS를 통해 로드밸런싱을 구현하며, 이 과정에서 connection-based 로드밸런싱이 기본적으로 적용됩니다.

    • iptables: DNAT(Destination NAT)를 통해 클라이언트의 요청을 특정 파드로 라우팅하며 각 연결은 고정된 Pod에 바인딩됩니다.
    • IPVS: 좀 더 정교한 로드밸런싱 알고리즘을 제공하지만 여전히 connection-based 로드밸런싱 방식입니다.

    그러면 gRPC를 사용하면서 로드밸런싱을 하기 위해서 어떻게 해야하는가?

    1. Service mesh

    Istio의 Envoy proxy의 기능을 활용하면 L7 레이어의 연결 관리 및 트래픽 라우팅을 설정할 수 있어 HTTP/2 및 gRPC의 Long lived connection을 호환가능하게 해줍니다.

    Istio가 gRPC Long-Lived Connection 지원 가능한 이유

    1. Envoy는 Connection Pool을 사용하여 클라이언트와 서버 간의 연결을 관리하여 클라이언트와의 장기 연결을 유지하면서 파드들의 상태에 따라 트래픽을 동적으로 조정할 수 있습니다.
    2. Istio는 Request-level 로드밸런싱을 지원하며, gRPC 요청을 개별적으로 파드들에 분배합니다.
    3. Istio는 VirtualService와 DestinationRule을 통해 연결 유지 시간, 재시도, 타임아웃, 로드밸런싱 정책 등을 세밀히 제어할 수 있습니다.

    설정 예시

    Destination rule

    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: grpc-destination
    spec:
      host: grpc-service.default.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            http2MaxRequests: 1000  # HTTP/2 연결 당 최대 요청 수
            maxRequestsPerConnection: 0  # 연결당 제한 없음
            idleTimeout: 1h  # 비활성 연결 유지 시간
        outlierDetection:
          consecutiveErrors: 5  # 오류 발생 시 연결 종료 기준
          interval: 10s  # 오류 탐지 간격
          baseEjectionTime: 1m  # 연결 제거 시간
        loadBalancer:
          simple: ROUND_ROBIN  # 요청을 라운드 로빈 방식으로 분배
    

    Virtual service

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: grpc-virtual-service
    spec:
      hosts:
      - grpc-service.default.svc.cluster.local
      http:
      - route:
        - destination:
            host: grpc-service.default.svc.cluster.local
            port:
              number: 50051
        timeout: 15s  # 요청 타임아웃
        retries:
          attempts: 3  # 최대 재시도 횟수
          perTryTimeout: 5s  # 각 시도당 타임아웃
          retryOn: 5xx,connect-failure,refused-stream  # 재시도 조건
    

    2. 클라이언트 사이드 로드밸런싱

    gRPC는 클라이언트 사이드에서 로드밸런싱이 가능합니다.
    클라이언트에서 Kubernetes API를 사용해 서비스의 엔드포인트(Endpoints)를 직접 조회하여 여러 Pod에 트래픽을 분산시킬 수 있고 channel을 만드는 과정에서 로드밸런싱 정책을 설정할 수 있습니다.

    private val channel: ManagedChannel = ManagedChannelBuilder
        .forTarget("dns:///example.example.svc.cluster.local")
        .defaultLoadBalancingPolicy("round_robin")
        .usePlaintext()
        .build();

    위 설정 방식은 현재 experimental 상태로 운영환경에서 적용하기에는 리스크가 존재합니다.
    https://github.com/grpc/grpc-java/issues/1771

    728x90

    'Infra' 카테고리의 다른 글

    [Istio] External Authorization  (0) 2024.08.04
    [Kubernetes] 쿠버네티스 클러스터 구조  (0) 2024.03.31
Designed by Tistory.