로드 밸런서
Load Balancer
트래픽을 여러 서버에 분산하는 장치. L4(TCP), L7(HTTP) 레벨로 구분. 고가용성의 핵심.
Load Balancer
트래픽을 여러 서버에 분산하는 장치. L4(TCP), L7(HTTP) 레벨로 구분. 고가용성의 핵심.
로드 밸런서(Load Balancer)는 들어오는 네트워크 트래픽을 여러 서버에 효율적으로 분산시키는 장치 또는 소프트웨어입니다. 단일 서버가 모든 요청을 처리할 수 없을 때, 로드 밸런서는 트래픽을 여러 서버로 나누어 시스템 전체의 처리량을 높이고 응답 시간을 줄입니다. Netflix, Amazon, Google 같은 대규모 서비스들은 초당 수백만 건의 요청을 로드 밸런서를 통해 처리합니다.
로드 밸런서는 크게 L4(Layer 4)와 L7(Layer 7)로 구분됩니다. L4 로드 밸런서는 TCP/UDP 계층에서 동작하며 IP 주소와 포트 번호를 기반으로 트래픽을 분산합니다. 패킷 내용을 들여다보지 않아 처리 속도가 빠르고 리소스 효율적입니다. L7 로드 밸런서는 HTTP/HTTPS 계층에서 동작하며 URL, 쿠키, HTTP 헤더 등 애플리케이션 레벨 정보를 기반으로 더 정교한 라우팅이 가능합니다.
Health Check는 로드 밸런서의 핵심 기능입니다. 주기적으로 백엔드 서버들의 상태를 확인하여 장애가 발생한 서버로는 트래픽을 보내지 않습니다. TCP 포트 체크, HTTP 엔드포인트 호출 등 다양한 방식으로 서버 상태를 모니터링합니다. 이를 통해 사용자는 서버 장애를 인지하지 못한 채 정상적인 서비스를 이용할 수 있습니다.
클라우드 환경에서는 AWS의 ELB(Elastic Load Balancer) 제품군이 널리 사용됩니다. ALB(Application Load Balancer)는 L7 기반으로 HTTP/HTTPS 트래픽에 최적화되어 있고, NLB(Network Load Balancer)는 L4 기반으로 초저지연과 초고성능이 필요한 경우에 적합합니다. 온프레미스 환경에서는 Nginx, HAProxy 같은 소프트웨어 로드 밸런서와 F5 BIG-IP 같은 하드웨어 장비가 사용됩니다.
| 구분 | L4 로드 밸런서 | L7 로드 밸런서 |
|---|---|---|
| 동작 계층 | Transport Layer (TCP/UDP) | Application Layer (HTTP/HTTPS) |
| 라우팅 기준 | IP 주소, 포트 번호 | URL, 쿠키, HTTP 헤더, 호스트명 |
| 성능 | 초저지연, 높은 처리량 | 패킷 분석으로 상대적 지연 |
| SSL 처리 | 패스스루 또는 별도 처리 | SSL 종료(Termination) 지원 |
| AWS 제품 | NLB (Network Load Balancer) | ALB (Application Load Balancer) |
| 적합한 상황 | 게임 서버, 실시간 스트리밍, IoT | 웹 서비스, API Gateway, 마이크로서비스 |
서버에 순차적으로 요청을 분배합니다. 가장 단순하고 널리 사용되는 방식으로, 모든 서버의 성능이 동일할 때 효과적입니다.
서버별 가중치를 설정하여 성능 좋은 서버에 더 많은 트래픽을 분배합니다. 서버 스펙이 다를 때 유용합니다.
현재 연결 수가 가장 적은 서버에 새 요청을 전달합니다. 요청 처리 시간이 다양할 때 부하를 균등하게 분산합니다.
클라이언트 IP 주소를 해시하여 항상 같은 서버로 라우팅합니다. 세션 지속성이 필요할 때 사용합니다.
응답 시간이 가장 빠른 서버에 요청을 전달합니다. 실시간 성능 모니터링이 필요하지만 최적의 사용자 경험을 제공합니다.
무작위로 서버를 선택합니다. 구현이 간단하고 대규모 시스템에서 통계적으로 균등한 분산을 제공합니다.
# /etc/nginx/nginx.conf - Nginx 로드 밸런서 설정
http {
# 백엔드 서버 그룹 정의
upstream backend_servers {
# 로드 밸런싱 알고리즘 (기본: round robin)
# least_conn; # 최소 연결
# ip_hash; # IP 해시 (세션 스티키니스)
# random two least_conn; # 랜덤 + 최소 연결
# 서버 목록 (가중치 설정 가능)
server 10.0.1.10:8080 weight=5; # 가중치 5
server 10.0.1.11:8080 weight=3; # 가중치 3
server 10.0.1.12:8080 weight=2; # 가중치 2
# 백업 서버 (다른 서버 장애시 사용)
server 10.0.1.20:8080 backup;
# Health Check 설정
# max_fails: 실패 허용 횟수
# fail_timeout: 서버 제외 시간
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
# 연결 유지 (Keep-Alive)
keepalive 32;
}
# API 서버 그룹 (URL 기반 라우팅용)
upstream api_servers {
least_conn;
server 10.0.2.10:3000;
server 10.0.2.11:3000;
}
server {
listen 80;
listen 443 ssl;
server_name example.com;
# SSL 설정 (L7 SSL Termination)
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# 기본 요청 → 웹 서버
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_set_header X-Forwarded-Proto $scheme;
# 타임아웃 설정
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
# /api 요청 → API 서버 (L7 라우팅)
location /api {
proxy_pass http://api_servers;
proxy_set_header Host $host;
}
# Health Check 엔드포인트
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
# /etc/haproxy/haproxy.cfg - HAProxy 로드 밸런서 설정
global
log /dev/log local0
maxconn 50000
daemon
defaults
log global
mode http
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/example.pem
# ACL 정의 (L7 라우팅 규칙)
acl is_api path_beg /api
acl is_static path_beg /static
acl is_websocket hdr(Upgrade) -i websocket
# ACL 기반 백엔드 라우팅
use_backend api_backend if is_api
use_backend static_backend if is_static
use_backend websocket_backend if is_websocket
default_backend web_backend
# 웹 백엔드 (Round Robin)
backend web_backend
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
# Health Check: 2초 간격, 3번 실패시 제외
server web1 10.0.1.10:8080 check inter 2s fall 3 rise 2 weight 5
server web2 10.0.1.11:8080 check inter 2s fall 3 rise 2 weight 3
server web3 10.0.1.12:8080 check inter 2s fall 3 rise 2 weight 2
# API 백엔드 (Least Connections)
backend api_backend
balance leastconn
option httpchk GET /api/health
# 쿠키 기반 세션 스티키니스
cookie SERVERID insert indirect nocache
server api1 10.0.2.10:3000 check cookie api1
server api2 10.0.2.11:3000 check cookie api2
# 정적 파일 백엔드
backend static_backend
balance roundrobin
server static1 10.0.3.10:80 check
server static2 10.0.3.11:80 check
# WebSocket 백엔드
backend websocket_backend
balance source
timeout tunnel 3600s
server ws1 10.0.4.10:8080 check
server ws2 10.0.4.11:8080 check
# 모니터링 페이지
listen stats
bind *:8404
stats enable
stats uri /stats
stats auth admin:password123
// AWS CDK - ALB + NLB 로드 밸런서 설정
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
export class LoadBalancerStack extends cdk.Stack {
constructor(scope: cdk.App, id: string) {
super(scope, id);
const vpc = new ec2.Vpc(this, 'VPC', { maxAzs: 3 });
// ═══════════════════════════════════════
// ALB (Application Load Balancer) - L7
// ═══════════════════════════════════════
const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
vpc,
internetFacing: true,
loadBalancerName: 'my-alb',
});
// HTTPS 리스너 (SSL Termination)
const httpsListener = alb.addListener('HTTPS', {
port: 443,
certificates: [certificate],
sslPolicy: elbv2.SslPolicy.TLS12,
});
// 타겟 그룹 (백엔드 서버)
const webTargetGroup = new elbv2.ApplicationTargetGroup(this, 'WebTG', {
vpc,
port: 8080,
protocol: elbv2.ApplicationProtocol.HTTP,
targetType: elbv2.TargetType.IP,
// Health Check 설정
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
healthyHttpCodes: '200-299',
},
// 로드 밸런싱 알고리즘
loadBalancingAlgorithmType:
elbv2.TargetGroupLoadBalancingAlgorithmType.LEAST_OUTSTANDING_REQUESTS,
// 스티키 세션 (선택적)
stickinessCookieDuration: cdk.Duration.hours(1),
});
// URL 기반 라우팅 (L7 기능)
httpsListener.addTargetGroups('Default', {
targetGroups: [webTargetGroup],
});
// /api/* 요청은 API 서버로 라우팅
httpsListener.addTargetGroups('ApiRouting', {
priority: 10,
conditions: [elbv2.ListenerCondition.pathPatterns(['/api/*'])],
targetGroups: [apiTargetGroup],
});
// ═══════════════════════════════════════
// NLB (Network Load Balancer) - L4
// ═══════════════════════════════════════
const nlb = new elbv2.NetworkLoadBalancer(this, 'NLB', {
vpc,
internetFacing: true,
crossZoneEnabled: true, // 교차 영역 로드 밸런싱
});
// TCP 리스너 (L4)
const tcpListener = nlb.addListener('TCP', {
port: 443,
});
// 타겟 그룹 (게임 서버 등)
const gameTargetGroup = new elbv2.NetworkTargetGroup(this, 'GameTG', {
vpc,
port: 7777,
protocol: elbv2.Protocol.TCP,
healthCheck: {
protocol: elbv2.Protocol.TCP,
interval: cdk.Duration.seconds(10),
},
// 클라이언트 IP 보존
preserveClientIp: true,
});
tcpListener.addTargetGroups('GameServers', gameTargetGroup);
// 출력
new cdk.CfnOutput(this, 'ALBDnsName', { value: alb.loadBalancerDnsName });
new cdk.CfnOutput(this, 'NLBDnsName', { value: nlb.loadBalancerDnsName });
}
}
"마이크로서비스 API는 ALB를 사용하고, 실시간 게임 서버는 NLB를 사용하는 게 좋겠습니다. ALB는 URL 기반 라우팅이 가능해서 /users는 User 서비스로, /orders는 Order 서비스로 보낼 수 있고, NLB는 초저지연이라 게임의 실시간성을 보장할 수 있습니다."
"L4 로드 밸런서는 TCP 계층에서 IP와 포트만 보고 분산해서 빠르고, L7은 HTTP 헤더나 URL까지 분석해서 더 정교한 라우팅이 가능합니다. 저는 웹 서비스에는 ALB를 사용하고, Health Check 주기는 30초, unhealthy threshold는 3으로 설정해서 장애 서버를 90초 내에 제외시켰습니다."
"로드 밸런서 Health Check에서 서버 2대가 unhealthy로 빠졌습니다. 현재 정상 서버 3대로 트래픽이 몰리고 있어요. 긴급하게 Auto Scaling을 트리거해서 서버를 2대 더 추가하고, 장애 서버는 로그 확인 후 재시작하겠습니다."
Health Check가 없으면 장애 서버로도 트래픽이 전달되어 사용자에게 에러가 발생합니다. 반드시 /health 엔드포인트를 구현하고 로드 밸런서에서 주기적으로 체크하세요.
IP Hash나 쿠키 기반 스티키니스를 사용하면 특정 서버에 부하가 집중될 수 있습니다. 가급적 Stateless 설계를 하고, 세션은 Redis 같은 외부 저장소에 저장하세요.
로드 밸런서와 백엔드 서버를 하나의 가용 영역(AZ)에만 배포하면 해당 AZ 장애시 전체 서비스가 다운됩니다. 최소 2개 이상의 AZ에 분산 배포하세요.
Cross-Zone Load Balancing 활성화, Connection Draining(Deregistration Delay) 설정으로 안전한 서버 제거, 접근 로그 활성화로 트래픽 분석. Health Check 주기는 10~30초가 적정합니다.