9.4 흔한 문제와 해결
pg_auto_failover의 FSM은 한 번에 한 단계씩 진행합니다. 따라서 문제가 생기면 대개 "어떤 노드가 목표 상태에 도달하지 못해 전체가 멈춘" 상태로 나타납니다. 이 장에서는 FAQ의 진단 순서를 먼저 정리합니다. 이어서 이 노트의 실습에서 실제로 발생한 문제 8건을 증상과 조치로 정리합니다.
진단 순서
FAQ는 두 조건을 먼저 확인하라고 안내합니다. monitor의 health check가 성공해야 합니다. 또한 모든 노드에서 pg_autoctl run이 실행 중이어야 합니다. monitor는 현재 목표 상태에 도달했다는 보고를 받기 전까지 다음 목표 상태를 주지 않습니다. 한 노드의 keeper가 멈추면 다른 노드의 전이도 대기합니다.
pg_autoctl show state # Connection 열의 '!'는 health check 실패
pg_autoctl show events --count 20
pg_autoctl status # 로컬 pg_autoctl 서비스 상태
실습에서 확인한 문제 8건
아래는 이 노트의 실습 로그에서 확인한 문제입니다. 컨테이너 환경에서 두드러지지만, 1번부터 5번은 VM에서도 같은 원인으로 발생합니다.
1. monitor에 PGDATABASE를 주면 기동 실패
compose의 공통 환경 변수인 PGDATABASE=appdb를 monitor에도 지정했더니 monitor listener가 재시작을 반복했습니다.
ERROR Connection to database failed: ... FATAL: database "appdb" does not exist
ERROR Failed to create user "autoctl" on local postgres server
ERROR pg_autoctl service listener exited with exit status 12
monitor 프로세스는 PGDATABASE에 지정된 데이터베이스로 접속합니다. monitor에는 이 변수를 지정하지 않습니다.
2. pid 파일이 남아 재시작 실패
pid 파일은 pg_autoctl show file 출력대로 /tmp/pg_autoctl/<PGDATA 경로>/pg_autoctl.pid에 있습니다. 컨테이너를 stop/start해도 /tmp는 남아 있습니다. 또한 init: true 때문에 새 pg_autoctl이 이전과 같은 pid 6을 받았습니다.
FATAL pg_autoctl is already running with pid 6
pg_autoctl은 기록된 pid의 프로세스가 실행 중이면 중복 실행으로 판단합니다. 이때 해당 프로세스를 자기 자신으로 잘못 판단했습니다. 실습에서는 /tmp를 tmpfs로 바꿔 해결했습니다. VM에서는 재부팅할 때 /tmp가 비워지는지 확인합니다.
3. HBA에 IP가 기록되어 IP 변경 후 복제 거부
역방향 DNS가 없어 keeper가 HBA에 호스트명 대신 IP를 기록했습니다. 컨테이너를 다시 만들어 standby IP가 바뀌자 primary가 복제 연결을 거부했습니다. standby는 5분 넘게 secondary/catchingup에 머물렀습니다.
WARN Using IP address "172.18.0.3" in HBA file instead of hostname "node1"
ERROR ... FATAL: no pg_hba.conf entry for replication connection from host "172.18.0.6", user "p...
노드 IP를 고정하거나 역방향 DNS를 올바르게 설정합니다. IP가 바뀌었다면 primary의 pg_hba.conf를 수정하고 reload합니다(8.3).
4. pg_rewind가 HBA에 차단되어 전체 basebackup
--dbname appdb로 만든 노드에는 replicator용 appdb 규칙만 생성됩니다. pg_rewind는 dbname=postgres로 접속하므로 연결이 거부됩니다. 그러면 keeper는 pg_basebackup으로 전체 데이터를 복사합니다. 실습 데이터는 30 MB라 2초 만에 끝났지만, 큰 DB에서는 재합류가 크게 늦어집니다. postgres DB에 대한 replicator 규칙을 추가합니다(6.5).
5. standby 합류가 checkpoint 대기로 95초
pg_basebackup이 spread checkpoint를 기다려 첫 standby 합류에 97.8초가 걸렸습니다. primary에서 CHECKPOINT를 먼저 실행하자 3.5초로 줄었습니다(1.4). 수동 CHECKPOINT는 dirty buffer를 한꺼번에 flush하고 직후 WAL을 늘립니다. 따라서 운영 primary에서는 I/O 여유를 확인한 뒤 실행해야 합니다. 또는 spread checkpoint 대기를 허용합니다.
6. set 명령이 --pgdata를 무시
노드 컨테이너에 PG_AUTOCTL_MONITOR가 설정되어 있으면 --monitor가 주어진 것으로 처리됩니다. 따라서 --pgdata는 무시됩니다.
WARN Given --monitor URI, the --pgdata option is ignored
FATAL Please use either --name or --pgdata to target a specific node
pg_autoctl set node candidate-priority --name node3 0처럼 --name을 지정합니다. monitor 컨테이너에서 set formation number-sync-standbys를 실행할 때는 --monitor URI가 필요했습니다. 이 값이 없으면 value 2 is not valid. Expected a non-negative integer value.처럼 원인과 무관한 메시지가 나왔습니다.
7. 알림 대기 중 get_nodes ERROR
perform switchover, set formation 등이 알림을 기다리는 동안 get_nodes($1, $2)에 잘못된 group 인자가 전달되어 ERROR가 기록됐습니다. 인자 값은 로그마다 달랐습니다. 인자에는 'about state changes in f'나 깨진 바이트가 포함됐습니다. 이 실습에서는 명령 결과에 영향이 없었습니다. 2.2-4.pgdg13+1에서 관찰했습니다. 원인은 intToString() 임시 값의 수명이 끝난 포인터를 SQL 인자로 전달하는 결함입니다. 같은 패턴이 perform_failover와 설정 변경 인자에도 있습니다. set formation은 get_nodes()가 실패하면 적용 완료 대기를 건너뜁니다. main의 커밋 5e91db1(#1133)에서 수정됐습니다. 무시해도 된다고 단정하지 않습니다. 명령을 실행한 뒤 show state와 show settings로 결과를 다시 확인합니다.
8. monitor가 멈춘 동안 호스트명이 외부 DNS로 해석
monitor 컨테이너를 멈추자 노드의 keeper 로그에 예상하지 못한 주소가 기록됐습니다. monitor 호스트명이 Docker 내장 DNS가 아닌 외부 DNS의 응답으로 해석된 주소였습니다. 이름이 사라지면 resolver가 상위 DNS에 질의하기 때문입니다. monitor 호스트명은 상위 DNS에 같은 이름이 없도록 정하거나 FQDN을 사용합니다.
2번과 7번은 이 노트의 환경에서 관찰한 현상입니다. 원인은 로그와 소스를 바탕으로 추정했습니다. 다른 배포판 패키지나 main 브랜치에서는 다를 수 있습니다.
풀 수 없는 상태가 되면
FAQ는 하드 크래시 뒤 FSM이 진행하지 못하는 경우가 주로 버그 때문이라고 설명합니다. 이때 마지막 수단은 pg_autoctl drop node로 노드를 monitor에서 삭제해 group을 단순화하는 것입니다. 노드가 하나만 남으면 해당 노드는 single이 됩니다. 남은 노드의 candidate priority가 0이면 single이 되지 못하고 report_lsn에 머뭅니다. monitor가 정상화되면 pg_autoctl create postgres로 노드를 다시 등록합니다. 실행 중인 PostgreSQL을 감지한 뒤 작업을 이어서 진행합니다. 경우에 따라 로컬 state 파일을 지워야 합니다. 이때 오류 메시지가 필요한 조치를 안내합니다.
# 사라진 노드를 monitor에서 제거
pg_autoctl drop node --formation default --name node3 --force
v2.2 이후 main 브랜치에는 두 가지 수정이 반영됐습니다. 하나는 self-fenced primary가 도달할 수 없는 목표 상태에서 영원히 멈추는 문제(#1158)입니다. 다른 하나는 switchover 실패 후 복구할 수 없는 timeline 상태(#1161)입니다. v2.2에서 설명하기 어려운 고착이 발생하면 이 이슈들을 먼저 확인합니다(10.1).
정리
고착의 원인은 대개 health check 실패와 keeper 중단 중 하나입니다. 먼저 show state의 ! 표시와 keeper 로그의 Failed to transition을 확인합니다. 실습에서 확인한 문제는 대부분 HBA와 이름 해석, 컨테이너 파일 시스템에서 발생했습니다. 풀 수 없는 상태에서는 drop node로 group을 줄인 뒤 다시 등록합니다.