특정 파드에 트래픽이 몰리는 현상 이유와 해결 방법

운영중인 서비스의 모니터링을 고도화하던 중 특정 파드에만 리소스가 몰리는 현상을 발견하여 그 이유와 해결 방법에 대한 리서치를 공유합니다.

문제 인식#

특정 파드로 몰리는 트래픽

처음에 저 그래프를 봤던 순간을 기억합니다... 당연히 트래픽을 라운드 로빈 방식으로 분산하여 받고 있을 줄 알았는데... 쿠버네티스에게 배신을 당한 느낌입니다. 너... 이렇게 동작하고 있었던거니?

이런 현상이 발생하고 있는 이유를 리서치하는 과정에서 Load balancing and scaling long-lived connections in Kubernetes 이라는 아주 잘 작성된 글을 발견하였습니다. 이미지와 상세한 설명으로 이유를 잘 알려주니 꼭 읽어볼 것을 추천드립니다.

이 글에서 간단하게 설명드리면, 쿠버네티스의 서비스에 IP 주소는 etcd 에 저장되고 저장된 IP 주소를 kube-proxy 에서 사용하는데, kube-proxy 는 iptables 을 사용합니다. iptables 는 로드 밸런싱을 하도록 설계되지 않았지만, 쿠버네티스는 스마트한 규칙 세트를 통해 비슷하게 작동하도록 하고 있습니다.

kube-proxy_rule_diagram

이러한 이유로 클라이언트에서 Long-lived connections 을 사용할 경우 쿠버네티스는 스케일 아웃을 하지 않습니다. 즉 keep-alive 헤더가 있는 HTTP request 는 TCP 연결을 유지하기 때문에 로드 밸런싱 되지 않는다는 것이죠. 그렇다고 keep-alive 를 사용하지 않도록 권고하는 것은 바람직하지 못합니다. 파이썬에서 Connection: keep-alive 을 사용하는 법을 설명한 Learning about the HTTP “Connection: keep-alive” header 글에 테스트 결과는 연결을 유지했을 때 이점을 잘 보여줍니다.

Average time with keep-alive/persistent connections: 7.00 seconds
Average time with new connections: 22.38 seconds

해결 방법#

그렇다면 어떻게 이 문제를 해결할 수 있을까요? 친절하게도 위에서 공유한 글에는 해결 방법까지 제안하고 있습니다. 글에서 제안하는 방법, 운영중인 서비스의 특성과 제 개인적인 생각까지 반영한 해결 방안은 아래와 같습니다.

클라이언트 측 로직 개선#

  • 클라이언트에서 service 에 엔드포인트 목록을 가지고 와서 커넥션 풀을 만들고, 돌아가면서 요청해야 함
  • 주기적으로 엔드포인트 업데이트도 포함
  • 서비스 내부 클라이언트들은 로직을 개선하여 해결할 수 있지만 API 를 사용하는 고객들에게 로직 개선을 기대하는 것은 무리

클라이언트 갯수 증가#

  • 웹 접근 요청은 엔진엑스를 통하여 프록시되고 있기 때문에 엔진엑스와 특정 파드에 영구 커넥션이 열려서 웹앱으로 접근하는 모든 요청이 단일 파드에 몰리는 현상이 있을 것으로 추정
  • 웹 클라이언트를 늘려서 영구 연결을 맺는 여는 클라이언트를 늘려서 분산 가능

서비스 메시 적용#

  • 각 서비스에 변경없이 가장 우아하게 문제를 해결할 수 있는 방법이라고 생각
  • 러닝 커브 및 변경 범위가 너무 넓음, 단기적으로 해결할 수 없음
  • 서비스 메시 도구 중 istio 가 가장 유력

Application API Gateway 도입#

  • Application API Gateway 가 로드 밸런싱
  • API 게이트웨이 자체적으로 같은 이슈를 겪게 될 가능성이 있으나, 서비스 로직을 수행하는 주체가 아니기 때문에 부하로 인한 문제 해소에는 도움이 될 것으로 예상

아무래도 서비스 메시 적용을 하는 방법으로 해결할 가능성이 가장 커보입니다. istio는 이전에도 도입을 검토했었고 학습도 어느 정도는 진행된 상태입니다. 장기적으로 봤을 때도 가장 효과적인 해결 방법이라고 생각합니다. 하지만 제대로 공부하고 쓰지 않는다면 이슈가 쿠버에서 istio 로 옮겨갈 가능성도 있고, 또 관리 포인트도 늘어나는 점은 감안해야 합니다.

마치며#

쿠버네티스를 사용한지 꽤나 오래되었음에도 내부적으로 어떻게 동작하는지 확실히 학습하지 못한 부분에 깊이 반성하고 있습니다. 물론 이 이슈 뿐아니라 무지로 인해 지금도 최적화되어 동작하지 않는 부분이 존재할 것으로 예상됩니다. 끊임없는 학습에 필요성을 다시 한번 체감합니다. 조만간 이 이슈를 해결한 뒤 트래픽이 모든 파드에 우아하게 분산된 그래프를 공유해보도록 하겠습니다.