Load Balancer는 실제로 서버 요청을 어떻게 분산할까?
웹서비스의 사용자가 늘어나면 한 대의 서버만으로 모든 요청을 처리하기 어려워진다.
처음에는 Nginx와 애플리케이션 서버 한 대로도 충분했던 서비스가 트래픽이 증가하면서 CPU 사용률이 높아지고 응답 시간이 길어지기 시작한다. 이때 흔히 서버를 여러 대로 늘리고 앞단에 Load Balancer를 배치한다.
구조는 단순해 보인다.
사용자는 Load Balancer에 접속하고, Load Balancer는 여러 서버 가운데 하나를 선택해 요청을 전달한다.
하지만 실제 동작은 단순히 요청을 A 서버, B 서버, C 서버에 차례로 던지는 것만으로 설명하기 어렵다.
클라이언트와 Load Balancer 사이의 연결, Load Balancer와 Backend Server 사이의 연결, HTTP Keep-Alive, 서버의 상태 확인, 장애 서버 제외 등 여러 과정이 함께 동작한다.
이번 글에서는 Load Balancer가 실제로 하나의 요청을 받아 Backend Server까지 전달하는 과정을 서버 운영 관점에서 살펴본다.
서버를 여러 대로 늘렸는데 왜 Load Balancer가 필요할까?
웹서버가 한 대라면 구조는 매우 단순하다.
Client
↓
Web Server
사용자가 서버 IP나 해당 서버를 가리키는 도메인으로 접속하면 모든 요청이 한 서버로 들어간다.
하지만 서버가 세 대로 늘어난다면 문제가 생긴다.
Web Server A
Web Server B
Web Server C
클라이언트가 이 가운데 어느 서버로 접속해야 할지 결정할 방법이 필요하다.
사용자에게 서버 IP 세 개를 직접 선택하게 할 수는 없다.
그래서 일반적으로 서버 앞에 하나의 진입점을 만든다.
┌─ Server A
Client → LB ─┼─ Server B
└─ Server C
클라이언트는 실제 Backend Server의 존재를 몰라도 된다.
사용자는 동일한 서비스 주소로 요청을 보내고 Load Balancer가 내부에서 어떤 서버로 전달할지 결정한다.
이 구조의 핵심은 단순한 트래픽 분배가 아니다.
Backend Server를 추가하거나 제거해도 클라이언트 측의 접속 구조를 바꾸지 않아도 된다는 점이다.
서버 한 대에 장애가 발생했을 때 정상 서버만 사용하도록 구성할 수도 있다.
따라서 Load Balancer는 서버 여러 대를 하나의 서비스처럼 보이게 만드는 역할도 한다.
하나의 요청은 Load Balancer를 어떻게 통과할까?
사용자가 브라우저에서 다음과 같은 주소에 접속한다고 가정해보자.
https://example.com
DNS 조회 결과가 Load Balancer의 IP를 반환하도록 구성돼 있다면 클라이언트는 Backend Server가 아니라 Load Balancer와 먼저 통신한다.
흐름을 단순화하면 다음과 같다.
Client
↓
Load Balancer
↓
Backend Server
하지만 네트워크 관점에서는 여기에서 두 개의 구간을 구분해야 한다.
첫 번째는
Client ↔ Load Balancer
이고 두 번째는
Load Balancer ↔ Backend Server
이다.
특히 Layer 7 방식의 Reverse Proxy형 Load Balancer에서는 Load Balancer가 클라이언트의 요청을 받아 HTTP 내용을 확인한 뒤 새로운 Backend 연결을 통해 서버로 요청을 전달할 수 있다.
예를 들어 Client가 Load Balancer와 TCP Connection을 맺었다고 해서 동일한 TCP Connection이 그대로 Backend Server까지 이어지는 것은 아니다.
Load Balancer가 연결의 중간 지점이 되는 구조에서는 앞단과 뒷단의 Connection이 서로 분리된다.
이 개념을 이해하지 못하면 실제 장애 분석 과정에서
“사용자는 연결됐는데 왜 Backend 연결은 실패하지?”
같은 상황을 이해하기 어려워진다.
Load Balancer는 어떤 서버로 보낼지 어떻게 결정할까?
Load Balancer는 Backend Server 목록을 가지고 있다.
예를 들어 다음 세 서버가 있다고 가정하자.
10.0.0.11
10.0.0.12
10.0.0.13
Load Balancer는 요청이 들어오면 설정된 분산 방식에 따라 이 가운데 하나를 선택한다.
가장 기본적인 방식 가운데 하나가 Round Robin이다.
개념적으로는
Request 1 → Server A
Request 2 → Server B
Request 3 → Server C
Request 4 → Server A
처럼 서버를 순서대로 선택한다.
하지만 실제 환경에서는 단순 순환 방식만 사용하는 것은 아니다.
현재 연결이 적은 서버를 선택하거나, 특정 기준에 따라 동일 사용자를 같은 서버로 보내거나, 서버마다 서로 다른 가중치를 적용할 수도 있다.
어떤 알고리즘을 선택하느냐에 따라 실제 요청 분포가 달라진다.
다만 여기에서 중요한 것은 Load Balancer가 서버를 선택한 이후에도 해당 서버가 실제 요청을 처리할 수 있는 상태인지 판단해야 한다는 점이다.
이때 Health Check가 중요해진다.
서버가 죽었는데 계속 요청을 보내면 어떻게 될까?
Backend Server 가운데 한 대가 장애 상태라고 가정해보자.
Server A : 정상
Server B : 장애
Server C : 정상
Load Balancer가 서버 상태를 전혀 확인하지 않는다면 요청 일부는 계속 Server B로 전달될 수 있다.
그 결과 사용자는 간헐적인 오류를 경험하게 된다.
예를 들어 세 번 중 한 번씩
502 Bad Gateway
503 Service Unavailable
Connection refused
Timeout
같은 문제가 나타날 수 있다.
이 때문에 Load Balancer는 Backend Server의 상태를 주기적으로 확인하는 기능을 사용한다.
이를 일반적으로 Health Check라고 한다.
예를 들어 HTTP 서비스라면 특정 경로에 요청을 보내 정상 응답 여부를 확인할 수 있다.
GET /health
정상 서버:
200 OK
장애 서버:
Timeout
Connection refused
5xx
처럼 판단할 수 있다.
문제가 있는 서버를 Backend Pool에서 일시적으로 제외하면 새로운 요청이 정상 서버로만 전달된다.
따라서 Load Balancer의 중요한 역할은 요청을 골고루 나누는 것뿐 아니라 현재 요청을 받을 수 있는 서버만 선택하는 것이다.
실제 장애에서는 어떤 증상이 나타날까?
Load Balancer 환경에서 자주 발생하는 문제 가운데 하나는 모든 서버가 정상처럼 보이는데 특정 요청에서만 오류가 발생하는 경우다.
예를 들어 Backend Server가 세 대 있다고 하자.
A : 정상
B : 정상
C : 애플리케이션 오류
C 서버의 프로세스 자체는 실행 중이고 /health 요청에는 200을 반환한다면 Load Balancer는 C 서버를 정상으로 판단할 수 있다.
하지만 실제 서비스 요청을 처리할 때 C 서버에서 오류가 발생하면 사용자는 간헐적인 장애를 경험한다.
이런 환경에서는
정상
정상
오류
정상
정상
오류
처럼 문제가 반복될 수도 있다.
또 다른 사례는 특정 Backend Server의 처리 속도만 느린 경우다.
서버는 죽지 않았기 때문에 Health Check를 통과하지만 실제 요청 처리 시간이 매우 길어진다.
이 경우 전체 서비스가 완전히 중단되지는 않지만 특정 요청에서 응답 지연이 발생한다.
따라서 Load Balancer 환경에서는 단순히
서버가 살아 있는가?
만 확인해서는 부족하다.
실제 애플리케이션을 정상적으로 처리할 수 있는가?
까지 확인하는 Health Check 설계가 중요하다.
요청이 실제 어느 서버로 전달되는지 어떻게 확인할까?
부하분산 문제가 의심될 때는 Load Balancer와 Backend Server 양쪽을 함께 확인해야 한다.
먼저 Backend Server의 Access Log를 확인하면 각 서버에 실제 요청이 얼마나 들어오는지 확인할 수 있다.
Nginx라면 일반적으로 Access Log에서 요청 기록을 확인한다.
예를 들어 각 서버에서 다음처럼 로그를 확인할 수 있다.
tail -f /var/log/nginx/access.log
이 명령을 사용하는 목적은 실시간으로 어느 Backend Server에 요청이 들어오는지 확인하기 위해서다.
A 서버에는 요청이 계속 들어오는데 B 서버에는 전혀 들어오지 않는다면 Load Balancer의 Backend 등록 상태나 Health Check 결과를 확인해야 한다.
반대로 모든 서버에 요청은 들어오지만 특정 서버에서만 5xx가 증가한다면 Load Balancer보다는 해당 Backend Server의 애플리케이션이나 리소스 문제일 가능성을 먼저 살펴봐야 한다.
서버별 요청량도 중요하다.
예를 들어 요청 수가 다음처럼 분포한다면
Server A : 10,000
Server B : 9,800
Server C : 10,200
비슷한 수준으로 분산되고 있다고 볼 수 있다.
하지만
Server A : 20,000
Server B : 9,500
Server C : 500
처럼 극단적인 차이가 있다면 이유를 분석해야 한다.
Health Check, 가중치, 세션 고정, 분산 알고리즘 등 여러 설정이 원인이 될 수 있기 때문이다.
Client IP가 Backend Server에서 다르게 보이는 이유
Reverse Proxy 방식의 Load Balancer 환경에서는 Backend Server가 직접 Client와 연결하지 않는 경우가 많다.
연결 구조는 다음과 같다.
Client
↓
Load Balancer
↓
Backend
이 때문에 Backend Server에서 네트워크 연결을 보면 Client IP가 아니라 Load Balancer의 IP가 보일 수 있다.
웹 애플리케이션에서 실제 사용자 IP가 필요하다면 일반적으로 HTTP 헤더를 이용해 원래 주소 정보를 전달한다.
대표적으로 다음과 같은 헤더가 사용될 수 있다.
X-Forwarded-For 또는 X-Real-IP
다만 이 값을 사용할 때는 신뢰할 수 있는 Proxy가 추가한 값인지 확인하는 설정이 중요하다.
클라이언트가 임의로 보낸 헤더까지 실제 사용자 IP로 신뢰하면 접근 제어나 로그 분석이 잘못될 수 있기 때문이다.
Load Balancer를 도입하면 단순히 네트워크 구조만 바뀌는 것이 아니라 로그 해석 방식에도 영향을 준다는 점을 기억해야 한다.
Load Balancer 자체가 병목이 되지는 않을까?
Backend Server를 여러 대로 늘려도 모든 요청이 Load Balancer를 통과한다면 또 다른 문제가 생길 수 있다.
Load Balancer 자체의 처리 능력이 부족하면 Backend Server에는 여유가 있는데도 서비스 전체가 느려질 수 있다.
운영 환경에서는 Load Balancer의
Connection 수
Request 처리량
응답 시간
Backend 연결 상태
Error 비율
CPU
Memory
Network
등을 함께 확인해야 한다.
특히 Reverse Proxy 방식에서는 Frontend Connection과 Backend Connection을 모두 관리해야 하기 때문에 연결 설정이 중요하다.
Backend 서버를 계속 추가한다고 서비스 처리량이 무조건 증가하는 것은 아니다.
Load Balancer, 네트워크 대역폭, 데이터베이스, 애플리케이션 같은 다른 구간에서 병목이 발생할 수 있기 때문이다.
Load Balancer를 안정적으로 운영하려면 무엇을 봐야 할까?
첫 번째는 Backend Server 상태를 정확히 판단할 수 있는 Health Check를 구성하는 것이다.
단순히 포트가 열려 있는지만 검사하면 애플리케이션 장애를 놓칠 수 있다.
두 번째는 Backend Server별 요청량과 응답 시간을 비교해야 한다.
특정 서버로 요청이 집중되거나 특정 서버만 응답 시간이 길어진다면 서버 자체 문제인지 분산 설정 문제인지 구분해야 한다.
세 번째는 Load Balancer 로그와 Backend 로그를 함께 분석해야 한다.
사용자는 Load Balancer에 연결하기 때문에 Backend Server 로그만 보면 전체 요청 흐름을 놓칠 수 있다.
네 번째는 Connection 구조를 이해해야 한다.
Client에서 Load Balancer까지의 Connection과 Load Balancer에서 Backend까지의 Connection은 서로 다른 구간이 될 수 있다.
따라서 Timeout이나 Keep-Alive 문제도 어느 구간에서 발생하는지 구분해서 봐야 한다.
정리
Load Balancer는 단순히 요청을 여러 서버에 골고루 나누는 장비나 프로그램이 아니다.
사용자가 서비스 주소로 요청을 보내면 Load Balancer가 먼저 요청을 받고 Backend Server의 상태와 분산 정책을 기준으로 실제 요청을 처리할 서버를 선택한다.
Reverse Proxy 방식에서는
Client ↔ Load Balancer 와 Load Balancer ↔ Backend Server가 서로 다른 연결로 관리될 수도 있다.
이 구조 때문에 Backend Server에서 보는 Client 정보가 달라질 수 있고 Timeout, Keep-Alive, Connection 문제도 각각 다른 구간에서 발생할 수 있다.
또한 Backend Server 한 대가 장애를 일으켰을 때 단순히 서버를 여러 대 운영하고 있다는 사실만으로 서비스가 안전해지는 것도 아니다.
Load Balancer가 서버 상태를 제대로 판단하고 장애 서버를 분산 대상에서 제외해야 실제 고가용성 구조가 만들어진다.
따라서 Load Balancer를 이해할 때 가장 중요한 것은 “어떤 서버에 몇 퍼센트씩 나누는가”보다 사용자의 하나의 요청이 Load Balancer를 거쳐 어떤 Connection을 만들고, 어떤 기준으로 Backend Server를 선택하며, 장애 서버를 어떻게 제외하는가를 이해하는 것이다.
이 흐름을 이해하면 이후 Round Robin, Least Connection, Health Check, Session Persistence 같은 부하분산 설정도 단순 옵션이 아니라 실제 요청 흐름 안에서 이해할 수 있다.




댓글 0
첫 댓글을 남겨보세요.