HAProxy(High Availability Proxy)는 고가용성(HA), 로드밸런싱, 그리고 TCP(L4) 및 HTTP(L7) 기반의 프록시 기능을 제공하는 강력한 오픈소스 소프트웨어입니다.
초당 수백만 건의 커넥션을 처리할 수 있는 압도적인 성능을 자랑하며, 실습 인프라에서 HAProxy는 시스템 전체의 트래픽을 통제하는 '단일 진입점(Single Point of Entry)' 역할을 수행합니다.
Event-Driven 및 Single-Process 모델:
Apache와 같은 멀티 스레드/프로세스 방식이 아닌, 이벤트 기반의 비동기 아키텍처를 사용하여 메모리 사용량이 극도로 적고 컨텍스트 스위칭 오버헤드가 없습니다.
L4 / L7 로드밸런싱 동시 지원:
IP와 Port 기반의 빠른 L4 라우팅은 물론, HTTP 헤더, 쿠키, URI 패턴 등을 분석하여 세밀하게 분기하는 L7 라우팅까지 모두 완벽하게 지원합니다.
무중단 설정 반영 (Zero-Downtime Reload):
설정 파일(haproxy.cfg)을 수정하고 리로드할 때, 기존에 맺어져 있던 클라이언트의 연결(Session)을 끊지 않고 새 설정으로 전환할 수 있습니다.
강력한 헬스 체크(Health Check):
백엔드 서버의 포트 응답뿐만 아니라, 특정 HTTP 상태 코드까지 확인하여 세밀하게 장애 서버를 판별하고 트래픽을 차단(Failover)합니다.
이러한 특징 덕분에 클라이언트는 백엔드 서버의 개별 IP를 알 필요 없이, 로드밸런서의 IP로만 요청을 보내면 HAProxy가 설정된 알고리즘(Round Robin 등)에 따라 최적의 서버로 트래픽을 분산하고 무중단 서비스를 달성합니다.
이번글은 고양(Goyang) IDC 물리 서버 환경의 사설 네트워크 내에서 가동되는 HAProxy를 활용하여 트래픽 제어 시스템을 구축하고 검증한 과정을 담고 있습니다.
실습 인프라는 고양 IDC에 구축된 물리 서버 클러스터를 기반으로 합니다. 안전하고 폐쇄적인 관리를 위해 다음과 같은 네트워크 구조를 채택했습니다.
192.168.1.0/24)에 안전하게 직접 합류합니다.haproxy.cfg) 구조HAProxy의 모든 트래픽 제어는 haproxy.cfg 설정 파일을 통해 이루어집니다. 설정 파일은 여러 섹션으로 나뉘어 구성됩니다.
| 섹션명 | 역할 | 주요 파라미터 (옵션) |
|---|---|---|
global | 전역 설정 (HAProxy 자체의 실행 환경) |
|
defaults | 이후 섹션들에 공통으로 적용될 기본값 |
|
frontend | 외부 클라이언트 트래픽의 수신 진입점 |
|
backend | 실제 트래픽을 처리할 뒷단의 서버 그룹 |
|
listen | frontend와 backend를 한 번에 설정 (단축) |
|
backend 섹션에서 트래픽을 어떤 규칙으로 분산할지 결정하는 핵심 옵션입니다.
roundrobin: 가장 기본적인 방식으로, 등록된 서버들에 순서대로 균등하게 트래픽을 전송합니다.static-rr: 서버마다 weight(가중치)를 부여하여, 그 비율에 따라 트래픽을 전송합니다. (예: 9:1 비율 배포)balance url_param: HTTP GET 요청의 파라미터를 분석하여 조건에 맞는 서버로 트래픽을 보냅니다. (조건 미달 시 라운드로빈)balance hdr(<name>): HTTP 헤더 값을 분석하여 특정 헤더(예: 베타 유저 헤더)를 가진 요청만 특정 서버로 보냅니다. A/B 테스트에 유리합니다. (조건 미달 시 라운드로빈)
| 컴포넌트 | 사설 IP 주소 | 역할 | 소프트웨어 (Docker) |
|---|---|---|---|
| haproxy-lb | 192.168.1.172 | 리버스 프록시 및 로드밸런서 | HAProxy |
| ha-web-01 | 192.168.1.185 | 웹서버 v1 (Blue) | Nginx |
| ha-web-02 | 192.168.1.228 | 웹서버 v2 (Green) | Nginx |
| ha-web-01 (v1 Stable) | ha-web-02 (v2 Canary) |
|---|---|
![]() | ![]() |
위 이미지는 각 노드가 제공하는 실제 웹 인터페이스입니다. 로드밸런싱 결과에 따라 클라이언트는 위 두 화면 중 하나가 나타나게 됩니다.
graph TD
subgraph "Administrator Environment"
Admin[Administrator] -->|WireGuard VPN Tunnel| PrivateLAN[Goyang Private LAN: 192.168.1.0/24]
end
subgraph "Target Infrastructure (Private)"
PrivateLAN -->|Direct Control/SSH| LB[haproxy-lb: 172]
PrivateLAN -->|Direct Control/SSH| Web1[ha-web-01: 185]
PrivateLAN -->|Direct Control/SSH| Web2[ha-web-02: 228]
LB -->|Traffic Distribution| Web1
LB -->|Traffic Distribution| Web2
end
목적: 유입 트래픽을 두 대의 백엔드 서버에 균등하게 분산한다.
설정 (haproxy.cfg):
backend web_servers
balance roundrobin
server web01 192.168.1.185:80 check
server web02 192.168.1.228:80 check
검증 및 결과:
# 사설망 내부에서 LB 노드 호출 테스트
for i in {1..10}; do curl -s http://192.168.1.172 | grep -o 'v[12]'; done
결과 : v1 v2 v1 v2 v1 v2 v1 v2 v1 v2
목적: 특정 서버 장애 감지 시, 가용 가능한 노드로 트래픽을 자동 전환한다.
설정:
server web01 192.168.1.185:80 check inter 3s rise 2 fall 3
server web02 192.168.1.228:80 check inter 3s rise 2 fall 3
검증 절차:
1. ha-web-02 컨테이너 중지: docker stop nginx-web
2. 사설망 내 호출 결과 v1으로만 트래픽이 집중됨을 확인.
검증 및 결과
| ha-web-01 (v1 Stable) | ha-web-02 (v2 Canary) |
|---|---|
![]() | ![]() |
LB IP 접속 시: 기존에 v1과 v2로 분산되던 요청이 이제는 정상 상태인 ha-web-01 (v1)으로만 100% 집중됩니다. 사용자는 서버 한 대가 죽었음에도 불구하고 서비스 중단을 경험하지 않습니다.
개별 Node IP 접속 시: 장애가 발생한 192.168.1.228 (v2)로 직접 접속을 시도할 경우, 웹 서비스(Nginx) 프로세스가 중단되었으므로 '연결 거부(Connection Refused)' 혹은 '페이지를 찾을 수 없음' 오류가 발생합니다.
목적: 특정 조건(HTTP 헤더)을 만족하는 요청만 특정 백엔드 노드로 전달한다.
설정:
frontend http_front
bind *:80
acl is_beta hdr(X-Beta-User) -i true # 헤더 조건 정의
use_backend beta_server if is_beta # 조건 충족 시 v2로
default_backend web_servers # 기본은 roundrobin
backend beta_server
server web02 192.168.1.228:80 check # 베타 유저 전용
backend web_servers
balance roundrobin
server web01 192.168.1.185:80 check weight 9
server web02 192.168.1.228:80 check weight 1
흐름
요청 들어옴
↓
X-Beta-User: true 헤더 있어?
├── YES → beta_server → web02 (v2 초록색 고정)
└── NO → web_servers → web01 90% / web02 10%
검증 및 결과
# 일반 유저
for i in {1..10}; do curl -s http://localhost | grep -o 'v[12]'; done
결과 : v1 v1 v1 v1 v1 v1 v1 v1 v2 v1
# 베타 유저 (헤더 포함)
for i in {1..10}; do curl -s -H "X-Beta-User: true" http://localhost | grep -o 'v[12]'; done
결과 : v2 v2 v2 v2 v2 v2 v2 v2 v2 v2