서버가 여러 대면 알아서 골고루 나눠질 것 같죠? 사실은 어떤 기준으로 나누느냐에 따라 같은 사용자가 계속 같은 서버로 갈 수도, 매번 다른 서버로 갈 수도 있어요.Proxy, Reverse Proxy, 그리고 Load Balancer에서는 서버 앞단이 요청을 먼저 받고 여러 서버 중 하나를 고를 수 있다는 큰 그림을 봤어요. 그리고 L4와 L7 로드 밸런서에서는 앞단이 IP·포트만 보는지, HTTP Host·path까지 읽는지에 따라 판단 재료가 달라진다는 걸 봤죠. 이번에는 로드 밸런서가 어떤 서버를 고르는 방식을 더 자세히 볼게요. 운영 중에는 이런 말을 자주 만나요.
- 로그인했는데 다음 요청에서 로그인이 풀려요.
- 서버 한 대가 유독 바빠요.
- 새 버전을 조금만 배포했는데 특정 사용자만 계속 새 버전을 봐요.
- 장애 서버를 빼도 일부 사용자는 계속 그쪽으로 가는 것처럼 보여요.
“이 사용자의 다음 요청은 같은 서버로 가야 할까요, 아니면 아무 건강한 서버로 가도 될까요?”
여기서는 특정 제품의 설정 문법보다 로드 밸런싱 방식, sticky session/session affinity, stateful 앱과 stateless 앱의 차이, 운영 중 읽어야 할 신호에 집중해요. 실제 제품은 가중치, health check, slow start, consistent hashing 같은 세부 기능을 섞어 제공할 수 있으니, 이름보다 무엇을 기준으로 서버를 고르는지를 먼저 보세요.
식당이 손님을 빈자리로 보낼 수도, 같은 담당 직원에게 붙일 수도 있어요
식당 입구에서 손님을 안내하는 장면을 떠올려볼게요.- 어떤 식당은 손님이 올 때마다 가장 덜 바쁜 테이블로 안내해요.
- 어떤 식당은 순서대로 1번, 2번, 3번 테이블에 번갈아 앉혀요.
- 그런데 상담이 이어지는 손님이라면, 다음 방문 때도 이전 담당 직원에게 보내는 편이 좋을 수 있어요.
핵심은 요청을 나누는 기준이에요. 로드 밸런서는 단순히 “여러 대로 보내는 장치”가 아니라, 매 요청 또는 매 연결마다 어떤 후보를 고를지 판단하는 장치예요.
이 그림에서 중요한 건 “서버가 여러 대”라는 사실보다 선택 기준이에요. 선택 기준이 바뀌면 성능, 세션, 장애, 배포를 읽는 방식도 같이 바뀌어요.
Round Robin은 순서대로 나눠 보내는 가장 쉬운 그림이에요
Round robin은 후보 서버를 순서대로 돌면서 요청을 보내는 방식이에요.
예를 들어 요청 1, 2, 3이 모두 비슷하게 50ms 안에 끝난다면 round robin은 괜찮아요. 그런데 요청 1은 10초짜리 리포트 생성이고, 요청 2와 3은 20ms짜리 상태 조회라면 어떨까요?
겉으로는 한 건씩 나눴지만, 실제로는
app-a가 훨씬 오래 바쁠 수 있어요.
이 그림은 round robin의 한계를 보여줘요. 로드 밸런서가 요청의 무게를 모르면, 건수는 균등해도 체감 부하는 균등하지 않을 수 있어요.
Least Connections는 지금 덜 바쁜 쪽을 보려는 방식이에요
Least connections는 현재 연결 수가 적은 서버를 고르는 방식이에요. 이름 그대로 “지금 연결을 덜 들고 있는 서버”를 우선하는 감각이에요.
Connection reuse, Keep-Alive, Pooling에서 본 것처럼, 연결은 요청과 같은 단위가 아니에요. 그래서 least connections를 볼 때도 “연결 수가 적다”를 곧바로 “실제로 한가하다”로 읽으면 안 돼요.
위쪽은 연결이 하나지만 요청이 여러 개예요. 아래쪽은 연결이 세 개지만 요청이 단순할 수 있어요. 그래서 “least connections”라는 이름은 실제 구현과 프로토콜 버전까지 같이 읽어야 해요.
Hash 기반 분산은 같은 입력을 같은 서버 쪽으로 보내려는 방식이에요
Hash 기반 분산은 어떤 값을 넣으면, 그 값으로 서버를 고르는 방식이에요. 예를 들어 출발지 IP를 기준으로 해시하면 이런 식의 감각이 돼요.
여기서 source IP hash는 특히 함정이 있어요. 회사, 학교, 통신사 NAT 뒤에 많은 사용자가 같은 공인 IP로 보일 수 있거든요. 그러면 로드 밸런서는 “같은 사용자”라고 착각한 게 아니라, 같은 출발지 IP라는 입력을 같은 서버로 보낸 것뿐이에요.
이 장면에서는 알고리즘이 고장 난 게 아닐 수 있어요. 해시 기준으로 고른 값이 실제 사용자 분포를 잘 표현하지 못했을 뿐이에요.
Sticky Session은 이전에 고른 서버를 계속 기억하려는 방식이에요
Sticky session은 같은 클라이언트의 다음 요청을 되도록 같은 백엔드로 보내는 방식이에요. 다른 이름으로 session affinity라고도 불러요. 가장 흔한 장면은 쿠키 기반 sticky예요.app-b로 보내려고 할 수 있어요.
app-b 메모리에만 로그인 세션이나 장바구니 임시 상태가 있다면, 다음 요청이 app-a로 가는 순간 사용자는 로그아웃된 것처럼 느낄 수 있어요.
하지만 sticky session은 치료제처럼 보이면서도 비용이 있어요. 같은 사용자를 같은 서버에 붙잡아두면, 서버를 마음대로 빼거나 트래픽을 자연스럽게 재분배하기가 어려워질 수 있어요.
Sticky는 상태 저장 문제를 숨겨줄 수 있지만, 없애주지는 않아요
여기서 중요한 구분이 나와요.- 앱이 요청마다 필요한 상태를 중앙 저장소나 토큰에서 읽으면, 어느 서버로 가도 처리하기 쉬워요.
- 앱이 서버 메모리에 사용자 상태를 들고 있으면, 같은 사용자 요청이 같은 서버로 가야 안전해져요.
Sticky session은 stateful 앱을 당장 운영 가능하게 도와줄 수 있어요. 하지만 근본적으로는 상태가 특정 서버에 묶여 있다는 사실이 남아 있어요.
이 구조에서는
app-b가 내려가면 sticky가 더는 사용자를 구해주지 못해요. 사용자는 다른 서버로 이동할 수는 있지만, app-b 메모리에 있던 세션은 같이 이동하지 않아요.
반대로 세션을 공유 저장소에 두면 그림이 달라져요.
이렇게 되면 sticky가 없어도 다음 요청을 처리하기 쉬워져요. 물론 공유 저장소 자체의 지연, 장애, 비용은 새로 봐야 해요. 하지만 적어도 “어느 앱 서버 메모리에 들어 있느냐”에 묶이는 문제는 줄어들어요.
배포와 장애에서는 sticky가 예상보다 오래 남을 수 있어요
Sticky session은 장애와 배포를 볼 때 특히 중요해져요. 예를 들어 서버 세 대가 있고, 사용자가 쿠키로app-b에 붙어 있다고 해볼게요. 이제 app-b에 새 버전을 배포했는데 버그가 있어요. 그러면 모든 사용자가 망가지는 게 아니라, app-b에 붙은 사용자만 계속 그 버그를 볼 수 있어요.
여기서 drain은 서버를 바로 끊어내지 않고, 새 요청은 줄이면서 기존 연결이나 기존 사용자가 정리될 시간을 주는 운영 동작이에요. 제품마다 이름은 다르지만, “서버를 풀에서 빼는 중인데 기존 트래픽을 어떻게 처리하나”라는 질문은 공통이에요.
서버를 뺀다는 말은 단순히 “목록에서 삭제”가 아니에요. sticky가 있으면 이미 그 서버에 붙어 있던 사용자와 연결을 어떻게 보낼지까지 같이 결정해야 해요.
운영 화면에서는 이런 신호를 같이 읽어요
Sticky session이나 로드 밸런싱 방식이 의심될 때는 아래 신호를 같이 보면 좋아요.
특히 쿠키 기반 sticky를 볼 때는 쿠키가 “설정됐다”만 보면 부족해요. 다음 요청에 그 쿠키가 실제로 붙어야 해요.
알고리즘 이름만으로 장애 원인을 단정하면 위험해요
로드 밸런싱 방식은 로그에서 짧게 보이지만, 실제 판단은 여러 조건이 겹쳐요.
이 표의 목적은 “어떤 알고리즘이 정답”을 고르는 게 아니에요. 장애를 만났을 때 서버 선택 기준을 증거로 확인하는 순서를 만들기 위한 거예요.
잘못 읽기 쉬운 함정
Sticky session이 있으면 세션 문제가 해결됐다고 보기
Sticky는 같은 사용자를 같은 서버로 보내려는 장치예요. 서버가 죽거나 drain되거나 쿠키가 사라지면 상태 문제는 다시 드러날 수 있어요. 중요한 상태라면 공유 저장소, 토큰, 재시도 가능한 설계까지 같이 봐야 해요.Source IP hash를 사용자 단위 고정으로 생각하기
source IP는 사용자 ID가 아니에요. NAT, VPN, 모바일망, 프록시를 거치면 많은 사용자가 같은 IP로 보일 수 있고, 한 사용자의 IP가 바뀔 수도 있어요. IP hash는 “IP 기준 고정”이지 “사람 기준 고정”이 아니에요.Least connections면 항상 가장 한가한 서버를 고른다고 보기
연결 수는 중요한 신호지만 CPU, 메모리, 요청 처리 시간, HTTP/2 스트림 수, DB 대기 시간을 모두 대표하지는 못해요. 그래서 least connections도 실제 metric과 같이 봐야 해요.Stateless로 만들면 로드 밸런싱 고민이 사라진다고 보기
stateless에 가까워지면 sticky 의존은 줄어들어요. 하지만 rate limit, 캐시 warm-up, tenant별 데이터 쏠림, DB pool, 외부 API 제한처럼 다른 분산 문제가 남을 수 있어요.Health check만 통과하면 그 서버로 보내도 안전하다고 보기
health check는 보통 작은 확인이에요./health는 정상인데 로그인 세션 저장소만 느리거나, 특정 path만 실패할 수 있어요. 로드 밸런서가 보는 건강과 사용자가 느끼는 건강은 다를 수 있어요.
장애를 만났을 때는 이렇게 좁혀봐요
로그인 풀림, 특정 서버 쏠림, 배포 후 일부 사용자만 실패 같은 증상을 만나면 아래 순서로 좁혀볼 수 있어요. 이 흐름에서 핵심은 “로드 밸런서가 이상하다”로 뭉개지 않는 거예요. 어떤 기준으로 서버가 선택됐고, 그 기준이 지금 증상과 맞물리는지를 확인해야 해요. 실전에서는 이런 질문을 남겨두면 좋아요.자, 정리해볼까요?
로드 밸런서는 여러 서버 중 하나를 고를 때 순서, 연결 수, hash, sticky 정보 같은 기준을 써요.round robin은 단순하지만 요청의 무게와 사용자 상태를 모를 수 있어요.least connections는 현재 연결 수를 보지만, 연결 수가 실제 부하를 완벽하게 대표하지는 않아요.hash 기반 분산은 같은 입력을 같은 서버로 보내려 하지만, source IP 같은 입력은 사용자 단위와 다를 수 있어요.sticky session은 같은 사용자를 같은 서버로 보내는 데 도움을 주지만, 서버 로컬 상태 문제를 근본적으로 없애지는 않아요.배포, drain, health check, canary를 볼 때는 sticky 사용자가 어디로 재배정되는지까지 같이 봐야 해요.
이어서 보면 좋은 글
- L4와 L7 로드 밸런서는 무엇을 보고 나눠 보낼까요? — 서버 선택 전에 앞단이 어떤 정보까지 읽는지 먼저 정리하고 싶을 때 좋아요.
- Connection reuse, Keep-Alive, Pooling은 왜 같이 봐야 할까요? — 서버 선택 뒤 연결을 새로 여는지, pool에서 다시 쓰는지 이어서 볼 수 있어요.
- 502, 503, 504는 어디서 만든 응답일까요? — 특정 백엔드 실패가 앞단 오류로 어떻게 보이는지 같이 읽기 좋아요.
이어서 볼 질문
다음에는 서버 앞단을 지나 캐시 쪽으로 시선을 옮겨서,Cache-Control과 Age 헤더를 보고 지금 보는 응답이 새 원본인지, 오래된 사본인지 읽어볼 수 있어요.