Load Balancer
로드 밸런서
트래픽을 여러 서버에 분산. L4(TCP), L7(HTTP). 고가용성 핵심.
로드 밸런서
트래픽을 여러 서버에 분산. L4(TCP), L7(HTTP). 고가용성 핵심.
로드 밸런서(Load Balancer)는 들어오는 네트워크 트래픽을 여러 백엔드 서버에 균등하게 분산시켜 서비스의 가용성과 성능을 높이는 핵심 인프라 컴포넌트입니다. 단일 서버가 모든 요청을 처리하면 병목이 발생하고 장애 시 서비스 전체가 중단되지만, 로드 밸런서를 통해 트래픽을 분산하면 수평 확장(Scale-out)이 가능해지고 무중단 서비스를 구현할 수 있습니다.
로드 밸런서는 작동하는 OSI 계층에 따라 L4와 L7으로 구분됩니다. L4(Transport Layer) 로드 밸런서는 TCP/UDP 포트 정보만으로 라우팅하여 빠르지만 단순합니다. AWS NLB가 대표적입니다. L7(Application Layer) 로드 밸런서는 HTTP 헤더, URL 경로, 쿠키 등 애플리케이션 레벨 정보를 분석하여 더 정교한 라우팅이 가능합니다. AWS ALB, Nginx, HAProxy가 대표적입니다.
트래픽 분산 알고리즘에는 여러 종류가 있습니다. Round Robin은 순서대로 서버에 요청을 분배하는 가장 단순한 방식입니다. Least Connections는 현재 연결 수가 가장 적은 서버로 보내 부하를 균등하게 합니다. IP Hash는 클라이언트 IP 기반으로 항상 같은 서버로 연결하여 세션 유지가 필요한 경우에 사용합니다. Weighted 방식은 서버 성능에 따라 가중치를 부여해 고성능 서버가 더 많은 요청을 처리합니다.
Health Check는 로드 밸런서의 핵심 기능으로, 주기적으로 백엔드 서버의 상태를 확인합니다. HTTP 200 응답, TCP 포트 연결, 커스텀 스크립트 등으로 서버 정상 여부를 판단하고, 장애 서버는 자동으로 트래픽 풀에서 제외됩니다. Sticky Session(세션 고정)은 같은 사용자의 요청을 동일 서버로 보내 세션 데이터 일관성을 유지하며, 쿠키나 소스 IP 기반으로 구현됩니다.
# /etc/nginx/nginx.conf - Nginx 로드 밸런서 설정
http {
# 업스트림 서버 그룹 정의
upstream backend_servers {
# 로드 밸런싱 알고리즘 (기본: Round Robin)
# least_conn; # 최소 연결 방식
# ip_hash; # IP 해시 (Sticky Session)
# 백엔드 서버 목록 (weight로 가중치 설정)
server 10.0.1.10:8080 weight=3; # 높은 성능 서버
server 10.0.1.11:8080 weight=2;
server 10.0.1.12:8080 weight=1;
server 10.0.1.13:8080 backup; # 백업 서버 (다른 서버 장애 시 사용)
# Health Check 설정
keepalive 32; # 업스트림 연결 유지
}
# API 서버용 별도 업스트림
upstream api_servers {
least_conn; # API는 최소 연결 방식 적합
server 10.0.2.10:3000;
server 10.0.2.11:3000;
}
server {
listen 80;
listen 443 ssl;
server_name example.com;
# SSL 설정
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# L7 라우팅: URL 경로별 분기
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 타임아웃 설정
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
location /api/ {
proxy_pass http://api_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# Health Check 엔드포인트
location /health {
access_log off;
return 200 "OK";
}
}
}
# /etc/haproxy/haproxy.cfg - HAProxy 고급 설정
global
maxconn 50000
log stdout format raw local0
stats socket /var/run/haproxy.sock mode 660
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3
# 프론트엔드: 클라이언트 요청 수신
frontend http_front
bind *:80
bind *:443 ssl crt /etc/ssl/certs/example.pem
# HTTP → HTTPS 리다이렉트
redirect scheme https if !{ ssl_fc }
# ACL 기반 라우팅 (L7)
acl is_api path_beg /api
acl is_static path_beg /static /images /css /js
acl is_websocket hdr(Upgrade) -i websocket
# ACL에 따른 백엔드 분기
use_backend api_backend if is_api
use_backend static_backend if is_static
use_backend ws_backend if is_websocket
default_backend web_backend
# 백엔드: 웹 서버 그룹
backend web_backend
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
# Sticky Session (쿠키 기반)
cookie SERVERID insert indirect nocache
server web1 10.0.1.10:8080 check cookie web1 weight 100
server web2 10.0.1.11:8080 check cookie web2 weight 100
server web3 10.0.1.12:8080 check cookie web3 weight 50 backup
# 백엔드: API 서버 그룹
backend api_backend
balance leastconn
option httpchk GET /api/health
http-check expect status 200
server api1 10.0.2.10:3000 check inter 3s fall 3 rise 2
server api2 10.0.2.11:3000 check inter 3s fall 3 rise 2
# 백엔드: 정적 파일 (캐시 서버)
backend static_backend
balance uri # URL 해시로 캐시 효율 극대화
server cache1 10.0.3.10:80 check
server cache2 10.0.3.11:80 check
# 통계 대시보드
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if TRUE
# AWS Application Load Balancer (ALB) with Terraform
# ALB 생성
resource "aws_lb" "main" {
name = "my-app-alb"
internal = false
load_balancer_type = "application" # "network" for NLB
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids
enable_deletion_protection = true
enable_http2 = true
access_logs {
bucket = aws_s3_bucket.alb_logs.id
prefix = "alb-logs"
enabled = true
}
tags = { Name = "my-app-alb" }
}
# 타겟 그룹 (백엔드 서버 풀)
resource "aws_lb_target_group" "app" {
name = "my-app-tg"
port = 8080
protocol = "HTTP"
vpc_id = var.vpc_id
target_type = "instance" # "ip" for Fargate
# Health Check 설정
health_check {
enabled = true
path = "/health"
port = "traffic-port"
protocol = "HTTP"
healthy_threshold = 2
unhealthy_threshold = 3
timeout = 5
interval = 30
matcher = "200-299"
}
# Sticky Session (쿠키 기반)
stickiness {
type = "lb_cookie"
cookie_duration = 86400 # 1일
enabled = true
}
# 느린 시작 (새 인스턴스 워밍업)
slow_start = 60
}
# HTTPS 리스너 (L7)
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.main.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = var.acm_certificate_arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}
# 경로 기반 라우팅 규칙
resource "aws_lb_listener_rule" "api" {
listener_arn = aws_lb_listener.https.arn
priority = 100
condition {
path_pattern { values = ["/api/*"] }
}
action {
type = "forward"
target_group_arn = aws_lb_target_group.api.arn
}
}
# HTTP → HTTPS 리다이렉트
resource "aws_lb_listener" "http_redirect" {
load_balancer_arn = aws_lb.main.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
# 출력
output "alb_dns_name" {
value = aws_lb.main.dns_name
}
"현재 트래픽 패턴을 보면 API 서버에 부하가 집중되고 있습니다. ALB에서 경로 기반 라우팅으로 /api/*는 별도 타겟 그룹으로 분리하고, Least Connections 알고리즘을 적용하면 롱 폴링 요청이 많은 API 특성에 맞게 부하를 분산할 수 있습니다."
"서버 3번이 Health Check 실패로 타겟 그룹에서 제외됐습니다. ALB가 자동으로 트래픽을 나머지 서버로 분산하고 있어서 사용자 영향은 없습니다. unhealthy_threshold가 3이라 9초 후에 빠졌고, 복구되면 healthy_threshold 2로 6초 후 다시 투입됩니다."
"세션 기반 인증을 사용하면 Sticky Session을 켜야 하는데, 그러면 부하 분산 효율이 떨어집니다. JWT 토큰 방식으로 전환하면 Stateless가 되어 어느 서버로 가도 상관없게 됩니다. 그래야 Auto Scaling으로 서버가 늘어나도 문제가 없습니다."
단순히 TCP 포트 체크만 하면 애플리케이션이 죽어도 서버가 살아있다고 판단합니다. 반드시 HTTP 엔드포인트(/health)로 DB 연결, 필수 서비스 상태까지 확인하는 Health Check를 구현하세요.
세션 고정을 켜면 특정 서버에 트래픽이 몰려 부하 분산 효과가 사라집니다. 장애 시 해당 서버의 세션도 유실됩니다. Redis/Memcached로 세션을 외부화하거나 JWT로 Stateless 아키텍처를 구현하세요.
로드 밸런서 자체가 단일 장애점(SPOF)이 되면 무의미합니다. AWS ALB/NLB는 반드시 Multi-AZ로 배포하고, 자체 구축 시 Active-Standby 또는 Active-Active 구성으로 이중화하세요.
Connection Draining(deregistration_delay)을 설정해 서버 제거 시 기존 요청이 완료될 때까지 대기. slow_start로 새 서버 투입 시 점진적으로 트래픽 증가. CloudWatch로 HealthyHostCount, RequestCount, TargetResponseTime 모니터링 필수.