Profile picture

[Linux] HAProxy + Keepalived로 고가용성 웹 서비스 구축해보기

JaehyoJJAng2025년 01월 10일

개요

"장애를 견딜 수 있는 고가용성 아키텍처를 직접 설계하고 구축해봤다"라고 자신 있게 말할 수 있도록, 온프레미스 환경에서 무중단 웹 서비스를 만들어 보겠습니다.


작업 순서는 대략 다음과 같습니다.

  • 네트워크 아키텍처 설계 (Public, DMZ, Internal)
  • L7 로드 밸런싱 (HAProxy)
  • IP Failover 이중화 (Keepalived)
  • 리버스 프록시와 WAS 연동 (Nginx + FastAPI)
  • 방화벽 포트 포워딩 (Fortinet 기준)

STEP 1: 고가용성 아키텍처를 향해

모든 구축의 시작은 탄탄한 설계라고 하죠?

제 목표는 서비스의 어떤 한 부분이 고장 나더라도 전체 서비스가 중단되지 않는 것!

즉, 단일 장애 지점(Single Point of Failure) 을 제거하는 것입니다.


네트워크 구성도

PNETLab과 같은 가상화 시뮬레이터를 사용하여 다음과 같은 아키텍처를 구성할겁니다.
image


핵심 컴포넌트 역할을 다음과 같이 정리해봤습니다.

컴포넌트 역할
HAProxy (in DMZ) 사용자의 모든 요청을 가장 먼저 받는 L7 로드밸런서입니다. 내부 웹 서버들의 상태를 주기적으로 확인(Health Check)하여 정상인 서버에게만 트래픽을 분배합니다.
Keepalived (in DMZ) HAProxy 서버 자체의 고장을 대비합니다. 가상의 IP(VIP: 10.0.1.100)를 하나 만들어 Master 서버가 사용하다가, Master 서버에 장애가 발생하면 즉시 Backup 서버가 VIP를 가져와 서비스 중단을 막습니다.
Nginx (in Internal) 실제 애플리케이션(FastAPI) 바로 앞에서 요청을 받아 처리하는 리버스 프록시 웹 서버입니다.
FastAPI (in Internal) 간단한 웹 애플리케이션입니다.

STEP 2: 기초 공사 - 내부 웹 서버 구축하기

먼저 요청을 실제로 처리할 내부 웹 서버 2대(web-1, web-2)를 준비할게요.


2-1. FastAPI 애플리케이션 준비

두 서버에서 동일하게 간단한 파이썬 웹 앱을 만들도록 합시다.

# 필요한 패키지 설치
sudo apt update && sudo apt install -y python3-pip

# FastAPI와 Uvicorn(WAS) 설치
pip3 install fastapi uvicorn

# 앱 디렉토리 및 파일 생성
mkdir -p ~/app && cd ~/app
cat << EOF > main.py
from fastapi import FastAPI
import socket

app = FastAPI()
hostname = socket.gethostname()

@app.get("/")
def read_root():
    return {"message": f"Hello from {hostname}"}

@app.get("/test")
def read_test():
    return {"message": f"This is a test page from {hostname}"}
EOF

2-2. Nginx 설치 및 연동

80번 포트로 들어온 요청을 내부 8000번 포트의 FastAPI 앱으로 넘겨주는 리버스 프록시로 Nginx를 설정하겠습니다.

# Nginx 설치
sudo apt install -y nginx

# Nginx 설정 파일 수정 (/etc/nginx/sites-available/default)
sudo tee /etc/nginx/sites-available/default > /dev/null <<'EOF'
server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EOF

2-3. 서비스 자동 실행 등록 (Systemd)

서버가 재부팅 되어도 FastAPI 앱이 자동으로 실행되도록 서비스로 등록해줄게요!

# /etc/systemd/system/fastapi.service 파일 생성
sudo tee /etc/systemd/system/fastapi.service > /dev/null <<'EOF'
[Unit]
Description=FastAPI application
After=network.target

[Service]
User=ubuntu # 실제 사용자 계정으로 변경
Group=www-data
WorkingDirectory=/home/ubuntu/app # 실제 앱 경로로 변경
ExecStart=/usr/local/bin/uvicorn main:app --host 127.0.0.1 --port 8000

[Install]
WantedBy=multi-user.target
EOF

# 서비스 활성화 및 시작
sudo systemctl enable fastapi
sudo systemctl start fastapi
sudo systemctl restart nginx

등록하면 다음과 같이 systemd로 제어가 가능하고, journalctl 명령어로 시스템 로그도 확인 가능합니다!
image


STEP 3: 핵심 방어선 - DMZ 로드밸런서 구축하기

이제 서비스의 관문이 될 haproxy-1haproxy-2를 설정할 차례입니다!


3-1. HAProxy 설치 및 설정

두 서버에 동일하게 HAProxy를 설치하고 내부 웹 서버로 트래픽을 분산하도록 설정합니다.

sudo apt install -y haproxy

# /etc/haproxy/haproxy.cfg 설정
sudo tee /etc/haproxy/haproxy.cfg > /dev/null <<'EOF'
global
    log /dev/log    local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /
    server web-1 192.168.10.10:80 check
    server web-2 192.168.10.11:80 check
EOF

sudo systemctl restart haproxy

3-2. Keepalived로 IP Failover 구현

드디어 VIP를 설정하고 Master-Backup 이중화를 구현할 차례입니다.


haproxy-1 (Master) 설정

sudo apt install -y keepalived

# /etc/keepalived/keepalived.conf
sudo tee /etc/keepalived/keepalived.conf > /dev/null <<'EOF'
vrrp_instance VI_1 {
    state MASTER           # Master 서버
    interface eth1         # VIP가 할당될 실제 인터페이스
    virtual_router_id 51   # Master/Backup 그룹 ID (동일해야 함)
    priority 101           # 우선순위 (Master가 높아야 함)
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.10.30         # 서비스 대표 가상 IP (VIP)
    }
}
EOF

sudo systemctl restart keepalived

haproxy-2 (Backup) 설정

sudo apt install -y keepalived

# /etc/keepalived/keepalived.conf
sudo tee /etc/keepalived/keepalived.conf > /dev/null <<'EOF'
vrrp_instance VI_1 {
    state BACKUP           # Backup 서버
    interface eth1
    virtual_router_id 51
    priority 100           # 우선순위 (Backup이 낮아야 함)
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.10.30
    }
}
EOF

sudo systemctl restart keepalived

이제 haproxy-1 (Master) 서버에서 ip a 명령어를 쳐보면 192.168.10.30 이라는 IP가 추가로 할당된 것을 볼 수 있어요!
image


STEP 4: 최종 관문 - 방화벽 포트 포워딩

이제 외부 인터넷에서 들어오는 요청이 우리 서비스의 VIP로 도달할 수 있도록 방화벽을 설정해야 합니다!
(Fortinet 기준)


1. Virtual IP (VIP) 객체 생성

Policy & Objects > Virtual IPs에서 외부 공인 IP:80/443 포트와 내부 로드밸런서 VIP(10.0.1.100):80/443 포트를 매핑하는 객체를 만듭니다.


2. 방화벽 정책 생성

Policy & Objects > Firewall Policy에서 아래와 같은 정책을 만듭니다.

  • Incoming Interface: wan1 (외부망 인터페이스)
  • Outgoing Interface: dmz (내부망 인터페이스)
  • Source: all
  • Destination: 위에서 만든 VIP 객체
  • Service: HTTP, HTTPS
  • Action: ACCEPT

image
이로써 외부에서 방화벽의 공인 IP로 접속하면, 그 요청이 안전하게 내부 로드밸런서의 VIP로 전달되겠죠?


STEP 6: 쇼타임! 무중단 서비스 테스트하기

이제 가장 재미있는 시간입니다!

우리가 만든 시스템이 정말 장애를 견뎌내는지 테스트해 봅시다!


6-1. 정상 상태 (로드밸런싱)

클라이언트 PC에서 VIP(192.168.10.30)로 계속 요청을 보내 web-1web-2에서 번갈아 응답이 오는지 확인해볼게요.

while true; do curl -k https://10.0.1.100; sleep 1; done

image


6-2. 로드밸런서 장애 상황 만들기

  • 클라이언트에서 ping 192.168.10.30 (VIP)을 계속 실행합니다.
  • PnetLab에서 haproxy-1 (Master) 서버를 중지시킵니다.
  • Ping이 1~2개 정도 끊겼다가 즉시 다시 응답하기 시작합니다.
  • haproxy-2 (Backup) 서버에 접속해 ip a를 실행하면, VIP(192.168.10.30)가 이쪽으로 넘어온 것을 확인할 수 있습니다.

Loading script...