infra
Platform

모듈 맵

[Infra Ops] L4/L7 로드밸런서와 VIP 기반 이중화 구성

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 21 / 52

[Infra Ops] L4/L7 로드밸런서와 VIP 기반 이중화 구성

L4/L7 LB 차이, VIP 동작 원리, Health Check 전략, Failover 테스트까지 — 고가용성 서비스를 위한 로드밸런서 운영 실무

🚨INCIDENT ALERT
HIGH

서비스 오픈 일주일 전, 운영팀에서 연락이 왔습니다. "지금 WAS가 서버 한 대인데, 이게 죽으면 어떻게 됩니까?" 개발팀은 코드만 짰고, 인프라 담당자는 "로드밸런서 붙이면 됩니다"라고 했지만 — 어떤 로드밸런서를 어떻게 구성해야 하는지, Health Check는 어디로 잡아야 하는지, Active 장비가 죽으면 Standby로 어떻게 넘어가는지 아무도 정확히 모릅니다.

이 모듈에서는 L4/L7 차이를 구분하고, VIP+keepalived 기반 이중화 원리를 이해하며, Nginx upstream으로 Health Check와 분산 알고리즘을 직접 설정하는 과정을 다룹니다.

이번 챕터에서 배울 것
  • 1L4와 L7 로드밸런서의 동작 계층 차이와 사용 기준을 설명할 수 있다
  • 2VIP와 keepalived VRRP로 LB 이중화가 동작하는 원리를 이해할 수 있다
  • 3Nginx upstream에서 분산 알고리즘과 Health Check를 설정할 수 있다
  • 4Active/Standby Failover 동작 순서를 추적하고 검증할 수 있다
  • 5HAProxy, Nginx upstream, F5의 상황별 선택 기준을 설명할 수 있다

L4 vs L7 — 어느 계층에서 결정하나

💡개념

L4 LB: IP와 포트만 본다

로드밸런서를 처음 접하면 "그냥 트래픽 나눠주는 거 아닌가?"라고 생각하기 쉽습니다. 하지만 어느 계층에서 결정을 내리느냐에 따라 할 수 있는 것과 없는 것이 완전히 달라집니다. L4 LB는 OSI 4계층(Transport Layer)에서 동작하기 때문에 패킷의 IP 주소와 TCP/UDP 포트 번호만 볼 수 있습니다. HTTP 메서드가 GET인지 POST인지, URL이 /api/인지 /static/인지 알 수 없습니다.

L4 LB: IP와 포트만 본다

L4 LB 동작 방식:

클라이언트 → [VIP:443] → L4 LB → 백엔드 서버 A (192.168.10.11:443)
                                  백엔드 서버 B (192.168.10.12:443)
                                  백엔드 서버 C (192.168.10.13:443)

L4 LB는 TCP 연결 수립 단계(SYN 패킷)에서 분산 결정을 내립니다. 연결이 맺어진 후의 HTTP 요청 내용은 보지 않습니다. 덕분에 처리 오버헤드가 낮고 속도가 빠릅니다.

대표적인 사용 사례:

  • TCP 기반 서비스 (DB, Redis, SMTP)
  • 단순 HTTP 트래픽 분산 (URL 기반 라우팅 불필요)
  • 매우 높은 처리량이 필요한 환경

장단점:

항목내용
처리 속도빠름 (패킷 레벨 처리)
URL 기반 라우팅불가
SSL Termination불가 (패킷을 해석 못함)
구현 예AWS NLB, HAProxy (TCP mode), LVS
💡개념

L7 LB: HTTP 내용까지 본다

API 요청과 정적 파일 요청을 서로 다른 서버군으로 보내야 합니다. API는 WAS 클러스터로, /static/은 CDN Origin으로, /admin/은 어드민 서버로 분리하고 싶습니다. L4 LB는 URL을 볼 수 없어서 이런 라우팅이 불가능합니다. HTTP 내용을 읽어야만 가능한 이 역할을 L7 LB가 합니다.

L7 LB는 OSI 7계층(Application Layer)에서 동작합니다. HTTP 요청을 완전히 파싱하기 때문에 URL 경로, Host 헤더, 쿠키, HTTP 메서드 등을 기반으로 분산 결정을 내릴 수 있습니다. 실제 운영 환경에서 웹 서비스를 위해 가장 많이 쓰이는 방식입니다.

L7 LB 동작 방식 (콘텐츠 기반 라우팅 예시):

클라이언트 GET /api/users  → L7 LB → API 서버 A (192.168.10.11:8080)
클라이언트 GET /static/... → L7 LB → CDN Origin (192.168.10.20:80)
클라이언트 GET /admin/...  → L7 LB → Admin 서버 (192.168.10.30:8080)

장단점:

항목내용
URL/헤더 기반 라우팅가능
SSL Termination가능 (인증서를 LB에서 처리)
헬스체크 정밀도HTTP 응답 코드, body까지 확인 가능
처리 오버헤드L4보다 높음
구현 예Nginx upstream, HAProxy (HTTP mode), AWS ALB
L4 vs L7 — 상황별 선택 기준
HTTP 서비스, URL 경로별 라우팅 필요L7 LB (Nginx upstream, HAProxy HTTP mode, AWS ALB)
DB/Redis/SMTP 등 TCP 서비스 분산L4 LB (HAProxy TCP mode, AWS NLB)
SSL Termination을 LB에서 처리하고 싶음L7 LB
초고속 처리량, 패킷 레벨 성능 필요L4 LB (LVS/IPVS, AWS NLB)
단일 포트로 여러 서비스 라우팅L7 LB (Host 헤더 기반 분기)

VIP와 keepalived — LB 이중화 원리

💡개념

VIP와 VRRP — Failover가 동작하는 방식

로드밸런서 자체가 단일 장애점(SPOF)이 되면 안 됩니다. LB 이중화는 Active/Standby 구성으로 해결합니다. 이때 핵심 개념이 VIP(Virtual IP)입니다.

VIP는 Active LB 장비가 보유하는 가상 IP 주소입니다. 클라이언트와 DNS는 항상 이 VIP로 접속합니다. Active LB가 장애로 죽으면 Standby LB가 VIP를 인계받아 서비스를 이어받습니다. 이 과정에서 클라이언트는 IP 변경을 인지하지 못합니다.

VRRP(Virtual Router Redundancy Protocol) 기반 keepalived:

[Active LB]   192.168.1.10 (Real IP)  ← VIP 192.168.1.100 보유
    ↕ VRRP heartbeat (224.0.0.18, UDP)
[Standby LB]  192.168.1.11 (Real IP)  ← VIP 없음, 대기 중

Failover 발생 순서:

  1. Active LB가 응답 없음 (VRRP 패킷 중단)
  2. Standby가 MASTER_DOWN_INTERVAL 내에 감지
  3. Standby가 VIP를 자신의 NIC에 추가 (ip addr add 192.168.1.100/24 dev eth0)
  4. Gratuitous ARP 발송 → 스위치/라우터 ARP 캐시 갱신
  5. 트래픽이 Standby(이제 새 Active)로 유입

keepalived 설정 예시 (Active 장비):

로컬 터미널
# /etc/keepalived/keepalived.conf (Active)
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51       # Active/Standby 동일해야 함
    priority 100               # Standby는 90 등 낮게 설정
    advert_int 1               # 1초마다 VRRP 광고

    authentication {
        auth_type PASS
        auth_pass secure1234   # Active/Standby 동일
    }

    virtual_ipaddress {
        192.168.1.100/24       # VIP
    }

    # LB 프로세스 죽으면 우선순위 낮춰 Failover 유도
    track_script {
        chk_nginx
    }
}

vrrp_script chk_nginx {
    script "systemctl is-active nginx"
    interval 2
    weight -20                 # nginx 죽으면 priority를 20 낮춤
}
로컬 터미널
# /etc/keepalived/keepalived.conf (Standby)
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90                # Active(100)보다 낮게
    advert_int 1
    # ... (나머지 동일)
}

Failover 흐름 — Active/Standby VIP 전환

Health Check 전략

💡개념

TCP/HTTP/Script — 세 가지 Health Check 방식

Health Check를 제대로 설정하지 않으면, 서버가 실제로 동작 불능이 되어도 LB가 계속 트래픽을 보냅니다. 반대로 너무 민감하게 설정하면 일시적 지연에도 서버를 제외시켜 불필요한 Failover가 생깁니다.

세 가지 방식 비교:

방식확인 내용신뢰도적합 상황
TCP포트 연결 가능 여부낮음포트만 열려있으면 통과
HTTP지정 URL의 응답 코드중간앱 레벨 확인 가능
Script커스텀 스크립트 실행 결과높음DB 연결, 임계치 체크 등 복합 조건

Nginx upstream Health Check 설정:

Nginx
# /etc/nginx/conf.d/upstream.conf

upstream backend_pool {
    # 분산 알고리즘 (기본: Round Robin)
    # least_conn;           # 최소 연결 수 서버로
    # ip_hash;              # 클라이언트 IP 기반 고정 (Sticky Session)

    server 192.168.10.11:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.10.12:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.10.13:8080 weight=2 max_fails=3 fail_timeout=30s;
    # weight=2: 다른 서버의 2배 트래픽 수신

    keepalive 32;           # 백엔드 연결 풀 유지
}

server {
    listen 80;

    location / {
        proxy_pass http://backend_pool;
        proxy_connect_timeout 5s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;

        # Health Check 실패 서버 재시도
        proxy_next_upstream error timeout http_500 http_502 http_503;
        proxy_next_upstream_tries 2;
    }

    # Health Check 엔드포인트 (모니터링용)
    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
}

HAProxy Health Check 설정 (더 세밀한 제어):

HAPROXY
# /etc/haproxy/haproxy.cfg
backend web_servers
    balance roundrobin
    option httpchk GET /actuator/health   # Spring Boot health endpoint
    http-check expect status 200

    server web1 192.168.10.11:8080 check inter 5s rise 2 fall 3
    server web2 192.168.10.12:8080 check inter 5s rise 2 fall 3
    # inter 5s: 5초마다 체크
    # rise 2: 2번 성공하면 UP으로 복구
    # fall 3: 3번 실패하면 DOWN으로 제외

분산 알고리즘 선택

분산 알고리즘 — 상황별 선택
일반적인 stateless REST API 서버 분산Round Robin (기본값, 순서대로 균등 분배)
서버 사양이 다르거나 처리 능력 차이가 큼Weighted Round Robin (weight 값으로 비율 조정)
요청 처리 시간 편차가 크거나 긴 요청 혼재Least Connections (현재 연결 가장 적은 서버로)
서버 메모리에 세션 저장하는 레거시 앱IP Hash (클라이언트 IP 고정 → Sticky Session 효과)
외부 세션 저장소(Redis) 사용하는 현대적 앱Round Robin 또는 Least Connections (Sticky 불필요)

실습 — 상태 확인

1Nginx upstream 설정 및 상태 확인

nginx -T는 include된 모든 설정 파일을 합쳐서 출력합니다. upstream 블록을 grep해서 현재 백엔드 풀 구성을 확인합니다. max_fails, fail_timeout, weight 값을 검토합니다.

로컬 터미널
# Nginx 전체 설정에서 upstream 관련 설정 확인
nginx -T | grep -A 20 upstream

# LB를 통한 헬스 엔드포인트 응답 확인
curl -v http://192.168.1.100/health

# upstream 각 서버 직접 확인 (LB 우회)
curl -v http://192.168.10.11:8080/actuator/health
curl -v http://192.168.10.12:8080/actuator/health

# Nginx 상태 모듈 (stub_status 활성화된 경우)
curl http://localhost/nginx-status
nginx -T | grep -A 20 upstream
🔍실행 후 확인할 것
  • nginx -T 출력에 upstream 블록이 나타나고 server 목록이 정확한가
  • curl http://VIP/health 가 200 OK를 반환하는가
  • 각 백엔드 서버 직접 접속 시 모두 응답하는가
  • Nginx error.log에 upstream 관련 에러가 없는가
2keepalived VIP 상태 확인

Active LB 장비에서 실행하면 Real IP와 VIP 두 개가 모두 보여야 합니다. Standby 장비에서는 Real IP만 보입니다.

로컬 터미널
# 현재 이 장비가 VIP를 보유하고 있는지 확인
ip addr show | grep -E 'inet.*eth'
# Active 장비 예시:
# inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0
# inet 192.168.1.100/24 scope global secondary eth0  ← VIP

# keepalived 서비스 상태 및 현재 역할 확인
systemctl status keepalived

# keepalived 로그에서 MASTER/BACKUP 전환 이력 확인
journalctl -u keepalived -n 50 --no-pager | grep -E "MASTER|BACKUP|Entering"
ip addr show | grep -E 'inet.*eth'
🔍실행 후 확인할 것
  • Active LB에서 ip addr show 출력에 VIP가 secondary로 보이는가
  • Standby LB에서 VIP가 보이지 않는가
  • systemctl status keepalived가 active (running) 상태인가
  • journalctl에서 'Entering MASTER STATE' 이력이 보이는가

트러블슈팅

원인: Health Check 경로를 /로 설정했을 때 자주 발생합니다. /는 단순히 웹 서버가 살아있는지만 확인하고, 애플리케이션이 실제로 정상인지(DB 연결, 의존 서비스 연결 등)는 확인하지 않습니다. Nginx나 Tomcat은 떠 있지만 DB 연결 풀이 고갈된 상태에서 LB는 계속 정상으로 판단합니다.

로컬 터미널
# 현재 Health Check 경로 확인
nginx -T | grep -E "(health|check|upstream)"
grep -r "httpchk\|health" /etc/haproxy/

# 앱이 제공하는 실제 헬스 엔드포인트로 변경
# Spring Boot: /actuator/health (DB, Redis 연결까지 체크)
# 직접 만든 앱: /health/ready (의존성 체크 로직 포함)

# 헬스 엔드포인트 응답 내용 확인
curl -s http://192.168.10.11:8080/actuator/health | python3 -m json.tool
# 정상: {"status":"UP","components":{"db":{"status":"UP"},...}}
# 비정상: {"status":"DOWN","components":{"db":{"status":"DOWN"},...}}

해결: Health Check 경로를 앱의 /actuator/health 또는 커스텀 /health/ready 엔드포인트로 변경하고, 해당 엔드포인트가 DB 연결, 캐시 서버 연결까지 체크하도록 앱 팀과 협의합니다.

원인: Standby LB에 maxconn 설정이 Active보다 낮게 잡혀 있거나, Standby가 오래 대기하다가 갑자기 Active가 되면서 TCP 연결 큐가 쌓립니다. HAProxy의 경우 Standby의 기본 maxconn이 다르게 설정된 경우입니다. Nginx의 경우 worker_processes나 worker_connections가 낮을 수 있습니다.

로컬 터미널
# HAProxy Standby 서버의 maxconn 확인
grep -E "maxconn|timeout" /etc/haproxy/haproxy.cfg

# HAProxy 런타임 통계 확인 (socat 필요)
echo "show info" | socat stdio /var/run/haproxy/admin.sock | grep -E "MaxConn|CurrConns"

# Nginx worker 설정 확인
nginx -T | grep -E "worker_processes|worker_connections"

# 현재 연결 수 확인
ss -s
netstat -an | grep ESTABLISHED | wc -l

해결: Active와 Standby의 maxconn, worker_connections 설정을 동일하게 맞춥니다. Failover 발생 시 systemctl reload haproxy 또는 nginx -s reload로 연결 큐를 초기화합니다. 장기적으로는 양쪽 LB 설정을 Ansible이나 Chef로 동기화하여 설정 차이가 생기지 않게 관리합니다.

💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

인프라 담당자가 LB를 처음 구성할 때 가장 많이 받는 질문 두 가지가 있습니다. "Health Check 경로 어떻게 설정하나요?"와 "Active 장비가 죽으면 어떻게 됩니까?"입니다.

1. 신규 서비스 LB 구성 체크리스트:

서버 터미널
# 1. upstream 서버 목록과 분산 알고리즘 결정
# 2. Health Check 경로 앱 팀과 협의 (/actuator/health 권장)
# 3. max_fails, fail_timeout 설정 (3회 실패, 30초 제외)
# 4. VIP 설정 및 keepalived 구성
# 5. Failover 테스트: Active keepalived 중지 후 VIP 이동 확인
systemctl stop keepalived   # Active에서 실행
ip addr show                 # Standby에서 VIP 인계 확인

2. 장애 발생 시 LB 점검 순서:

서버 터미널
# LB 상태 확인
systemctl status nginx haproxy keepalived

# VIP 보유 장비 확인
ip addr show | grep 'VIP주소'

# 백엔드 서버 상태 확인 (HAProxy 통계 페이지)
curl http://localhost:8404/stats   # HAProxy stats 페이지 (설정 필요)

# Nginx upstream 에러 확인
grep "upstream" /var/log/nginx/error.log | tail -20

3. HAProxy vs Nginx upstream vs F5 선택 기준:

제품적합한 상황
Nginx upstream이미 Nginx 사용 중인 환경, 간단한 L7 분산
HAProxy세밀한 Health Check, TCP/HTTP 혼용, 통계 대시보드 필요
F5 BIG-IP금융/공공기관, 하드웨어 어플라이언스 필요, 벤더 지원 계약 필요
AWS ALB/NLB클라우드 네이티브 환경, 관리 부담 최소화

LB 이중화와 Health Check를 올바르게 구성하는 것만으로도 서비스 가용성을 99.9% 이상으로 끌어올릴 수 있습니다. 다음 모듈에서는 폐쇄망 환경에서 내부 서버가 외부 기관과 통신하는 Proxy/NAT 구조를 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

L4 로드밸런서와 L7 로드밸런서의 가장 핵심적인 차이는?

Q2

VIP(Virtual IP)가 필요한 이유로 가장 적절한 것은?

Q3

Active Health Check와 Passive Health Check의 차이는?

Q4

Sticky Session(세션 고정)이 반드시 필요한 서비스와 필요 없는 서비스를 올바르게 구분한 것은?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 60
[Infra Ops] Proxy/NAT 구조와 폐쇄망 외부 통신 설정
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점