🌍 네트워크

Load Balancer

로드 밸런서

트래픽을 여러 서버에 분산. 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으로 서버가 늘어나도 문제가 없습니다."

⚠️ 흔한 실수 & 주의사항

Health Check 설정 미흡

단순히 TCP 포트 체크만 하면 애플리케이션이 죽어도 서버가 살아있다고 판단합니다. 반드시 HTTP 엔드포인트(/health)로 DB 연결, 필수 서비스 상태까지 확인하는 Health Check를 구현하세요.

Sticky Session 남용

세션 고정을 켜면 특정 서버에 트래픽이 몰려 부하 분산 효과가 사라집니다. 장애 시 해당 서버의 세션도 유실됩니다. Redis/Memcached로 세션을 외부화하거나 JWT로 Stateless 아키텍처를 구현하세요.

싱글 AZ에 로드 밸런서 배치

로드 밸런서 자체가 단일 장애점(SPOF)이 되면 무의미합니다. AWS ALB/NLB는 반드시 Multi-AZ로 배포하고, 자체 구축 시 Active-Standby 또는 Active-Active 구성으로 이중화하세요.

올바른 로드 밸런서 운영 방법

Connection Draining(deregistration_delay)을 설정해 서버 제거 시 기존 요청이 완료될 때까지 대기. slow_start로 새 서버 투입 시 점진적으로 트래픽 증가. CloudWatch로 HealthyHostCount, RequestCount, TargetResponseTime 모니터링 필수.

🔗 관련 용어

📚 더 배우기