2.4 split brain 방지와 watchdog
split brain은 두 노드가 동시에 자신을 primary로 믿고 쓰기를 받는 상황이다. 양쪽에 서로 다른 데이터가 쌓이기 시작하면 단순 재기동으로는 돌이키지 못한다. Patroni의 1차 방어선은 2.2의 leader lock이고, 이 절은 lock 갱신이 실패했을 때 primary가 스스로 물러나는 규칙과, 소프트웨어 보호가 통하지 않는 경우를 대비한 watchdog을 다룬다.
self-demotion: 최악을 가정하고 물러난다
primary의 Patroni가 leader lock 갱신에 실패했다고 하자. 원인은 둘 중 하나다. DCS 자체가 죽었거나, 자기 노드가 network partition으로 고립됐거나. 단일 노드의 관점에서는 이 둘을 구분할 방법이 없다. 그래서 Patroni는 최악의 경우인 network partitioning을 가정한다. 고립이 맞다면 반대편 멤버들은 leader key 만료 후 새 leader를 뽑을 것이므로, 구 primary는 leader key가 만료되기 전에 먼저 PostgreSQL을 demote하고 read-only로 다시 기동한다. 새 primary가 등장하는 시점에 쓰기를 받는 노드가 둘이 되는 일을 이 선제 강등이 막는다.
모든 흔들림에 이렇게 반응하는 것은 아니다. retry_timeout보다 짧은 DCS 장애나 네트워크 문제는 재시도로 흡수되며 leader 강등을 유발하지 않는다.
flowchart TD
T["leader lock 갱신 시도"] --> OK{"성공?"}
OK -->|"성공"| KEEP["primary 유지"]
OK -->|"실패"| RT{"retry_timeout<br/>이내 복구?"}
RT -->|"복구"| KEEP
RT -->|"초과"| DEM["즉시 demote"]
DEM --> RO["read-only로 재기동"]
watchdog: 커널이 지키는 마지막 선
self-demotion은 Patroni 프로세스가 정상 동작한다는 전제 위의 이야기다. Patroni 프로세스가 crash했거나, PostgreSQL 정지가 제때 끝나지 않거나, 시스템이 과부하로 멈칫거리면 이 소프트웨어적 보호가 작동하지 못하고, PostgreSQL은 leader key가 만료된 뒤에도 트랜잭션을 계속 받는 상태로 남는다. watchdog은 이 마지막 구멍을 커널 수준에서 막는 장치다. watchdog 장치는 정해진 시간 안에 갱신(keepalive)이 없으면 시스템을 강제로 리셋한다.
Patroni는 promote 직전에 watchdog을 활성화하고, demote한 뒤에 비활성화한다. 클러스터 관리를 멈추는 pause 상태에서도 비활성화한다. leader가 아닌 노드가 watchdog에 물려 있을 이유가 없기 때문이다.
softdog으로 시작하기
전용 하드웨어 watchdog이 없는 서버에서는 Linux 커널의 softdog 모듈을 쓴다. Patroni를 시작하기 전에 root로 실행한다.
modprobe softdog
# Patroni가 postgres 사용자로 돌 때 장치 접근을 허용
chown postgres /dev/watchdogmodprobe softdog soft_noboot=1로 모듈을 올리면 재부팅 대신 kernel ring buffer에 로그만 남아, 발화 여부를 안전하게 확인할 수 있다.mode와 safety_margin
watchdog 설정은 로컬 설정 파일(patroni.yml)의 watchdog 섹션에 둔다.
watchdog:
mode: required # off / automatic / required
device: /dev/watchdog # 기본값
safety_margin: 5 # 기본값 (초)mode는 세 값을 가진다. off는 watchdog을 쓰지 않는다. automatic은 watchdog 장치가 있으면 쓰고 없으면 무시한다. required는 가장 엄격해서, watchdog 활성화에 실패한 노드는 leader가 되기를 거부한다. split brain을 확실히 막고 싶은 환경이라면 required가 그 의도에 맞는 선택이다.
safety_margin은 watchdog 발화와 leader key 만료 사이의 여유 시간(초)이다. 기본값 5는 watchdog이 TTL 만료 5초 전에 발화하도록 구성한다는 뜻이다. 기본 설정(ttl=30, loop_wait=10)에서 HA 루프는 시스템이 강제 리셋되기 전까지 최소 15초(ttl - safety_margin - loop_wait)를 확보한다. DCS가 불능인 상황에서는 모든 클라이언트 연결이 종료된 상태에 도달하기까지 최소 5초(ttl - safety_margin - loop_wait - retry_timeout)가 남는다.
어떤 상황에서도 watchdog 발화를 보장하고 싶다면 safety_margin을 -1로 설정한다. 이 경우 watchdog timeout은 ttl // 2, 즉 TTL의 절반 시점으로 잡힌다.
ttl, loop_wait, retry_timeout을 조정할 때는 리셋 전 여유가 얼마나 남는지 산식을 다시 계산한다. ttl을 줄이거나 loop_wait를 늘리면 그만큼 여유가 깎인다.정리
- leader lock 갱신에 실패한 primary는 network partition을 최악으로 가정하고 lock 만료 전에 스스로 demote한다.
retry_timeout보다 짧은 순단은 강등 사유가 아니다.- watchdog은 Patroni 프로세스 자체가 무력화된 경우를 커널 리셋으로 막는 최후 수단이며,
required모드는 watchdog 없이는 leader가 되지 않겠다는 선언이다.