로드 밸런서
서비스의 사용자가 늘어나면, 하나의 서버로는 모든 요청을 처리할 수 없습니다. 서버를 여러 대로 확장(Scale Out)하면, 들어오는 요청을 어떤 기준으로 어떤 서버에 보낼지 결정해야 합니다. 이 역할을 하는 것이 로드 밸런서(Load Balancer)입니다.
L4 vs L7 로드 밸런서
로드 밸런서는 동작하는 계층에 따라 두 종류로 나뉩니다.
L4 (NLB)
┌──────────────┐
│ 클라이언트 │
│ 요청 │
└──────┬───────┘
▼
┌──────────────┐
│ IP + Port만 │
│ 확인 │
│ 빠름, 저비용 │
└──────┬───────┘
▼
S1 / S2 / S3
L7 (ALB)
┌──────────────┐
│ 클라이언트 │
│ 요청 │
└──────┬───────┘
▼
┌──────────────┐
│ HTTP 헤더 │
│ URL 경로 │
│ 쿠키, 호스트 │
│ 느림, 고비용 │
└──────┬───────┘
▼
/api → API 서버
/web → Front 서버
/static → CDN 서버L4 로드 밸런서는 전송 계층(TCP/UDP)에서 동작합니다. IP 주소와 포트 번호만 보고 트래픽을 분배합니다. 패킷의 내용(HTTP 헤더, URL 등)은 검사하지 않습니다. 처리가 빠르고 오버헤드가 적습니다. AWS의 NLB(Network Load Balancer)가 대표적입니다.
L7 로드 밸런서는 애플리케이션 계층(HTTP)에서 동작합니다. HTTP 헤더, URL 경로, 쿠키 등을 분석하여 라우팅 결정을 내립니다. /api/* 요청은 API 서버 그룹으로, /static/* 요청은 정적 파일 서버 그룹으로 보내는 것이 가능합니다. AWS의 ALB(Application Load Balancer)가 대표적입니다.
| 비교 항목 | L4 (NLB) | L7 (ALB) |
|---|---|---|
| 동작 계층 | TCP/UDP | HTTP/HTTPS |
| 라우팅 기준 | IP, 포트 | URL, 헤더, 쿠키 |
| TLS 종단 | 패스스루 가능 | 필수 복호화 |
| 지연 시간 | 매우 낮음 (~μs) | 낮음 (~ms) |
| WebSocket | 지원 | 지원 |
| 가격 | 저렴 | 비쌈 |
| 적합한 사용처 | TCP 서비스, 게임, IoT | 웹 API, 마이크로서비스 |
로드 밸런싱 알고리즘
라운드 로빈 (Round Robin)
요청1 → S1 요청2 → S2 요청3 → S3 요청4 → S1
(S1이 느려도 같은 비율로 배분)
최소 연결 (Least Connections)
S1: 활성 15개 S2: 활성 3개 S3: 활성 8개
→ 새 요청 → S2 (가장 여유 있음)
(처리 시간이 불균일할 때 효과적)| 알고리즘 | 동작 방식 | 장점 | 단점 | 사용 시나리오 |
|---|---|---|---|---|
| 라운드 로빈 | 순서대로 분배 | 단순, 균등 | 서버 성능 차이 무시 | 동일 스펙 서버 |
| 가중치 라운드 로빈 | 가중치 비율 분배 | 성능 차이 반영 | 가중치 수동 설정 | 서버 스펙 혼합 |
| 최소 연결 | 연결 수 최소 서버로 | 동적 부하 반영 | 상태 추적 비용 | 요청 처리 시간 불균일 |
| 가중치 최소 연결 | 가중치 + 최소 연결 | 가장 정밀 | 구현 복잡 | 혼합 스펙 + 불균일 부하 |
| IP 해시 | IP 해시로 고정 서버 | 세션 유지 | 불균형 가능 | Sticky 필요 시 |
| 랜덤 | 무작위 선택 | 구현 최단순 | 비효율 가능 | 테스트 |
헬스 체크
로드 밸런서
│
├── 10초마다 ──→ S1: GET /health → 200 OK ✓ (정상)
│
├── 10초마다 ──→ S2: GET /health → 200 OK ✓ (정상)
│
└── 10초마다 ──→ S3: GET /health → 연결 시간 초과
3회 연속 실패 → 비정상(unhealthy) 판정
→ 트래픽 차단
복구 후 2회 연속 성공 → 정상(healthy) 복귀헬스 체크는 보통 /health 엔드포인트에 HTTP GET 요청을 보내고, 200 응답을 받는지 확인합니다. 단순히 프로세스가 살아있는지뿐 아니라, 데이터베이스 연결이나 외부 서비스 연동까지 확인하는 것이 좋습니다.
# Flask 헬스 체크 엔드포인트 예
from flask import Flask, jsonify
import psycopg2
app = Flask(__name__)
@app.route("/health")
def health():
checks = {"status": "healthy"}
# DB 연결 확인
try:
conn = psycopg2.connect("dbname=mydb user=app")
conn.close()
checks["database"] = "ok"
except Exception:
checks["database"] = "fail"
checks["status"] = "unhealthy"
return jsonify(checks), 503
return jsonify(checks), 200서버가 복구되면 다시 정상으로 판정하고 트래픽을 보내기 시작합니다. 이 과정이 자동화되어 있으므로, 서버 장애가 발생해도 사용자는 다른 정상 서버를 통해 서비스를 계속 이용할 수 있습니다.
Sticky Session과 세션 공유
세션 기반 인증에서 사용자의 세션이 서버 A의 메모리에 저장되어 있다면, 다음 요청이 서버 B로 가면 로그인 상태가 유지되지 않습니다.
Sticky Session
사용자A ──항상──→ S1 (세션 저장) ← S1 장애 시 세션 소실!
사용자B ──항상──→ S1 ← 특정 서버에 쏠림!
사용자C ──항상──→ S2
공유 세션 (Redis)
사용자A ──→ S1 ──┐
사용자B ──→ S2 ──┼──→ Redis (세션 저장소)
사용자C ──→ S3 ──┘ 어떤 서버든 동일 세션 접근
토큰 기반 (JWT)
사용자A ──→ S1 ──→ JWT 검증 (서버 상태 불필요)
사용자A ──→ S3 ──→ JWT 검증 (동일하게 작동)| 방식 | 서버 장애 시 | 확장성 | 구현 복잡도 |
|---|---|---|---|
| Sticky Session | 세션 소실 | 낮음 (불균형) | 낮음 |
| Redis 세션 공유 | 세션 유지 | 높음 | 중간 (Redis 운영) |
| JWT 토큰 | 영향 없음 | 매우 높음 | 중간 (토큰 설계) |
올바른 해결책은 세션을 외부 저장소에서 공유하거나 JWT 같은 토큰 기반 인증을 사용하는 것입니다.
다음 절에서는 로드 밸런서와 함께 자주 등장하는 프록시와 VPN을 다루겠습니다.