haproxy

haproxy(+keepalived)를 이용한 로드밸런싱 구성

misankim 2023. 4. 7. 00:30

웹서버가 다중으로 구성된 환경에서는 유입되는 트래픽을 다수의 웹서버로 전달할 로드밸런서의 역할이 필요합니다. 로드밸런서를 별도로 구성하지 않고 DNS 라운드로빈으로 1개 도메인에 여러 웹서버를 연결할 수도 있지만, 일반적인 네임서버에서 구성한 DNS 라운드로빈의 경우 헬스체크가 불가능한 단점이 있습니다.

로드밸런싱의 경우 물리장비인 L4 스위치를 통해 1개의 VIP(virtual IP)를 통해 수신한 트래픽을 다수의 real IP(웹서버)로 NAT하는 방식으로 사용하는 것이 일반적이나, haproxy 를 이용하여 소프트웨어적인 리버스 프록시 로드밸런서를 구성할 수 있는 방법이 있어 소개하고자합니다.

haproxy 란

HAProxy는 여러 서버에 걸쳐 요청을 분산시키는 TCP 및 HTTP 기반 응용 프로그램을위한 고 가용성로드 밸런서 및 프록시 서버를 제공하는 무료 오픈 소스 소프트웨어입니다.

토스트 클라우드 로드밸런서

토스트 클라우드 로드밸런서의 정보를 확인 시 아래와 같이 haproxy를 이용한 소프트웨어적인 로드밸런서임을 확인할 수 있습니다.

고가용성을 유지하기 위한 구성 포인트

haproxy 를 이용하여 부하분산을 하기 위해 아래와 같은 구성을 할 수 있습니다.

다만 위의 구성 시 한가지 문제점이 있습니다. 부하분산을 위한 haproxy 서버가 1대이기 때문에 haproxy 서버의 장애 발생 시 모든 서비스가 중단되는 점입니다.

때문에 haproxy 는 일반적으로 keepalived, pacemaker 등과 같은 장애조치(failover)를 위한 패키지를 이용하여 액티브-스탠바이 구성을 합니다. 액티브-스탠바이로 구성된 구성도는 아래와 같습니다.

구성 테스트

1) 웹서버 구성

웹서버는 각각 아래와 같이 web server1 과 web server2의 내용이 다르게 보이도록 구성했습니다. (실제 구성이라면 서버의 아이피나 도메인이 다른 두 서버로 구성하는게 맞으나, 편의상 한 서버에 다른 포트를 가진 두 데몬을 올려 진행했습니다.)

 

2) haproxy 구성

아래부터는 harpoxy 서버를 세팅합니다. haproxy 설치는 yum 설치와 소스컴파일 설치 모두 가능합니다. 가능한 최신 버전으로 사용하고 싶으시다면 소스컴파일 설치를 하는게 낫고, 낮은 버전이라도 관계 없다면 yum 으로 설치합니다. 테스트 구성에서는 소스컴파일 설치했습니다.

# yum 설치
yum install -y haproxy
# 소스컴파일 설치

## 필수 패키지 설치
yum groupinstall -y "Development tools"
yum install -y wget readline-devel openssl-devel pcre-devel systemd-devel

## lua 5.3 버전 이상 설치(lua 패키지 설치를 잘 못하면 rpm, yum 사용이 불가능해집니다. 신중하게 설치해야합니다.)
-> haproxy 컴파일 설치 시 lua-devel 5.3 버전 이상 필요하나, 
yum 으로 설치 가능한 버전은 5.1 버전이라 소스컴파일 설치

curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz
tar zxvf lua-5.3.4.tar.gz
cd lua-5.3.4
make linux test
make install

lua -v

## haproxy 설치
wget http://www.haproxy.org/download/2.2/src/haproxy-2.2.2.tar.gz

tar zxvf haproxy-2.2.2.tar.gz
cd haproxy-2.2.2

make -j $(nproc) TARGET=linux-glibc USE_OPENSSL=1 USE_ZLIB=1 USE_LUA=1 USE_PCRE=1 USE_SYSTEMD=1
make install

haproxy -v

cp examples/haproxy.init /etc/rc.d/init.d/haproxy
chmod 755 /etc/rc.d/init.d/haproxy

소스 컴파일 설치 시 haproxy 실행파일 경로 수정
sed -i 's;BIN=/usr/sbin/$BASENAME;BIN=/usr/local/sbin/$BASENAME;' /etc/rc.d/init.d/haproxy

haproxy 설정 파일(/etc/haproxy/haproxy.cfg)은 아래와 같이 구성합니다.

global # 서버의 전역 설정 부분입니다
    maxconn 50000 # 최대 수용 가능한 커넥션 수치입니다. defaults, frontend 등 하위 항목에 동일 설정이 있다면 하위 항목의 설정값이 우선입니다.
    log /dev/log local0 # 로그를 syslog로 보낼 수 있습니다. syslog 설정을 통해 원하는 파일에 로그를 남길 수 있습니다.

defaults # 서버의 기본 설정 부분입니다.
    timeout connect 10s
    timeout client 30s
    timeout server 30s
    log global
    mode http
    option httplog # httplog 옵션을 사용하면 로그를 조금 더 http 포맷에 맞게 상세히 남길 수 있습니다.(기본 로그는 tcplog)
    maxconn 3000

listen stats # frontend와 backend를 포함하는 설정 섹션이지만, haproxy 상태를 모니터링하는 페이지인 stats 페이지를 위한 설정이 들어가 있습니다. 
    bind *:8404
    stats enable
    stats uri /monitor
    stats refresh 5s

frontend haproxy_web # 트래픽을 수신하는 haproxy에 대한 설정입니다. haproxy_web는 레이블입니다.
    bind *:80
    default_backend web_servers

backend web_servers # 트래픽을 전달받을 백엔드(웹서버)에 대한 설정입니다. web_servers도 레이블입니다.
    balance roundrobin # 로드밸런싱 방식을 지정합니다.
    option httpchk HEAD / # 헬스체크에 대한 옵션을 지정합니다.
    default-server maxconn 20
    server server1 web1.premisan.shop:80 check # 실제 트래픽을 전달받을 서버에 대한 설정값입니다.
    server server2 web2.premisan.shop:8080 check

설정파일 구문 검사는 아래 명령어를 통해 진행합니다. 설정 파일에 문제가 없다면 "Configuration file is valid" 라는 문구가 출력됩니다.

haproxy -c -f /etc/haproxy/haproxy.cfg

haproxy 실행, 자동실행 설정

systemctl enable haproxy && systemctl start haproxy

구성한 두 haproxy 서버로 각각 접속하여 여러번 새로고침(F5)을 합니다. server1과 server2의 내용이 번갈아 가며 보여진다면 정상적인 것으로 확인할 수 있습니다. haproxy 의 상태를 확인 할 수 있는 stats 페이지에 접속합니다. stats 페이지에서 프론트엔드와 백엔드의 상태를 체크할 수 있습니다. 특히 우측 하단의 있는 server1, server2의 헬스체크 상태도 모니터링 가능합니다.

만약 하나의 웹서버가 다운된다면 헬스체크에 실패하여 아래와 같이 해당 서버가 붉게 표시되고 해당 서버로는 트래픽을 전달하지 않습니다.

추가로 백엔드에 쿠키를 이용하여 세션 지속성 설정을 구성하는 방법입니다. 토스트 클라우드 로드밸런서의 HTTP_COOKIE 방식처럼 별도의 쿠키를 추가하는 방식입니다. /etc/haproxy/haproxy.cfg 파일에 백엔드 설정을 아래와 같이 구성합니다.

backend web_servers
    balance roundrobin
    cookie SERVERUSED insert indirect nocache # SERVERUSED 라는 이름의 쿠키를 클라이언트로 전송
    option httpchk HEAD /
    default-server maxconn 20
    server server1 web1.premisan.shop:80 check cookie server1 # 쿠키를 활성화하며, 쿠키의 값은 server1
    server server2 web2.premisan.shop:8080 check cookie server2

클라이언트에서 쿠키는 아래와 같이 확인 가능합니다.

이번에는 토스트 클라우드 로드밸런서의 APP_COOKIE 방식처럼 백엔드 서버의 쿠키에 인스턴스 정보를 추가하는 방식입니다. /etc/haproxy/haproxy.cfg 파일에 백엔드 설정을 아래와 같이 구성합니다.

backend web_servers
    balance roundrobin
    cookie JSESSIONID prefix indirect nocache # 톰캣의 쿠키인 jsessionid 에 인스턴스 정보를 prefix로 추가합니다.
    option httpchk HEAD /
    default-server maxconn 20
    server server1 web1.premisan.shop:80 check cookie server1
    server server2 web2.premisan.shop:8080 check cookie server2

접속하여 쿠키를 확인해보면 jsessionid 쿠키의 앞에 "server2~"라는 값을 추가하여 트래픽이 전달될 인스턴스를 표시한 것을 알 수 있습니다.

3) keepalived 구성

이제 haproxy 구성은 완료하였으니 두 haproxy 서버를 failover로 구성할 keepalived 를 구성합니다. pacemaker 도 keepalived 와 동일하게 failover를 위한 패키지지만, keepalived 쪽이 설정하기가 더 간단했습니다.

keepalived 작동 원리

keepalived 는 서비스에 사용할 VIP(virtual IP)를 마스터 서버에서 사용할 수 있도록 하며, 마스터 서버에서 VRRP(Virtual Router Redundancy Protocol)를 이용하여 다수의 서버로 자신의 우선순위(priority)를 ADVERTISEMENT합니다. (ADVERTISEMENT 주기는 keepalived 설정 파일에서 설정 가능합니다.)

백업 서버는 마스터 서버로부터 ADVERTISEMENT 트래픽이 수신되지 않으면 자신이 마스터 서버가 되며, VIP를 인계받아 서비스를 이어갑니다. 만약 다시 마스터 서버가 살아나는 경우 우선순위 값에 의해 백업 서버는 다시 백업 상태로 들어가고, 복구된 마스터 서버가 마스터 역할을 이어 받습니다. 아래는 keepalived 를 구성하는 과정입니다. 설치는 간단히 yum 으로 설치합니다.

yum install -y keepalived

keepalived 의 설정 파일을 구성합니다. 액티브 상태로 운영할 마스터 서버와 마스터 서버 다운 시 마스터 권한을 인계 받을 백업 서버의 설정을 각각 다르게 진행합니다. 아래 설정 중 vrrp_script 라는 블럭이 중요한데, 기본적으로 keepalived는 서버가 죽었는지 살았는지만 체크합니다.

때문에 haproxy 서버가 살아는 있지만 데몬이 정상적으로 작동하지 않는 상태일 때, keepalived는 서버가 살아있는 것으로 간주하기 때문에 백업 서버가 마스터 권한을 인계받지 않습니다. 이런 경우 서버의 상태를 체크하기 위한 부가적인 스크립트를 지정할 수 있는데 스크립트의 실행이 성공하는 경우 "weight 10"이라는 설정값에 따라 서버의 priority가 weight 만큼 증가합니다.

예를 들어 마스터 서버가 살아 있으며, 스크립트가 성공하는 경우 priority는 100+10이 되어 110이 됩니다. 백업 서버의 경우 스크립트가 성공하는 경우 priority는 99+10이 되어 109이 됩니다.

-> 두 서버가 모두 살아있으며, 스크립트가 성공하는 경우 마스터 서버가 마스터 역할을 유지합니다.

하지만 마스터 서버가 살아있음에도 스크립트가 실패하는 경우 priority는 100+0이 되어 100이 됩니다. 이때, 백업 서버의 경우 스크립트가 성공하는 경우 priority는 99+10이 되어 109이 되기 때문에 이 경우 백업 서버가 마스터 역할을 인계받습니다.

vim /etc/keepalived/keepalived.conf

# 마스터
-> 아래는 상태 체크를 위한 스크립트 및 실행 간격, 실패 임계값, 성공 임계값, 가중치입니다.
vrrp_script my_chk {
    script       "/usr/local/bin/http_check.sh"
    interval 2
    fall 2
    rise 2
    weight 10
}

vrrp_instance VI_1 {
    state MASTER
    interface enp0s8
    virtual_router_id 51
    priority 100 # 마스터 서버의 우선 순위입니다.
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.2.100 # 서비스에 사용할 VIP입니다.
    }
track_script {
      my_chk # 스크립트를 수행할 것을 명시하는 부분입니다.
    }
}

# 백업
vrrp_script my_chk {
    script       "/usr/local/bin/http_check.sh"
    interval 2
    fall 2
    rise 2
    weight 10
}

vrrp_instance VI_1 {
    state BACKUP
    interface enp0s8
    virtual_router_id 51
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.2.100
    }
track_script {
      my_chk
    }
}

아래는 vrrp_script my_chk 블럭에 지정한 "/usr/local/bin/http_check.sh" 스크립트의 내용입니다. 스크립트는 예시이기 때문에 상황에 따라 수정하여 구성합니다.

vim /usr/local/bin/http_check.sh

#!/bin/bash
curl -v http://localhost/ > /tmp/http_check.log 2>&1
grep "HTTP/1.1 200" /tmp/http_check.log 

if [ "$?" == "0" ]
then 
        exit 0
else
        exit 1
fi

keepalived 데몬을 실행합니다. 설정한 VIP(192.168.2.100)으로 ping 응답이 있는지 체크합니다.

VIP를 통해 서비스에 접속해봅니다.

haproxy 로그를 확인하여 접속이 마스터 서버로 이뤄진 것이 맞는지 확인이 가능합니다. (rsyslog.conf 에 아래 설정이 필요합니다)

local0.\*                                                /var/log/haproxy

이제 마스터 서버의 haproxy 데몬을 다운시켜 백업 서버가 마스터 역할을 인계받는지 확인합니다. 마스터, 백업 상태의 경우 /var/log/messages 에 로그가 남기 때문에 tail -f 로 서버의 상태가 변하는 것을 실시간으로 확인 가능합니다.

systemctl stop haproxy

마스터 서버의 messages 로그는 아래와 같이 표시됩니다. 로그의 내용은 지정한 스크립트가 2회 실패하여 우선순위가 기존 110에서 100으로 변경되었고, 자신의 우선 순위보다 높은 우선순위를 가진 keepalived 서버가 있기 때문에 자신은 백업 상태로 돌입한다는 내용입니다.

백업 서버의 messages 로그는 아래와 같이 표시됩니다. 로그의 내용은 마스터 서버보다 자신의 우선순위가 더 높기 때문에 새 마스터 선출을 하여 마스터 상태로 돌입하고 VIP(192.168.2.100)을 지정한 인터페이스에 세팅한다는 내용입니다.

백업 서버가 마스터 역할을 인계받은 후에도 서비스에 정상적으로 접속되는지 확인합니다.

또한 haproxy 로그가 백업 서버(현재 마스터 역할)에 쌓이는 지 확인합니다.

이렇게 haproxy를 통한 로드밸런싱 설정과 haproxy 서버를 failover 할 수 있도록 keepalived 를 세팅하는 방법을 알아보았습니다.