본문으로 건너뛰기

[이슈] Grpc로 구성된 마이크로서비스의 트래픽 편향 발생

[이슈] Grpc로 구성된 마이크로서비스의 트래픽 편향 발생

이슈

파트너스 앱 사용자가 앱 사용이 되지 않는다고 이슈를 보고합니다.

현상

gRPC 트래픽이 특정 서버로 편향되어 해당 서버의 CPU 사용률이 높아집니다. 2개의 인스턴스 중 1개의 인스턴스에서만 CPU 사용률이 높습니다.

작업 내용

  1. gRPC 트래픽이 편향된 서버의 인스턴스를 종료합니다.
  2. src/config/grpc/grpc.config.service.tsloadBalancingConfig: [{ round_robin: {} }]dns:/// 스킴을 사용하도록 채널 옵션을 추가하여 gRPC 라운드 로빈 분산 설정을 적용합니다.

원인 분석

개발환경 테스트 결과

'출조일정', '출조예약', '출조이력' 등 Nest.js 모듈에서 gRPC 트래픽이 일정 시간 동안 특정 서버로 편향됩니다. 일정 시간 이후에는 트래픽이 다른 서버로 변경되기도 하나, 동일한 서버로 트래픽이 전송되기도 합니다.

gRPC 트래픽 편향 원인

  • gRPC 는 기본적으로 HTTP/2 단일 커넥션 위에서 다중 스트림을 처리합니다. 이 커넥션은 장시간 유지되며, L4 로드밸런서가 커넥션 단위로만 분산을 수행할 경우 최초로 연결된 백엔드 인스턴스에 트래픽이 계속 집약됩니다.
  • HTTP/2 의 커넥션 재활용 특성 때문에, Nest.js gRPC 클라이언트가 채널을 재사용하는 동안에는 새 커넥션이 거의 생성되지 않아 다른 인스턴스로의 분산 기회가 줄어듭니다.
  • 서버 측에서 커넥션을 리셋하거나 클라이언트에서 주기적으로 채널을 재생성하지 않으면, 특정 인스턴스에 세션이 고착되어 CPU 사용률이 한쪽으로 치우치는 현상이 발생합니다.
  • 인스턴스 재시작 또는 커넥션 종료 시 일시적으로 분산이 회복되지만, 커넥션이 다시 고착되면 동일한 편향이 재현됩니다.

gRPC pick_first 기본 정책 설명

  • gRPC 클라이언트는 별도 설정이 없으면 기본 로드 밸런서로 pick_first 정책을 사용합니다.
  • pick_first 는 서비스 디스커버리로부터 받은 엔드포인트 목록 중 첫 번째 서버에 커넥션을 맺고, 연결이 끊어지기 전까지 해당 서버로만 모든 호출을 전송합니다.
  • 커넥션이 장기간 유지되는 서비스에서는 사실상 단일 서버로 트래픽이 몰려 서버 자원 사용 편차가 커집니다.
  • 균등 분산이 필요한 환경에서는 round_robin 같은 정책을 명시해 각 호출이 번갈아 다른 인스턴스로 전달되도록 설정해야 합니다.

gRPC round_robin 동작 방식 정리

  • round_robin을 활성화하면 DNS 또는 서비스 디스커버리에서 받은 서버 목록을 순환하며 각 호출마다 다음 서버를 선택합니다.
  • 호출 시마다 무조건 새 커넥션을 만드는 것이 아니라, 해당 서버로 이미 열린 커넥션이 있으면 재사용하고 없으면 새 커넥션을 생성합니다.
  • 예를 들어 두 서버가 있을 때 첫 호출은 서버 A, 두 번째 호출은 서버 B로 전달되어 분산되지만, 항상 두 개의 커넥션을 동시에 유지한다는 보장은 없습니다.
  • 호출 빈도, keepalive 설정, 네트워크 상태에 따라 한쪽 커넥션만 살아 있을 수도 있으며, 필요 시점에 다른 서버로 연결이 열리고 재사용되는 식으로 동작합니다.

keepalive 동작 구조

  • gRPC는 서버별로 별도의 커넥션을 유지하며, keepaliveTimeMs에 맞춰 각 커넥션에 대해 독립적으로 ping을 보냅니다.
  • round_robin 덕분에 서버 A, B에 각각 커넥션이 열린 상태라면 두 커넥션 모두 설정된 주기로 ping이 전송되어, 장기 세션에서도 연결 상태가 유지됩니다.
  • 호출량이 적어 특정 서버에 아직 커넥션이 생성되지 않았다면 keepalive도 아직 시작되지 않으며, 해당 서버로 첫 호출이 발생해 커넥션이 만들어지는 순간부터 ping 스케줄이 개시됩니다.
  • keepalive 응답 실패로 커넥션이 끊어지는 경우, 다음 호출에서 gRPC가 새 커넥션을 열며 round_robin 순서에 따라 다른 서버로 붙을 수 있습니다.

개선 후 검증 결과

  • 2025년 10월 10일 테스트 기준, 수정된 gRPC 클라이언트 설정으로 호출 시 두 개의 백엔드 인스턴스에 요청이 번갈아 분산되는 것을 로컬 환경과 개발 환경에서 모두 확인합니다.
  • 기존 세션이 특정 인스턴스에 고착되지 않고, 호출 10회 이상 반복 시 응답 비율이 5:5에 근접하게 유지됩니다.
  • CPU 사용률 모니터링(CloudWatch)에서도 두 인스턴스가 모두 20% 내외로 균등하게 분담되어, 이전처럼 한쪽만 급격히 상승하는 현상이 재현되지 않습니다.
  • 신규 설정은 커넥션이 재활용되더라도 라운드 로빈 정책이 강제되어, 장기 세션에서도 특정 인스턴스 편중이 발생하지 않는 것을 gRPC 로그로 확인합니다.