[대규모 시스템] Day 3: 로드밸런싱과 수평 확장 - 서버를 여러 대로 늘리기
[대규모 시스템] Day 3: 로드밸런싱과 수평 확장 - 서버를 여러 대로 늘리기
이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.
서론: 한 대를 키우기 vs 여러 대로 늘리기
서버가 한계에 닿으면 두 갈래다. 더 큰 서버로 바꾸거나(수직 확장), 서버를 여러 대로 늘리거나(수평 확장). 수직 확장은 한계가 있고 단일 장애점이다. 대규모 시스템은 결국 수평 확장으로 간다. 그 전제 조건과 트래픽 분산 장치가 로드밸런서다.
1. 수직 확장 vs 수평 확장
1
2
3
4
5
6
7
8
9
수직 확장(Scale Up): 서버 사양을 키움 (CPU·RAM 증설)
✅ 간단, 코드 변경 없음
❌ 물리적 상한, 비용 급증, 단일 장애점, 무중단 불가
수평 확장(Scale Out): 서버 대수를 늘림
✅ 사실상 무한 확장, 장애 내성, 무중단 증설
❌ 무상태 설계 필요, 로드밸런서·분산 복잡도
대규모의 정석: 수평 확장. 단, 이를 위해 "무상태"가 전제다.
2. 무상태(Stateless)가 핵심 전제
수평 확장이 되려면 어느 서버가 요청을 받아도 같은 결과를 줘야 한다.
1
2
3
4
5
6
7
상태가 있는 서버 (확장 불가):
로그인 세션을 서버 A의 메모리에 저장
→ 다음 요청이 서버 B로 가면 "로그인 안 됨" (세션 없음)
무상태 서버 (확장 가능):
세션을 외부 공유 저장소(Redis - Day 1)나 토큰(JWT)에 둠
→ 어느 서버로 가도 동일하게 동작
원칙: 상태를 앱 서버 밖으로 밀어낸다. 세션은 Redis나 토큰, 파일은 객체 스토리지(S3), 데이터는 DB로. 앱 서버는 계산만 하는 교체 가능한 부품이 된다.
3. 로드밸런싱 알고리즘
1
2
3
4
5
Round Robin: 순서대로 분배. 가장 단순, 서버 성능이 균일할 때
Weighted RR: 서버 성능에 가중치 → 강한 서버에 더 많이
Least Connections: 현재 연결 수가 적은 서버로 → 요청 처리시간이 들쭉날쭉할 때
IP Hash: 클라이언트 IP 해시로 고정 → 세션 고정(sticky)이 필요할 때
Least Response Time: 응답이 빠른 서버 우선
대부분 Round Robin이나 Least Connections로 시작한다. gRPC 같은 장수명 연결은 L7 요청 단위 분산이 필요하다(gRPC Day 5 참고).
4. L4 vs L7 로드밸런서
1
2
3
4
5
6
7
8
9
10
11
L4 (전송 계층, TCP/UDP):
IP·포트만 보고 분배. 패킷 내용 안 봄.
✅ 매우 빠름, 프로토콜 무관
❌ 콘텐츠 기반 라우팅 불가
L7 (애플리케이션 계층, HTTP):
URL·헤더·쿠키를 보고 분배.
✅ 경로별 라우팅(/api → A, /img → B), TLS 종료, 콘텐츠 캐싱
❌ L4보다 느리고 비쌈
실무: L7(nginx, Envoy, ALB)이 유연성으로 가장 흔하다.
5. 헬스체크와 장애 제거
로드밸런서는 죽은 서버에 트래픽을 보내지 않아야 한다.
1
2
3
4
5
6
7
8
9
10
헬스체크:
LB가 주기적으로 각 서버에 /health 요청
→ 실패하면 풀에서 자동 제외, 복구되면 자동 복귀
Active: LB가 능동적으로 핑 (주기적 요청)
Passive: 실제 요청의 실패율을 보고 판단
우아한 종료(graceful shutdown):
서버 종료 시 LB가 먼저 트래픽을 끊고(draining),
진행 중 요청을 마친 뒤 종료 → 무중단 배포의 핵심
1
2
3
4
5
6
# 헬스 엔드포인트는 의존성까지 확인해야 의미가 있다
@app.route("/health")
def health():
if not db.ping() or not redis.ping():
return "unhealthy", 503 # LB가 이 서버를 제외
return "ok", 200
6. 오토스케일링
1
2
3
4
5
6
7
8
트래픽에 따라 서버 대수를 자동 조절:
지표(CPU·요청율·큐 lag - Day 2) 임계 초과 → 인스턴스 추가
한가하면 → 인스턴스 축소 (비용 절감)
주의:
- 스케일아웃은 즉시 안 됨(부팅 시간) → 선제적 스케일링·여유 용량
- 플래핑(껐다 켰다 반복) 방지: 쿨다운·히스테리시스
- DB 연결 풀: 서버가 늘면 DB 커넥션도 폭증 → 커넥션 풀러(PgBouncer)
오토스케일링은 앱이 무상태일 때만 안전하다. 상태가 있으면 새 인스턴스가 제 역할을 못 한다.
7. 다층 아키텍처로 종합
1
2
3
4
5
6
7
8
9
10
11
[DNS / GeoDNS] ← 지역별 분산
│
[CDN] ← 정적 자원 (Day 1)
│
[L7 Load Balancer] ← TLS 종료, 경로 라우팅
/ │ \
[App1] [App2] [App3] ← 무상태, 오토스케일
\ │ /
[Redis 캐시] [메시지 큐] ← Day 1, Day 2
│
[DB Primary] → [Read Replicas] ← 읽기 분산 (분산 시스템 Day 2)
각 계층이 독립적으로 확장된다. 병목이 생긴 계층만 늘리면 된다.
8. Day 3 체크리스트
- 수평 확장이 대규모의 정석이며 무상태가 전제임을 이해했다.
- 상태(세션·파일)를 앱 서버 밖으로 밀어내는 설계를 파악했다.
- 로드밸런싱 알고리즘과 L4/L7의 차이를 구분했다.
- 헬스체크와 우아한 종료로 무중단 운영의 기반을 잡았다.
- 오토스케일링의 주의점(부팅 지연·플래핑·DB 커넥션)을 인지했다.
다음 편 예고
서버를 늘려도 한 클라이언트가 트래픽을 독점하거나 악용하면 시스템이 위험하다. Day 4에서는 레이트 리미팅으로 시스템을 보호하고 공정하게 자원을 분배하는 알고리즘을 다룬다.
This post is licensed under CC BY 4.0 by the author.