개요
"장애를 견딜 수 있는 고가용성 아키텍처를 직접 설계하고 구축해봤다"라고 자신 있게 말할 수 있도록, 온프레미스 환경에서 무중단 웹 서비스를 만들어 보겠습니다.
작업 순서는 대략 다음과 같습니다.
- 네트워크 아키텍처 설계 (Public, DMZ, Internal)
- L7 로드 밸런싱 (HAProxy)
- IP Failover 이중화 (Keepalived)
- 리버스 프록시와 WAS 연동 (Nginx + FastAPI)
- 방화벽 포트 포워딩 (Fortinet 기준)
STEP 1: 고가용성 아키텍처를 향해
모든 구축의 시작은 탄탄한 설계라고 하죠?
제 목표는 서비스의 어떤 한 부분이 고장 나더라도 전체 서비스가 중단되지 않는 것!
즉, 단일 장애 지점(Single Point of Failure) 을 제거하는 것입니다.
네트워크 구성도
PNETLab과 같은 가상화 시뮬레이터를 사용하여 다음과 같은 아키텍처를 구성할겁니다.
핵심 컴포넌트 역할을 다음과 같이 정리해봤습니다.
컴포넌트 | 역할 |
---|---|
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
명령어로 시스템 로그도 확인 가능합니다!
STEP 3: 핵심 방어선 - DMZ 로드밸런서 구축하기
이제 서비스의 관문이 될 haproxy-1
과 haproxy-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가 추가로 할당된 것을 볼 수 있어요!
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
이로써 외부에서 방화벽의 공인 IP로 접속하면, 그 요청이 안전하게 내부 로드밸런서의 VIP로 전달되겠죠?
STEP 6: 쇼타임! 무중단 서비스 테스트하기
이제 가장 재미있는 시간입니다!
우리가 만든 시스템이 정말 장애를 견뎌내는지 테스트해 봅시다!
6-1. 정상 상태 (로드밸런싱)
클라이언트 PC에서 VIP(192.168.10.30
)로 계속 요청을 보내 web-1
과 web-2
에서 번갈아 응답이 오는지 확인해볼게요.
while true; do curl -k https://10.0.1.100; sleep 1; done
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
)가 이쪽으로 넘어온 것을 확인할 수 있습니다.