본문 바로가기
Cloud Computing & MSA

HAProxy L7 로드밸런싱 - balance 알고리즘 고르기, httpchk 헬스체크(기본은 OPTIONS), sticky 세션과 stats 대시보드

1103동103호·2026년 9월 5일·조회 1

서버를 두 대 이상으로 늘리고 나면 그 앞에 뭘 세울지가 다음 고민이 된다. 클라우드 관리형 로드밸런서(ALB, NLB)를 쓰면 편하지만, 온프레미스나 단일 VM 위에서 프로세스를 여러 개 띄워 분산하는 경우, 세밀한 헬스체크가 필요한 경우엔 HAProxy를 직접 세우는 쪽이 손에 잡힌다. 예전에 Nginx로 upstream 분산만 걸어 쓰던 구성을 HAProxy로 옮기면서 정리한 내용을 여기 남긴다.

결론부터 말하자면, HAProxy L7 로드밸런싱의 뼈대는 frontend(수신)와 backend(분배 대상 서버 묶음) 두 블록이다. 트래픽을 어떻게 나눌지는 balance로 정하고(웹은 roundrobin, 요청 비용 편차가 큰 API는 leastconn), 죽은 노드를 빼는 건 option httpchk 헬스체크가 맡는다. 여기서 헬스체크는 기본적으로 GET이 아니라 OPTIONS 요청을 /로 보낸다는 점을 알고 시작해야 헬스 엔드포인트와 방화벽, 로그 필터를 잘못 잡지 않는다.

1. 용어 정리: L7 로드밸런싱과 HAProxy의 구조

L7(레이어 7) 로드밸런싱은 HTTP 요청 내용, 즉 URL 경로, 헤더, 쿠키까지 들여다보고 분배 대상을 정하는 방식이다. TCP 연결만 보고 나누는 L4와 달리 경로별 라우팅이나 쿠키 기반 세션 고정을 걸 수 있다.

HAProxy(High Availability Proxy)는 TCP/HTTP 로드밸런서 겸 프록시다. 설정은 크게 네 블록으로 나뉜다.

  • global - 프로세스 전역 설정(로그, 최대 연결 수, 실행 사용자).
  • defaults - 아래 블록들이 공유할 기본값(모드, 타임아웃).
  • frontend - 클라이언트 요청을 받는 입구. 어떤 포트를 열고 어느 backend로 넘길지 정한다.
  • backend - 실제 요청을 처리할 서버 목록과 분배 규칙.

발행 시점(2026-09) 기준 최신 LTS는 3.4 LTS(2026-06 릴리스, 최신 3.4.4)이고, 그 이전 LTS인 3.2 LTS(2025-05 릴리스, 최신 3.2.23)도 여전히 유지보수 대상이다. 새로 까는 서버라면 3.4 또는 3.2 계열을 권한다. 세부 패치 번호는 배포판 패키지 상태에 따라 다를 수 있다.

# RHEL 9 계열
sudo dnf install -y haproxy

# Ubuntu 24.04
sudo apt install -y haproxy

haproxy -v
# HAProxy version 3.2.x ... - https://haproxy.org/

배포판 기본 저장소 버전이 낮으면 벤더 제공 패키지 저장소를 붙여 최신 LTS를 받는다. 설정 파일은 /etc/haproxy/haproxy.cfg 한 곳이다.

2. frontend와 backend 최소 구성

웹 서버 두 대를 뒤에 두는 가장 단순한 형태다. mode http가 L7 처리를 켜는 스위치다.

global
    log /dev/log local0
    maxconn 20000
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    http
    option  httplog
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend web_front
    bind *:80
    default_backend web_back

backend web_back
    balance roundrobin
    server web1 10.0.1.11:8080 check
    server web2 10.0.1.12:8080 check

server 줄 끝의 check가 헬스체크를 켠다. 이게 없으면 HAProxy는 노드가 죽어도 계속 트래픽을 보낸다. 여기까지가 L4 수준 TCP 연결 확인이다. HTTP 응답까지 확인하려면 다음 절의 option httpchk가 필요하다.

설정을 고쳤으면 반영 전에 문법부터 검사한다. 이 습관 하나로 오타 때문에 서비스가 안 뜨는 사고를 막는다.

haproxy -c -f /etc/haproxy/haproxy.cfg
# Configuration file is valid

sudo systemctl reload haproxy

reload는 기존 연결을 유지한 채 설정만 갈아끼운다. restart는 연결이 끊기니 운영 중엔 reload를 쓴다.

3. balance 알고리즘, 무엇을 언제 쓰나

balance는 backend 안에서 요청을 어느 server로 보낼지 정하는 규칙이다. 자주 쓰는 것만 추린다.

roundrobin

순서대로 돌아가며 배정한다. 요청 하나하나의 처리 비용이 비슷한 정적/일반 웹 트래픽의 기본값이다. server 가중치(weight)도 반영한다.

leastconn

현재 활성 연결이 가장 적은 server로 보낸다. 요청마다 처리 시간이 들쭉날쭉한 API, DB를 물고 오래 붙어 있는 연결에 맞다. 한쪽 노드에 무거운 요청이 몰려도 나머지가 받아준다.

source

클라이언트 IP를 해시해 같은 IP는 늘 같은 server로 보낸다. 쿠키 없이 IP 기준으로 세션을 고정하고 싶을 때 쓴다. 다만 NAT 뒤에 있는 다수 사용자가 한 IP로 묶이면 분배가 고르지 않다.

uri

요청 URI를 해시한다. 같은 경로 요청이 같은 server로 가므로 캐시 적중률을 높이려는 캐싱 backend에 유용하다.

권장은 이렇다. 상태 없는 웹 요청은 roundrobin, 요청 비용 편차가 큰 API 서버는 leastconn을 기본으로 잡는다. 세션 고정이 꼭 필요하면 IP 기반 source보다 다음 절의 쿠키 방식이 더 정밀하다.

4. option httpchk 헬스체크 - 기본 요청은 OPTIONS다

헬스체크는 backend server가 살아 있는지 주기적으로 확인해, 응답이 없거나 비정상이면 분배 대상에서 자동으로 빼는 기능이다. option httpchk를 걸면 TCP 연결만 보던 검사가 HTTP 응답까지 확인하는 L7 검사로 올라간다.

여기서 반드시 짚어야 할 기본 동작이 있다. option httpchk를 인자 없이 켜면 HAProxy는 GET이 아니라 OPTIONS 요청을 / 경로로 보낸다. 응답 상태 코드가 2xx 또는 3xx면 정상으로 판정한다. 공식 문서도 "Checks send an OPTIONS request to the URL / by default"라고 못박고 있다.

이 기본값을 모르면 함정에 걸린다. 애플리케이션이 /에 대한 OPTIONS를 처리하지 않아 405를 내거나, 프론트에 둔 WAF/프록시가 OPTIONS를 막거나, 접근 로그 필터를 GET 기준으로 잡아두면 멀쩡한 노드가 DOWN으로 빠진다. 그래서 실무에선 메서드와 경로를 명시하는 쪽을 권한다.

backend api_back
    balance leastconn
    option httpchk GET /healthz
    http-check expect status 200
    server api1 10.0.1.21:8080 check inter 3s fall 3 rise 2
    server api2 10.0.1.22:8080 check inter 3s fall 3 rise 2

option httpchk GET /healthz로 메서드와 경로를 바꾸고, http-check expect status 200으로 성공 조건을 200 하나로 좁혔다. Spring Boot라면 /actuator/health를 경로로 쓰면 그대로 맞아떨어진다. server 줄의 옵션은 이렇게 읽는다.

  • inter 3s - 3초 간격으로 검사.
  • fall 3 - 연속 3번 실패하면 DOWN 처리.
  • rise 2 - 연속 2번 성공하면 다시 UP.

헬스 엔드포인트는 가볍게 두는 게 좋다. DB 커넥션 검사까지 무겁게 물려두면 DB가 잠깐 느려질 때 전 노드가 동시에 DOWN으로 빠져 로드밸런서가 보낼 곳을 잃는다.

5. sticky 세션 - cookie insert로 노드 고정

서버 로컬에 세션을 들고 있는 애플리케이션(전형적인 톰캣 HttpSession)은 매 요청이 같은 노드로 가야 로그인 상태가 유지된다. HAProxy는 cookie 지시자로 backend server마다 식별 쿠키를 심어 이 고정을 처리한다.

backend web_back
    balance roundrobin
    cookie SRVID insert indirect nocache
    server web1 10.0.1.11:8080 check cookie web1
    server web2 10.0.1.12:8080 check cookie web2

동작을 뜯어보면 이렇다.

  • cookie SRVID insert - HAProxy가 SRVID라는 쿠키를 응답에 넣는다.
  • indirect - 이 쿠키는 backend server로 다시 전달하지 않는다(애플리케이션은 존재를 모른다).
  • nocache - 중간 캐시가 이 쿠키 붙은 응답을 캐싱하지 못하게 막는다.
  • server 줄의 cookie web1 - 각 노드에 부여할 쿠키 값. 클라이언트가 SRVID=web1을 들고 오면 web1로만 보낸다.

다만 sticky 세션은 확장의 발목을 잡는다. 특정 노드에 세션이 몰리면 분배가 기울고, 그 노드가 죽으면 거기 붙어 있던 사용자 세션이 통째로 날아간다. 그래서 가능하면 세션을 Redis 같은 외부 저장소로 빼고(Spring Session 등) HAProxy는 roundrobin/leastconn으로 고정 없이 굴리는 구성을 권한다. 세션 외부화가 당장 어려운 레거시에서만 cookie insert를 임시 방편으로 쓰는 게 맞다.

6. stats 대시보드로 트래픽과 장애 노드 보기

HAProxy는 내장 상태 페이지를 제공한다. 어느 노드가 UP/DOWN인지, 각 backend로 세션이 얼마나 가는지, 헬스체크 실패 횟수가 얼마인지 브라우저에서 바로 확인할 수 있다. listen 블록으로 별도 포트에 올린다.

listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:CHANGE_ME

reloadhttp://서버IP:8404/stats로 접속하면 backend별 표가 뜬다. 인증(stats auth) 없이 열지 말고, 8404 포트는 방화벽/보안그룹에서 관리자 IP로만 제한한다. 공개 노출은 서버 내부 구조를 그대로 드러내는 것과 같다.

표에서 눈여겨볼 열은 이렇다.

  • Status - UP(초록)/DOWN(빨강). DOWN이면 옆에 마지막 헬스체크 실패 사유(예: L7STS/405)가 뜬다. 405가 보이면 4절에서 말한 OPTIONS 문제일 확률이 높다.
  • Sessions - 노드별 현재/누적 세션 수. balance가 고르게 먹는지 여기서 확인한다.
  • Bytes - 인/아웃 전송량.
  • Check - 헬스체크 결과 코드. L7OK/200이면 정상.

운영 중 특정 노드만 잠깐 빼고 싶을 때는 stats 페이지에서 직접 조작하는 대신 소켓 명령이나 설정으로 처리하는 편이 이력 관리에 낫다. 스크립트로 상태만 뽑아보려면 admin 소켓의 show stat을 쓴다.

echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d, -f1,2,18
# pxname,svname,status
# web_back,web1,UP
# web_back,web2,DOWN

이렇게 하면 모니터링 도구나 알림 스크립트에서 노드 상태를 파싱해 쓸 수 있다.

7. 정리

HAProxy L7 구성은 frontend로 받고 backend로 나누는 두 블록이 핵심이다. 분배는 웹이면 roundrobin, 비용 편차가 큰 API면 leastconn을 기본으로 잡는다. 헬스체크는 option httpchk의 기본이 OPTIONS /라는 점을 전제로, 메서드와 경로를 명시하고 http-check expect status로 성공 조건을 좁혀두면 오탐이 준다. 세션 고정은 cookie insert로 가능하지만 확장을 막으니 외부 세션 저장소를 먼저 검토한다. 마지막으로 stats 페이지는 인증과 포트 제한을 걸어 올려두면 장애 노드 판별이 빨라진다.

자주 묻는 질문

option httpchk는 기본으로 어떤 요청을 보내나?

인자 없이 option httpchk만 켜면 HAProxy는 OPTIONS 요청을 / 경로로 보낸다. GET이 아니다. 응답 상태 코드가 2xx 또는 3xx면 정상으로 판정한다. 특정 메서드와 경로가 필요하면 option httpchk GET /healthz 처럼 명시하고, http-check expect status 200으로 성공 조건을 좁히는 것이 안전하다.

roundrobin과 leastconn 중 무엇을 골라야 하나?

요청 하나하나의 처리 비용이 비슷한 정적/일반 웹 트래픽은 roundrobin이 무난하다. 요청마다 처리 시간이 들쭉날쭉한 API나 오래 붙어 있는 연결은 leastconn이 낫다. leastconn은 활성 연결이 가장 적은 노드로 보내므로 무거운 요청이 한쪽에 몰려도 나머지가 받아준다.

sticky 세션을 꼭 써야 하나?

서버 로컬에 세션을 저장하는 레거시 애플리케이션이면 cookie insert로 노드를 고정해야 로그인이 유지된다. 다만 노드가 죽으면 거기 붙은 세션이 날아가고 분배도 기울 수 있다. 가능하면 세션을 Redis 같은 외부 저장소로 빼고 고정 없이 roundrobin/leastconn으로 굴리는 편을 권한다.

설정을 바꾼 뒤 restart와 reload 중 무엇을 써야 하나?

운영 중이면 reload를 쓴다. reload는 기존 연결을 유지한 채 설정만 교체하므로 서비스 중단이 없다. restart는 프로세스를 내렸다 올려 연결이 끊긴다. 반영 전에는 haproxy -c -f /etc/haproxy/haproxy.cfg로 문법 검사부터 하는 습관이 오타 사고를 막는다.

HAProxy는 어떤 버전을 설치해야 하나?

2026-09 기준 최신 LTS는 3.4 LTS(2026-06 릴리스)이고 이전 LTS인 3.2 LTS(2025-05 릴리스)도 유지보수 대상이다. 새 서버라면 3.4 또는 3.2 계열을 권한다. 짝수 번호 브랜치가 LTS로 릴리스 후 5년간 유지된다. 배포판 기본 저장소 버전이 낮으면 벤더 제공 패키지 저장소를 붙여 받는다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.