VIP 는 붙어 있는데 그 뒤의 서비스가 죽었을 때 전환이 안 되거나, 전환은 됐는데 클라이언트가 한참 동안 옛 노드로 계속 붙는다. 앞의 것은 health check 설정 문제이고, 뒤의 것은 ARP 문제다. 둘은 원인이 다르므로 따로 본다.
MASTER 는 advert_int 주기로 VRRP 광고를 보낸다. BACKUP 은 광고가 세 주기 + skew time 동안 오지 않으면 자신이 MASTER 가 된다. 즉 advert_int 1 이면 장애 감지에 3초 남짓 걸린다.
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
}
더 빨리 넘기려면 값을 줄인다. VRRPv2 는 advert_int 가 초 단위 정수라 1 미만을 쓰려면 VRRPv3(version 3)여야 한다.
값을 무작정 줄이면 네트워크가 잠깐 흔들릴 때마다 전환이 일어난다. 스위치 STP 재수렴이나 짧은 패킷 유실로 광고 한두 개가 빠지는 일은 흔하므로, 대부분의 온프레미스 환경에서는 advert_int 1 을 그대로 두고 아래의 health check 쪽을 손보는 편이 안정적이다.
virtual_router_id 는 같은 브로드캐스트 도메인 안에서 VRRP 그룹마다 달라야 한다. 다른 시스템이 같은 ID 를 쓰고 있으면 서로의 광고를 자기 것으로 받아 상태가 요동친다.
VRRP 는 기본적으로 노드가 살아 있는지만 본다. 그 위의 서비스(PostgreSQL, HAProxy 등)가 죽었을 때 넘기려면 vrrp_script 로 우선순위를 깎는다.
vrrp_script chk_pgsql {
script "/etc/keepalived/check_pgsql.sh"
interval 2
timeout 2
weight -20
fall 2
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
virtual_ipaddress {
192.168.10.100/24
}
track_script {
chk_pgsql
}
}
점검 스크립트는 종료 코드만 본다. 0 이면 정상, 그 외는 실패다.
#!/bin/bash
# /etc/keepalived/check_pgsql.sh
pg_isready -h 127.0.0.1 -p 5432 >/dev/null 2>&1 || exit 1
exit 0
weight 가 상대 우선순위보다 작다. 이게 가장 흔하다. MASTER 우선순위 100, BACKUP 90 인데 weight -10 을 주면 실패 시 100-10 = 90 이 되어 동률이다. 동률에서는 현재 MASTER 가 자리를 지키므로 전환되지 않는다. 반드시 상대보다 낮아지도록 차이보다 큰 값을 준다.
스크립트가 실행되지 않는다. enable_script_security 가 켜져 있으면(최근 배포판 기본 설정에 들어 있는 경우가 많다) keepalived 는 root 가 아닌 사용자로 스크립트를 돌리려 하고, 스크립트나 그 상위 디렉터리를 비root 가 쓸 수 있으면 실행을 거부한다. 로그에 Unsafe permissions found for script 로 남는다.
chown root:root /etc/keepalived/check_pgsql.sh
chmod 700 /etc/keepalived/check_pgsql.sh
비root 로 돌려야 하면 실행 계정을 명시한다.
global_defs {
enable_script_security
script_user keepalived_script
}
타임아웃이 없다. timeout 을 주지 않으면 DB 가 응답하지 않고 매달려 있을 때 스크립트도 같이 매달려 판정이 나지 않는다. interval 이하로 잡는다.
로그를 먼저 켠다. 판정 결과와 상태 전환이 모두 로그에 남는다.
global_defs {
router_id NODE1
log_detail
}
journalctl -u keepalived -f
VRRP_Script(chk_pgsql) failed · Changing effective priority from 100 to 80 · Entering MASTER STATE 순으로 찍히면 설정이 맞는 것이다.
새 MASTER 는 VIP 를 올린 직후 gratuitous ARP 를 뿌려 "이 IP 는 이제 내 MAC" 이라고 알린다. 이 패킷이 유실되거나 중간 장비의 ARP 캐시가 길면 클라이언트는 옛 MAC 으로 계속 보낸다.
같은 대역의 다른 장비에서 확인한다.
ip neigh show | grep 192.168.10.100
MAC 이 옛 노드 것이면 ARP 가 갱신되지 않은 것이다. 전환 시점에 실제로 패킷이 나갔는지 본다.
tcpdump -i eth0 -n arp and host 192.168.10.100
재전송 횟수와 간격을 늘려 유실에 대비한다.
vrrp_instance VI_1 {
garp_master_delay 1
garp_master_repeat 5
garp_master_refresh 60
}
garp_master_refresh 는 MASTER 상태를 유지하는 동안에도 주기적으로 다시 뿌린다. ARP 캐시를 오래 붙드는 장비가 중간에 있을 때 효과가 있다.
노드 자체의 ARP 응답 정책도 본다. 한 인터페이스에 여러 IP 가 올라간 구성에서 arp_ignore · arp_announce 가 기본값이면 엉뚱한 소스 IP 로 응답이 나갈 수 있다.
VIP 전환은 다른 수단(DNS, 외부 LB, Patroni 같은 클러스터 관리자)이 맡고 keepalived 로는 감시와 알림만 하고 싶은 경우가 있다. vrrp_script 는 vrrp_instance 의 track_script 로 묶여야 동작하므로 인스턴스 자체를 없앨 수는 없다. VIP 목록을 비우고 notify_* 만 쓰면 IP 를 건드리지 않고 상태 전환만 얻는다.
vrrp_instance monitor_only {
state BACKUP
interface eth0
virtual_router_id 90
priority 100
advert_int 1
track_script {
chk_pgsql
}
notify_fault "/etc/keepalived/on_fault.sh"
notify_master "/etc/keepalived/on_master.sh"
}
다만 이 용도라면 keepalived 보다 systemd 타이머나 모니터링 에이전트 쪽이 단순하다. VRRP 를 쓰지 않을 것이라면 keepalived 를 쓸 이유도 대체로 없다.