📚 네트워크 · 패킷 분석 › 04. 네트워크 장비 실습 — 158편
이전 글: 157. IPS란 무엇인가 · 다음 글: 159. Switch와 Router 비교
로드 밸런서(Load Balancer) 는 여러 대의 서버 앞에 서서, 들어오는 요청을 서버들에 나누어 보내는 장비입니다. 사용자는 로드 밸런서의 대표 주소 하나만 알고 접속하며, 실제로 어느 서버가 응답했는지는 모릅니다.
| 용어 | 의미 |
|---|---|
| VIP (Virtual IP) | 사용자가 접속하는 로드 밸런서의 대표 IP |
| 서버 풀(Pool) | 요청을 나눠 받는 실제 서버 묶음 |
| 헬스 체크(Health Check) | 서버가 정상 응답하는지 주기적으로 확인 |
| 세션 유지(Persistence) | 같은 사용자를 같은 서버로 계속 보내는 기능 |
| SSL 오프로딩 | TLS 암복호화를 로드 밸런서가 대신 처리 |
로드 밸런서는 가용성(서버 한 대 장애 시에도 서비스 유지)과 확장성(서버 추가로 처리량 증가)을 위한 장비이지만, 관제 관점에서는 로그에 보이는 IP를 바꾸는 장비라는 점이 가장 중요합니다.
일반적인 구성(로드 밸런서가 출발지 IP를 자기 IP로 바꾸는 방식)의 흐름입니다.
사용자 203.0.113.50
↓ 목적지 = VIP 198.51.100.80:443
[로드 밸런서]
├─ 헬스 체크 결과 정상인 서버만 후보
├─ 분배 알고리즘으로 서버 선택
├─ (L7) TLS 복호화, X-Forwarded-For: 203.0.113.50 헤더 추가
└─ 새 연결: 출발지 10.10.40.5 → 목적지 10.10.40.21:80
↓
[웹 서버 10.10.40.21] 웹 로그의 클라이언트 IP = 10.10.40.5 (로드 밸런서)
| 방식 | 판단 기준 | 특징 |
|---|---|---|
| L4 로드 밸런싱 | IP·포트(TCP/UDP) | 빠름, 요청 내용은 보지 않음 |
| L7 로드 밸런싱 | HTTP 호스트·URL·쿠키 등 | 경로별 분배, 헤더 추가·TLS 종료 가능 |
대표 분배 알고리즘은 라운드 로빈(차례대로), 최소 연결(연결 수가 가장 적은 서버), 출발지 IP 해시(같은 IP는 같은 서버)입니다. 구성 방식에 따라 서버가 로드 밸런서를 거치지 않고 사용자에게 직접 응답하는 DSR(Direct Server Return) 구조도 있습니다.
로드 밸런서가 놓이는 위치입니다.
| 위치 | 대상 | 참고 |
|---|---|---|
| DMZ의 공개 서비스 앞 | 웹·API 서버 | 방화벽 뒤, 서버 팜 앞이 일반적 |
| 내부 서비스 앞 | 내부 업무 시스템, DB 읽기 서버 | 내부 트래픽 분산 |
| 클라우드 | 관리형 로드 밸런서 서비스 | 로그 저장 기능은 별도 활성화가 필요한 경우가 많음 |
관제에서 로드 밸런서 때문에 생기는 대표 문제와 확인 위치입니다.
| 문제 | 원인 | 확인 위치 |
|---|---|---|
| 웹 로그의 클라이언트 IP가 모두 같음 | 로드 밸런서가 출발지 IP를 변환 | X-Forwarded-For 기록, 로드 밸런서 로그 |
| 같은 사용자 요청이 여러 서버 로그에 흩어짐 | 요청마다 다른 서버로 분배 | 전체 서버 로그 통합 검색 |
| 서버에서는 평문, 외부에서는 TLS | SSL 오프로딩 | 로드 밸런서 ↔ 서버 구간 캡처 |
| 서버가 풀에서 빠졌다가 복귀 | 헬스 체크 실패 | 로드 밸런서 이벤트 로그 |
실습 예시 — Nginx를 L7 로드 밸런서로 두고 웹 서버 VM 2대에 분배하는 구성입니다. 설치는 Rocky sudo dnf install nginx, Ubuntu sudo apt install nginx이며, 설정 파일은 두 배포판 모두 /etc/nginx/conf.d/에 둘 수 있습니다. 값은 예시(값은 환경마다 다름)입니다.
# /etc/nginx/conf.d/lab-lb.conf
log_format lb '$remote_addr -> $upstream_addr "$request" $status';
upstream lab_web {
server 192.168.10.21:80 max_fails=3 fail_timeout=10s;
server 192.168.10.22:80 max_fails=3 fail_timeout=10s;
}
server {
listen 8080;
access_log /var/log/nginx/lab-lb.log lb;
location / {
proxy_pass http://lab_web;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
sudo nginx -t && sudo systemctl restart nginx
# Rocky: SELinux가 프록시 연결을 막을 수 있음, 방화벽에 8080 허용 필요
sudo setsebool -P httpd_can_network_connect 1
sudo firewall-cmd --add-port=8080/tcp
# 클라이언트에서 여러 번 요청 후 분배 기록 확인
for i in 1 2 3 4; do curl -s -o /dev/null http://192.168.10.5:8080/; done
tail -4 /var/log/nginx/lab-lb.log
192.168.10.11 -> 192.168.10.21:80 "GET / HTTP/1.1" 200
192.168.10.11 -> 192.168.10.22:80 "GET / HTTP/1.1" 200
오픈소스 Nginx의 max_fails/fail_timeout은 실제 요청 실패를 보고 판단하는 수동(Passive) 방식입니다. 주기적으로 확인하는 능동 헬스 체크는 HAProxy나 상용 장비에서 제공합니다.
📷 [실습 화면 삽입 위치] 로드 밸런서 로그의
$upstream_addr가 두 서버로 번갈아 기록되고, 웹 서버 로그에는 클라이언트 IP 대신 로드 밸런서 IP가 남은 두 화면
IDS Alert: 203.0.113.50 → VIP 198.51.100.80 (웹 공격 의심)
↓ 로드 밸런서 로그: 해당 시각 요청이 어느 서버로 갔는가
↓ 해당 서버 웹 로그: X-Forwarded-For = 203.0.113.50 인 요청
↓ 응답 코드·처리 결과로 공격 성공 여부 판단
관제자가 확인할 질문
오탐 주의: 로드 밸런서의 헬스 체크 요청은 짧은 주기로 같은 경로를 반복 호출하므로, 서버 로그에서 자동화된 접근처럼 보일 수 있습니다. 헬스 체크 출발지 IP와 경로를 미리 파악해 둡니다.
$upstream_addr 로그를 쓰면 분배 결과를 확인할 수 있습니다.