서버 한 대로는 트래픽을 감당 못 한다. 그래서 여러 대 띄우고 앞에 load balancer 를 둬서 요청을 나눈다. 말로는 단순한데 실전에서는 "L4 냐 L7 이냐", "어떤 알고리즘", "세션이 한 서버에 붙어야 하나", "죽은 서버를 어떻게 빼나", 그리고 "load balancer 자신이 죽으면?" 이 전부 결정 사항이다. 이 가이드는 그 결정들을 하나씩 정리한다.
왜 필요한가
서버 1대: CPU/메모리 한계 → 동시 접속 한계 → 한 대 죽으면 전체 down
여러 대 + LB:
┌──→ server A
client → [ LB ] ─┼──→ server B
└──→ server C
얻는 것:
- 수평 확장 (scale out): 서버 추가로 처리량 ↑
- 고가용성: 한 대 죽어도 LB 가 그 서버로 안 보냄
- 무중단 배포: 서버 한 대씩 빼고 → 배포 → 다시 넣기 (rolling)L4 vs L7 — 어느 계층에서 분배하나
가장 큰 갈림길. OSI 계층 어디서 트래픽을 보고 결정하느냐다.
L4 (transport, TCP/UDP):
- 패킷의 IP + port 만 보고 forward
- 내용(HTTP path, header, cookie)은 안 봄 — 못 봄
- connection 단위로 한 서버에 묶어 그대로 흘림 (passthrough)
- 빠르고 가벼움, TLS 그대로 통과(termination 안 함)
예: AWS NLB, IPVS, HAProxy(tcp mode)
L7 (application, HTTP/HTTPS):
- HTTP 요청을 실제로 파싱 — path, header, cookie, method
- "/api 는 backend A, /img 는 backend B" 같은 라우팅 가능
- TLS termination, 압축, 헤더 주입, 재시도, 캐시
- 더 똑똑하지만 CPU 비용 ↑ (요청마다 파싱)
예: AWS ALB, Nginx, HAProxy(http mode), Envoy간단히: L4 는 "주소만 보고 배달", L7 은 "내용 읽고 배달". 둘을 섞기도 한다 — 바깥은 L4 로 받아 대역폭 처리하고, 안쪽 L7 이 세밀한 라우팅.
분배 알고리즘
"어느 서버로 보낼까" 를 고르는 규칙. 정답은 트래픽 모양에 달렸다.
round-robin (순번):
A → B → C → A → B → C ...
단순/공평. 단 서버 성능 같고 요청 비용 비슷할 때만 공평.
요청 하나가 10초, 다른 건 10ms 면 → 불균형.
weighted round-robin (가중 순번):
A(weight 3) → A → A → B(weight 1) → A → A → A → B ...
서버 스펙 다를 때. 8코어:4코어 = weight 2:1.
least-connections (최소 연결):
현재 active connection 가장 적은 서버로.
요청 길이가 들쭉날쭉할 때 round-robin 보다 균형 잘 맞음.
long-lived connection(웹소켓 등)에 특히 유리.
least-response-time:
연결 수 + 평균 응답시간 조합. 느려진 서버 자동 회피.
IP hash:
hash(client IP) % 서버수 → 항상 같은 서버.
같은 클라이언트를 같은 서버에 고정(아래 sticky session 의 한 방법).
서버 수 바뀌면 대부분 재배치됨(consistent hashing 으로 완화).Health check — 죽은 서버 빼기
LB 의 핵심 기능. 뒤 서버가 살아있는지 주기적으로 확인하고, 죽었으면 풀에서 빼서 트래픽을 안 보낸다.
수동(passive) health check:
실제 트래픽의 실패를 관찰 — 연속 5xx/timeout 나면 그 서버 제외.
추가 요청 없음(가벼움). 단 첫 사용자가 실패를 떠안음.
능동(active) health check:
LB 가 주기적으로 probe (예: 5초마다 GET /healthz)
- 연속 N회 실패 → unhealthy → 풀에서 제외
- 다시 연속 M회 성공 → healthy → 복귀
사용자 요청 전에 미리 감지.
좋은 /healthz 설계:
- 단순 200 만 X — DB/캐시 등 의존성도 점검(shallow vs deep)
- 단 너무 deep 하면 DB 살짝 흔들려도 전 서버 unhealthy → 전멸
- "나는 요청 처리 가능한가" 만 답하게(liveness vs readiness 구분)Sticky session (session affinity)
같은 클라이언트의 요청을 매번 같은 서버로 보내는 것. 서버가 세션 상태를 자기 메모리에 들고 있을 때 필요하다.
왜 필요?
로그인 세션을 server A 메모리에 저장 → 다음 요청이 B 로 가면
"로그인 안 됨" 상태 → 깨짐.
방법:
1) IP hash — client IP 기준 고정. 단 NAT 뒤 수천 명이 한 IP면 쏠림.
2) cookie 기반 — LB 가 어느 서버인지 cookie 심음(예: AWSALB).
가장 정확. NAT 영향 없음.
대가:
- 균형이 깨짐(특정 서버에 무거운 사용자 몰림)
- 그 서버 죽으면 → 거기 묶인 세션 전부 날아감
더 나은 길 — stateless 서버:
세션을 서버 메모리가 아닌 공유 저장소(Redis/DB)나 JWT 로.
그러면 sticky 불필요 → 어느 서버든 처리 가능 → 균형/장애 내성 ↑.
실무 권장: 가능하면 sticky 를 없애는 방향으로 설계.LB 자신의 고가용성
LB 가 single point of failure 면 의미가 없다. LB 자체도 이중화한다.
active-passive (failover):
LB1(active) + LB2(standby) 가 하나의 가상 IP(VIP) 공유.
- VRRP/keepalived 로 서로 heartbeat
- active 죽으면 → passive 가 VIP 인수(takeover) → 트래픽 이어받음
- standby 는 평소 놀고 있음(비용)
active-active:
여러 LB 가 동시에 트래픽 받음. 앞단 분배 필요 →
보통 DNS round-robin 또는 anycast 로 해결.
anycast:
같은 IP 를 여러 지역 LB 가 동시에 광고(BGP).
네트워크가 "가장 가까운" LB 로 자동 라우팅.
- 한 지역 LB/회선 죽으면 → BGP 가 다음 가까운 곳으로 재수렴
- 지연 ↓ + 장애 격리. CDN/대형 서비스의 표준.전체 그림
DNS (anycast IP 하나 반환)
│
┌─────────────┼─────────────┐
[LB region A] [LB region B] [LB region C] ← anycast, 가까운 곳
│
L7 라우팅 + health check + (가능하면 no sticky)
│
┌────┼────┐
srv srv srv ← stateless, 세션은 Redis/JWT
│
공유 세션 저장소 / DB흔한 함정
- health check 가 너무 deep — DB 가 잠깐 느려지면 모든 서버가 unhealthy 로 빠져 전멸. liveness(나 살아있나) 와 readiness(트래픽 받을 준비) 를 분리하라.
- sticky 에 의존하다 서버 죽음 — 묶인 세션이 통째로 증발. 가능하면 stateless 로 가고, 어쩔 수 없으면 세션을 공유 저장소에 둬라.
- least-connections 인데 connection ≠ 부하 — 어떤 요청은 연결만 잡고 거의 idle(웹소켓), 어떤 건 짧지만 CPU 폭발. 연결 수가 실제 부하를 반영하는지 확인.
- graceful shutdown 없이 서버 제거 — 진행 중인 요청이 끊김. 배포 시 "draining"(새 요청은 안 받고 기존 요청만 마무리) 단계를 둬라.
- thundering herd on recovery — 죽었던 서버가 돌아오자마자 모든 health check 가 동시에 healthy 판정 → 트래픽이 한꺼번에 쏠려 다시 죽음. slow start(가중치 점진 증가) 로 완화.
- TLS termination 위치 혼동 — L7 에서 termination 하면 LB→서버 구간이 평문일 수 있다. 내부도 암호화할지(re-encrypt) 정책으로 결정.
마무리
Load balancer 는 단순히 "요청 나누기" 가 아니라 L4/L7 선택 · 알고리즘 · health check · 세션 전략 · 자신의 이중화 가 한 묶음인 결정이다. 핵심 원칙 하나만 챙기면: 서버를 stateless 하게 만들수록 LB 가 단순하고 강해진다. 그러면 sticky session 도, "그 서버 죽으면 세션 날아감" 도 사라진다.