로드 밸런싱
Load Balancing
네트워크 트래픽을 여러 서버에 분산하여 고가용성(High Availability)과 성능을 확보하는 기술. OSI 모델의 L4(전송 계층)와 L7(응용 계층)에서 동작하며, Round Robin, Least Connection 등 다양한 분산 알고리즘을 지원합니다.
Load Balancing
네트워크 트래픽을 여러 서버에 분산하여 고가용성(High Availability)과 성능을 확보하는 기술. OSI 모델의 L4(전송 계층)와 L7(응용 계층)에서 동작하며, Round Robin, Least Connection 등 다양한 분산 알고리즘을 지원합니다.
로드 밸런싱(Load Balancing)은 들어오는 네트워크 트래픽을 여러 백엔드 서버에 분산하여 단일 서버의 과부하를 방지하고 서비스의 가용성과 응답 속도를 향상시키는 기술입니다. 식당에서 여러 카운터에 손님을 분산하는 것과 같은 원리로, 대규모 웹 서비스에서 필수적인 인프라 컴포넌트입니다.
로드 밸런서는 OSI 7계층 모델에서 동작하는 계층에 따라 L4(전송 계층)와 L7(응용 계층)로 구분됩니다. L4 로드 밸런서는 IP 주소와 TCP/UDP 포트 정보만으로 빠르게 분산하며, L7 로드 밸런서는 HTTP 헤더, URL, 쿠키 등 애플리케이션 레벨 정보를 기반으로 더 세밀한 라우팅이 가능합니다.
Health Check는 로드 밸런싱의 핵심 기능으로, 백엔드 서버의 상태를 주기적으로 확인하여 장애가 발생한 서버로는 트래픽을 보내지 않습니다. TCP 연결 체크, HTTP 응답 코드 확인, 커스텀 헬스체크 엔드포인트 등 다양한 방식을 지원하며, 이를 통해 무중단 서비스가 가능해집니다.
현대 클라우드 환경에서는 AWS ELB(ALB/NLB), GCP Cloud Load Balancing, Azure Load Balancer 등 관리형 서비스가 널리 사용됩니다. 온프레미스 환경에서는 HAProxy, Nginx, F5 BIG-IP 등이 대표적인 솔루션입니다. 마이크로서비스 아키텍처에서는 Kubernetes Ingress나 Service Mesh(Istio, Linkerd)가 내부 로드 밸런싱을 담당합니다.
| 구분 | L4 로드 밸런서 | L7 로드 밸런서 |
|---|---|---|
| 동작 계층 | 전송 계층 (TCP/UDP) | 응용 계층 (HTTP/HTTPS) |
| 분산 기준 | IP, Port 정보 | URL, Header, Cookie, Host |
| 속도 | 매우 빠름 (패킷 레벨) | 상대적으로 느림 (파싱 필요) |
| SSL 처리 | 패스스루 (백엔드에서 처리) | SSL Termination 가능 |
| 사용 사례 | DB, 게임 서버, TCP 기반 서비스 | 웹 서비스, API Gateway, MSA |
| AWS 서비스 | NLB (Network Load Balancer) | ALB (Application Load Balancer) |
요청을 순차적으로 서버에 분배. 가장 단순하고 널리 사용됨. 서버 성능이 동일할 때 효과적.
서버별 가중치를 부여하여 분배. 성능이 다른 서버 혼용 시 유용. (예: 3:1 비율)
현재 연결 수가 가장 적은 서버로 분배. 요청 처리 시간이 다양할 때 효과적.
클라이언트 IP 기반 해시로 동일 서버 연결. 세션 유지(Sticky Session)에 활용.
응답 시간이 가장 빠른 서버로 분배. 실시간 성능 모니터링 필요.
무작위로 서버 선택. 구현이 단순하고 부하가 균등하게 분산되는 경향.
# /etc/nginx/nginx.conf - Nginx 로드 밸런서 설정
http {
# 백엔드 서버 그룹 정의 (upstream)
upstream backend_servers {
# 분산 알고리즘 (기본: Round Robin)
# least_conn; # Least Connections
# ip_hash; # IP Hash (Sticky Session)
# random two least_conn; # 무작위 + Least Conn 혼합
# 서버 목록 (weight: 가중치, max_fails: 실패 허용 횟수)
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080 weight=2;
server 10.0.0.3:8080 weight=1 backup; # 백업 서버
# Health Check 설정
# max_fails=3: 3번 실패 시 비활성화
# fail_timeout=30s: 30초 후 재시도
server 10.0.0.4:8080 max_fails=3 fail_timeout=30s;
# 연결 유지 (Keep-Alive)
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_servers;
proxy_http_version 1.1;
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_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# L7 라우팅: URL 기반 분산
location /api/v2/ {
proxy_pass http://api_v2_servers;
}
}
}
# /etc/haproxy/haproxy.cfg - HAProxy 설정
global
log /dev/log local0
maxconn 4096
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3
# 프론트엔드: 클라이언트 요청 수신
frontend http_front
bind *:80
bind *:443 ssl crt /etc/ssl/certs/server.pem
# ACL 기반 라우팅
acl is_api path_beg /api
acl is_static path_end .css .js .png .jpg
use_backend api_servers if is_api
use_backend static_servers if is_static
default_backend web_servers
# 백엔드: 실제 서버 그룹
backend web_servers
balance roundrobin # 알고리즘: roundrobin, leastconn, source
option httpchk GET /health # HTTP Health Check
http-check expect status 200
server web1 10.0.0.1:8080 check weight 3
server web2 10.0.0.2:8080 check weight 2
server web3 10.0.0.3:8080 check backup # 백업 서버
backend api_servers
balance leastconn
option httpchk GET /api/health
# Sticky Session (쿠키 기반)
cookie SERVERID insert indirect nocache
server api1 10.0.0.10:8080 check cookie s1
server api2 10.0.0.11:8080 check cookie s2
# 모니터링 대시보드
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats auth admin:password
# 간단한 로드 밸런서 구현 예제 (Python)
import random
import time
from collections import defaultdict
from typing import List, Optional
class Server:
def __init__(self, host: str, port: int, weight: int = 1):
self.host = host
self.port = port
self.weight = weight
self.connections = 0
self.healthy = True
self.response_times = []
@property
def address(self) -> str:
return f"{self.host}:{self.port}"
class LoadBalancer:
def __init__(self, servers: List[Server], algorithm: str = "round_robin"):
self.servers = servers
self.algorithm = algorithm
self.current_index = 0
self.weighted_index = 0
def get_healthy_servers(self) -> List[Server]:
return [s for s in self.servers if s.healthy]
def round_robin(self) -> Optional[Server]:
"""순차적으로 서버 선택"""
healthy = self.get_healthy_servers()
if not healthy:
return None
server = healthy[self.current_index % len(healthy)]
self.current_index += 1
return server
def least_connections(self) -> Optional[Server]:
"""연결 수가 가장 적은 서버 선택"""
healthy = self.get_healthy_servers()
if not healthy:
return None
return min(healthy, key=lambda s: s.connections)
def ip_hash(self, client_ip: str) -> Optional[Server]:
"""IP 기반 해시로 서버 선택 (Sticky Session)"""
healthy = self.get_healthy_servers()
if not healthy:
return None
index = hash(client_ip) % len(healthy)
return healthy[index]
def weighted_round_robin(self) -> Optional[Server]:
"""가중치 기반 라운드 로빈"""
healthy = self.get_healthy_servers()
if not healthy:
return None
# 가중치 리스트 생성
weighted_list = []
for server in healthy:
weighted_list.extend([server] * server.weight)
server = weighted_list[self.weighted_index % len(weighted_list)]
self.weighted_index += 1
return server
def get_server(self, client_ip: str = None) -> Optional[Server]:
"""알고리즘에 따라 서버 반환"""
if self.algorithm == "round_robin":
return self.round_robin()
elif self.algorithm == "least_conn":
return self.least_connections()
elif self.algorithm == "ip_hash" and client_ip:
return self.ip_hash(client_ip)
elif self.algorithm == "weighted":
return self.weighted_round_robin()
return self.round_robin()
# 사용 예제
servers = [
Server("10.0.0.1", 8080, weight=3),
Server("10.0.0.2", 8080, weight=2),
Server("10.0.0.3", 8080, weight=1),
]
lb = LoadBalancer(servers, algorithm="weighted")
# 10번 요청 분산 시뮬레이션
for i in range(10):
server = lb.get_server()
print(f"Request {i+1} → {server.address}")
"API 서버 앞에 ALB를 두고, 헬스체크 엔드포인트를 /health로 설정했습니다. Unhealthy 임계값은 3회로, 약 30초 내에 장애 서버가 자동으로 제외됩니다. 현재 타겟 그룹에 4대가 있어서 한 대가 죽어도 트래픽은 75%만 감소합니다."
"L4 로드 밸런서는 TCP/UDP 레벨에서 IP와 포트만 보고 빠르게 분산하고, L7은 HTTP 헤더나 URL을 파싱해서 더 세밀한 라우팅이 가능합니다. 예를 들어 /api/v1은 레거시 서버로, /api/v2는 새 서버로 보내는 블루-그린 배포에 L7 ALB를 썼습니다."
"지금 Nginx upstream에서 502 에러가 나오는데, 백엔드 서버 3대 중 2대가 다운된 것 같습니다. max_fails 설정 때문에 아직 트래픽이 가고 있어요. 일단 수동으로 해당 서버를 upstream에서 제거하고, 나머지 1대에 모든 트래픽이 몰리니까 스케일 아웃 먼저 진행하겠습니다."
헬스체크를 설정하지 않으면 장애 서버로 계속 트래픽이 전달됩니다. 반드시 /health 엔드포인트를 만들고, DB 연결, 외부 API 상태까지 확인하는 종합 헬스체크를 구현하세요.
Round Robin에서 로그인 세션이 서버 메모리에 저장되면, 다음 요청이 다른 서버로 가면 로그아웃됩니다. Redis 세션 저장소를 사용하거나, IP Hash/쿠키 기반 Sticky Session을 설정하세요.
로드 밸런서 자체가 단일 장애점(Single Point of Failure)이 되면 안 됩니다. Active-Standby 또는 Active-Active 이중화를 구성하고, DNS Failover를 함께 설정하세요.
헬스체크 간격 10-30초, 타임아웃 5초, Unhealthy 임계값 2-3회로 설정. Connection Draining을 활성화하여 서버 제거 시 기존 연결이 정상 종료되도록 하세요. 모니터링 대시보드로 서버별 요청 수와 응답 시간을 실시간 확인하세요.