Keepalived + VRRP — VIP 페일오버와 Nginx HA 이중화
Keepalived는 Linux 환경에서 고가용성을 구현하기 위한 경량 도구입니다. VRRP(Virtual Router Redundancy Protocol) 프로토콜을 기반으로 여러 서버가 하나의 가상 IP(VIP)를 공유하고, Master 서버 장애 시 자동으로 Backup 서버가 VIP를 인수합니다. Nginx와 함께 사용하면 웹 서버 레이어의 SPOF를 완전히 제거할 수 있습니다.
Keepalived와 VRRP 프로토콜
VRRP(Virtual Router Redundancy Protocol)는 원래 라우터 이중화를 위해 설계된 네트워크 프로토콜(RFC 3768)입니다. Keepalived는 이 프로토콜을 서버 이중화에 응용합니다.
동작 원리
┌─────────────────────────────────────────────────────────────────┐
│ VRRP 그룹 (VRID: 51) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ MASTER 서버 │ │ BACKUP 서버 │ │
│ │ 192.168.1.10 │ │ 192.168.1.11 │ │
│ │ Priority: 100 │ │ Priority: 90 │ │
│ │ (VIP 보유) │ │ (대기 중) │ │
│ └──────────┬───────────┘ └──────────────────────┘ │
│ │ VRRP Advertisement (멀티캐스트, 1초 간격) │
│ └────────────────────────────────────────── │
│ │
│ VIP: 192.168.1.100 ← 클라이언트가 접속하는 단일 IP │
└─────────────────────────────────────────────────────────────────┘
MASTER 서버: 우선순위(Priority)가 높은 서버가 Master가 됩니다. VIP를 자신의 NIC에 바인딩하고 1초마다 VRRP Advertisement 패킷을 멀티캐스트로 전송합니다.
BACKUP 서버: Advertisement 패킷을 수신하며 Master를 모니터링합니다. Master로부터 패킷이 오지 않으면(dead interval = 3초) 장애로 판단하고 VIP를 인수합니다.
페일오버 시간: 기본 설정으로 약 3~4초 내에 페일오버가 완료됩니다.
설치
Ubuntu/Debian
sudo apt update
sudo apt install -y keepalived
# 서비스 활성화
sudo systemctl enable keepalived
sudo systemctl start keepalived
CentOS/RHEL
sudo yum install -y keepalived
# 또는 RHEL 8+
sudo dnf install -y keepalived
sudo systemctl enable keepalived
sudo systemctl start keepalived
커널 파라미터 설정
VIP가 바인딩될 수 있도록 non-local IP 바인딩을 허용합니다.
# /etc/sysctl.conf에 추가
echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Nginx 헬스체크 스크립트
Keepalived는 스크립트를 주기적으로 실행해 로컬 서비스 상태를 확인합니다. Nginx가 정상이면 Priority를 유지하고, 비정상이면 Priority를 낮춰 Backup 서버가 Master가 되도록 유도합니다.
# /etc/keepalived/check_nginx.sh
#!/bin/bash
# Nginx 프로세스 확인
if ! pgrep -x "nginx" > /dev/null; then
echo "Nginx process not found, attempting restart..."
systemctl restart nginx
sleep 2
# 재시작 후 재확인
if ! pgrep -x "nginx" > /dev/null; then
echo "Nginx restart failed"
exit 1
fi
fi
# HTTP 응답 확인 (실제 요청 테스트)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://127.0.0.1/health)
if [ "$HTTP_CODE" != "200" ]; then
echo "Nginx health check failed: HTTP $HTTP_CODE"
exit 1
fi
echo "Nginx is healthy"
exit 0
# 스크립트 실행 권한 부여
sudo chmod +x /etc/keepalived/check_nginx.sh
MASTER 서버 설정
# /etc/keepalived/keepalived.conf (MASTER 서버)
global_defs {
# 이 서버의 고유 ID
router_id NGINX_MASTER
# VRRP 스크립트 실행 사용자
script_user root
enable_script_security
}
# 헬스체크 스크립트 정의
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2 # 2초마다 실행
weight -20 # 실패 시 Priority를 20 감소
fall 2 # 2회 연속 실패 시 적용
rise 2 # 2회 연속 성공 시 복구
timeout 5 # 스크립트 타임아웃
}
vrrp_instance VI_1 {
state MASTER # 초기 상태: MASTER
interface eth0 # VRRP가 동작할 네트워크 인터페이스
virtual_router_id 51 # VRRP 그룹 ID (0~255, 같은 그룹끼리 동일값)
priority 100 # 우선순위 (높을수록 Master가 됨)
advert_int 1 # Advertisement 전송 간격 (초)
preempt # Master 복구 시 다시 Master 권한 탈환
# VRRP 인증 (같은 그룹의 모든 서버 동일)
authentication {
auth_type PASS
auth_pass secret123 # 8자 이내 패스워드
}
# 가상 IP 주소
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:1
}
# 헬스체크 스크립트 연결
track_script {
check_nginx
}
}
BACKUP 서버 설정
# /etc/keepalived/keepalived.conf (BACKUP 서버)
global_defs {
router_id NGINX_BACKUP
script_user root
enable_script_security
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
fall 2
rise 2
timeout 5
}
vrrp_instance VI_1 {
state BACKUP # 초기 상태: BACKUP
interface eth0
virtual_router_id 51 # MASTER와 동일한 VRID
priority 90 # MASTER보다 낮은 우선순위
advert_int 1
nopreempt # BACKUP이 Master가 된 후 원래 MASTER 복구 시 자동 전환 안 함
authentication {
auth_type PASS
auth_pass secret123
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:1
}
track_script {
check_nginx
}
}
Notify 스크립트 (페일오버 알림)
페일오버 발생 시 Slack, 이메일 등으로 알림을 보낼 수 있습니다.
# /etc/keepalived/notify.sh
#!/bin/bash
TYPE=$1 # GROUP 또는 INSTANCE
NAME=$2 # 인스턴스명
STATE=$3 # MASTER, BACKUP, FAULT
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
case $STATE in
MASTER)
MESSAGE="[HA] :crown: $HOSTNAME 이 MASTER로 승격되었습니다. ($TIMESTAMP)"
;;
BACKUP)
MESSAGE="[HA] :shield: $HOSTNAME 이 BACKUP 상태로 전환되었습니다. ($TIMESTAMP)"
;;
FAULT)
MESSAGE="[HA] :rotating_light: $HOSTNAME 에서 FAULT 발생! 즉시 확인 필요. ($TIMESTAMP)"
;;
esac
# Slack 알림 전송
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-type: application/json" \
-d "{\"text\": \"$MESSAGE\"}"
# 로그 기록
echo "$TIMESTAMP [KEEPALIVED] $HOSTNAME -> $STATE" >> /var/log/keepalived-notify.log
# keepalived.conf의 vrrp_instance 블록에 추가
vrrp_instance VI_1 {
# ... 기존 설정 ...
notify /etc/keepalived/notify.sh
}
페일오버 테스트
시나리오 1: Nginx 프로세스 종료
# MASTER 서버에서
sudo systemctl stop nginx
# 확인: VIP가 BACKUP 서버로 이동했는지 확인
# BACKUP 서버에서
ip addr show eth0 | grep "192.168.1.100"
# eth0:1: 192.168.1.100/24 가 표시되면 페일오버 성공
# Keepalived 로그 확인
sudo tail -f /var/log/syslog | grep keepalived
# 또는
sudo journalctl -u keepalived -f
시나리오 2: 서버 전체 다운
# MASTER 서버에서 (주의: 실제 장애 시뮬레이션)
sudo systemctl stop keepalived
# BACKUP 서버에서 VIP 확인
ip addr show | grep "192.168.1.100"
시나리오 3: 연속 요청 중 페일오버 확인
# 클라이언트에서 연속 요청
while true; do
RESPONSE=$(curl -s -w "\n%{http_code}" http://192.168.1.100/health)
echo "$(date): $RESPONSE"
sleep 0.5
done
Keepalived + HAProxy 조합 패턴
대규모 환경에서는 Keepalived로 HAProxy를 이중화하고, HAProxy가 백엔드 서버를 로드밸런싱하는 패턴을 사용합니다.
클라이언트
│
▼
VIP: 192.168.1.100 (Keepalived 관리)
┌─┴─┐
▼ ▼ (Keepalived VRRP)
HAProxy HAProxy
Master Backup
│
▼ (HAProxy 로드밸런싱)
┌──┴──┬──────┐
▼ ▼ ▼
App1 App2 App3
# HAProxy 헬스체크 스크립트
# /etc/keepalived/check_haproxy.sh
#!/bin/bash
if ! pgrep -x "haproxy" > /dev/null; then
systemctl restart haproxy
sleep 2
if ! pgrep -x "haproxy" > /dev/null; then
exit 1
fi
fi
# HAProxy stats 소켓으로 상태 확인
if ! echo "show info" | socat stdio /var/run/haproxy/admin.sock &>/dev/null; then
exit 1
fi
exit 0
트러블슈팅
Split-Brain 방지
두 서버가 동시에 Master가 되는 Split-Brain을 방지하려면 VRRP 멀티캐스트가 차단되지 않도록 방화벽을 설정합니다.
# iptables: VRRP 멀티캐스트 허용
sudo iptables -A INPUT -d 224.0.0.18 -j ACCEPT
sudo iptables -A OUTPUT -d 224.0.0.18 -j ACCEPT
# firewalld 사용 시
sudo firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
sudo firewall-cmd --reload
Keepalived 로그 확인
# 실시간 로그 모니터링
sudo journalctl -u keepalived -f
# VIP 상태 확인
ip addr show | grep "192.168.1.100"
# VRRP 상태 확인 (keepalived가 출력하는 상태 파일)
cat /tmp/keepalived.data 2>/dev/null || sudo cat /run/keepalived.pid
# 상세 디버그 로깅 활성화 (/etc/keepalived/keepalived.conf)
global_defs {
log_level 7
}
일반적인 문제와 해결책
VIP가 이전되지 않는 경우:
virtual_router_id가 양쪽 서버에서 동일한지 확인auth_pass가 양쪽 서버에서 동일한지 확인- 방화벽에서 VRRP 프로토콜(IP 프로토콜 번호 112) 허용 확인
두 서버 모두 MASTER가 되는 경우:
- 서버 간 네트워크 연결 확인
- VRRP 멀티캐스트(224.0.0.18) 차단 여부 확인
- 동일 네트워크 세그먼트에 있는지 확인
전문가 팁
nopreempt옵션을 BACKUP 서버에 적용하면 원래 MASTER가 복구되어도 즉시 전환하지 않아 안정적입니다.advert_int를 줄이면 빠른 감지가 가능하지만 네트워크 부하가 증가합니다. 1~2초가 적절합니다.- 클라우드 환경(AWS, GCP)에서는 VRRP 멀티캐스트가 지원되지 않으므로 각 클라우드의 고유 HA 솔루션을 사용하세요.
- 페일오버 테스트를 정기적으로 수행해 실제 장애 시 동작을 검증하세요.
weight -20설정 시 MASTER Priority(100) - 20 = 80이 BACKUP Priority(90)보다 낮아져 Backup이 Master가 됩니다. 이 수치를 정확히 계산하세요.