1. 개요

2편에서 리버스 프록시가 하는 일 중 하나로 로드밸런싱을 짧게 언급했다. 이번 편은 그 로드밸런싱을 조금 더 파본다. 요청을 여러 서버로 어떻게 나누는지, 그리고 그중 죽은 서버를 어떻게 걸러내는지가 핵심이다.


2. 핵심 내용

2-1. 로드밸런싱 알고리즘 몇 가지

  • 라운드로빈(Round Robin): 서버 목록을 순서대로 돌아가며 하나씩 배정한다. 구현이 단순하고, 서버들의 처리 능력과 요청 하나하나의 무게가 비슷할 때 잘 맞는다. 반대로 서버 성능이 제각각이거나 요청 하나가 오래 걸리는 경우가 섞여 있으면 특정 서버에 느린 요청이 몰려 쌓일 수 있다.
  • 최소 연결(Least Connections): 현재 처리 중인 연결 수가 가장 적은 서버로 보낸다. 요청 처리 시간이 들쭉날쭉한 환경, 예를 들어 어떤 요청은 즉시 끝나고 어떤 요청은 파일 업로드처럼 오래 걸리는 환경에서 라운드로빈보다 균형이 잘 맞는다.
  • IP 해시(IP Hash): 클라이언트 IP 를 해시해서 같은 클라이언트는 항상 같은 서버로 보낸다. 서버가 세션을 메모리에 들고 있어서 매번 같은 서버로 가야 하는 경우(sticky session) 쓴다. 다만 이건 근본적으로 무상태(stateless) 설계를 피하는 임시방편에 가깝다. 세션을 Redis 같은 공유 저장소로 빼면 어느 서버가 받아도 상관없어지고, 이 방식 자체가 필요 없어진다.
  • 가중치 기반(Weighted): 서버 성능이 다르면 각 알고리즘에 가중치를 곱해서, 스펙 좋은 서버가 더 많은 비율을 받게 한다.

2-2. 헬스체크가 없으면 벌어지는 일

로드밸런서는 기본적으로 뒤에 있는 서버들이 다 살아 있다고 가정하고 위 알고리즘대로 요청을 나눠준다. 헬스체크는 이 가정이 맞는지 주기적으로 확인하는 절차다. 헬스체크가 없거나 잘못 설정돼 있으면, 죽은 서버로도 계속 요청을 보내게 된다.

  • 서버 하나가 응답 없이 멈췄는데 헬스체크가 없으면, 라운드로빈은 여전히 순서대로 그 서버에도 요청을 보낸다. 클라이언트 입장에서는 요청의 일정 비율이 계속 타임아웃 난다.
  • 서버가 재시작 중이라 아직 요청 받을 준비가 안 됐는데 헬스체크 없이 트래픽을 받으면, 커넥션 풀이 아직 안 열려 있어 요청이 줄줄이 실패한다.

2-3. 무엇을 체크해야 “살아 있다” 인가

단순히 TCP 포트가 열려 있는지만 보는 건 부족하다. 포트는 열려 있어도 애플리케이션이 DB 연결이 끊겨서 모든 요청에 500을 내고 있을 수 있다. 그래서 보통 애플리케이션 레벨에서 /health 같은 경로를 따로 만들어 “핵심 의존성까지 정상인지” 응답하게 한다.

다만 이것도 지나치면 역효과가 난다. 헬스체크 응답에 모든 의존성(DB, 캐시, 외부 API, 메일 서버까지)을 다 엮어버리면, 정작 앱 자체는 멀쩡한데 부가 기능 하나가 잠깐 흔들려도 헬스체크가 실패해서 멀쩡한 서버가 로드밸런서 풀에서 빠지거나, 쿠버네티스라면 파드가 재시작당한다. 그래서 liveness(프로세스가 살아 있는지)와 readiness(지금 트래픽을 받을 준비가 됐는지)를 분리해서, liveness 는 최소한만 보고 readiness 에서 의존성 상태를 반영하는 식으로 설계하는 게 일반적이다.

쿠버네티스에서는 이 프로브를 노드의 kubelet 이 파드 IP 로 직접 보낸다. 그래서 네트워크 정책으로 파드 인그레스를 막으면 프로브 트래픽까지 같이 막혀 죽지 않은 파드가 재시작 루프를 도는 경우가 있는데, 이건 실제로 세 네임스페이스에 default-deny 를 세우며 겪은 함정이다. 시크릿 로테이션 중에도 비슷한 일이 생긴다. readiness 프로브가 DB 비밀번호를 쓰면 순서를 어떻게 잡아도 프로브가 실패하는 창이 생기는데, 이 문제는 순서가 아니라 겹치는 기간으로 풀어야 한다.

2-4. 얼마나 자주, 몇 번 실패하면 빼나

헬스체크 주기가 너무 길면 죽은 서버로 트래픽이 가는 시간이 길어지고, 너무 짧으면 네트워크가 잠깐 흔들릴 때마다 서버를 풀에서 뺐다 넣었다 하는 플래핑(flapping)이 생긴다. 그래서 보통 “몇 초마다 체크하되, 연속으로 N번 실패해야 뺀다” 는 식으로 임계값을 둔다. 연속 실패 조건이 없으면 순간적인 네트워크 지연 한 번에 멀쩡한 서버가 빠지는 일이 반복된다.


3. 마무리

요약

  • 라운드로빈은 단순하고 균일한 부하에 맞고, 최소 연결은 요청 처리 시간이 들쭉날쭉할 때 더 균형 있게 분산한다.
  • 헬스체크가 없으면 로드밸런서는 죽은 서버로도 계속 트래픽을 보낸다.
  • TCP 포트만 보지 말고 애플리케이션 레벨에서 체크하되, liveness 와 readiness 를 분리해 부가 기능 장애가 전체 재시작으로 번지지 않게 한다.
  • 연속 실패 횟수 같은 임계값 없이 체크 주기만 짧게 잡으면 순간 지연에도 멀쩡한 서버가 풀에서 빠지는 플래핑이 생긴다.

다음은 7편: 관측성 삼각형, 로그·메트릭·트레이스 다.