본문으로 건너뛰기
11.3 흔한 장애 시나리오

11.3 흔한 장애 시나리오

Patroni 클러스터에서 반복적으로 마주치는 장애는 유형이 정해져 있다. 이 챕터는 다섯 가지 시나리오를 놓고, 각 상황에서 Patroni 가 어떻게 반응하도록 설계되어 있는지와 DBA 가 어디부터 확인해야 하는지를 정리한다.

시나리오 A: DCS 장애

etcd 클러스터가 quorum 을 잃거나 네트워크가 끊겨 DCS 에 닿지 못하면, primary 는 leader lock 을 갱신하지 못한다. 이때의 기본 동작을 공식 문서는 “leader lock 갱신에 실패하면 PostgreSQL 은 즉시 demote 되어 read-only 로 시작된다"고 기술한다. DCS 가 안 보이는 상황에서는 다른 노드가 승격했는지 알 길이 없으므로, 이중 primary 를 막는 쪽으로 보수적으로 움직이는 것이다. 결과적으로 DCS 전체 장애는 클러스터 전체의 쓰기 중단으로 번진다.

    flowchart TD
  A["leader lock 갱신 실패"] --> B{"failsafe_mode?"}
  B -->|꺼짐| C["즉시 demote"]
  C --> D["read-only 로 재시작"]
  B -->|켜짐| E["전 멤버에<br/>POST /failsafe 요청"]
  E --> F{"전원 응답?"}
  F -->|예| G["primary 유지"]
  F -->|아니오| C
  

이 강등을 피하는 장치가 failsafe_mode(기본 false)다. DCS 는 죽었지만 멤버끼리는 통신이 되는 상황에서, primary 가 모든 멤버에게 REST 요청을 보내 전원이 응답하면 primary 를 유지한다. 한 멤버라도 응답하지 않으면 demote 된다. 동작 조건과 한계는 2.5 DCS 장애와 failsafe mode에서 다뤘다.

DCS 안의 클러스터 정보만 유실된 경우는 다르다. etcd 데이터가 지워졌어도 PostgreSQL 이 살아 있으면, Patroni 는 이미 실행 중인 클러스터를 감지하고 leader race 를 거쳐 DCS 키를 다시 만든다. 이 경우는 재초기화가 아니라 키 재생성으로 끝난다.

확인 순서는 patronictl list 로 클러스터 상태를 보고, 각 노드의 GET /patroni 에서 cluster_unlockedfailsafe_mode_is_active 를 읽는 것이다. patroni_dcs_last_seen 이 정체된 시각이 곧 장애 시작점이다.

시나리오 B: replication slot 함정

slot 관련 장애는 방향이 반대인 두 함정으로 나뉜다. slot 이 사라져서 나는 장애와, slot 이 남아서 나는 장애다.

먼저 사라지는 쪽이다. 멤버가 내려가면 upstream 에 있던 그 멤버의 slot 은 member_slots_ttl(기본 30분, 4.0.0 이상) 경과 후 제거된다. slot 이 사라진 뒤에는 그 멤버가 필요로 하던 WAL 이 재활용되므로, 장기간 다운됐던 멤버는 재기동해도 복제를 이어가지 못하고 patronictl reinit 로 데이터 디렉토리를 다시 만들어야 한다. 계획된 장기 정지가 예상되면 member_slots_ttl 을 늘리거나, 해당 멤버의 slot 을 permanent slot 으로 선언해 두는 방법이 있다(3.2.0 이상). standby cluster 의 원본 쪽 slot 은 Patroni 가 자동 관리하지 않으므로 처음부터 수동 생성 또는 permanent slot 대상이다. slot 관리 체계는 5.4 replication slot 관리에서 다뤘다.

반대로 남아서 나는 장애가 있다. permanent slot 은 failover 를 넘어 유지되는 대신, 소비자(백업 도구, CDC, standby cluster)가 죽어도 사라지지 않는다. 소비가 멈춘 slot 은 restart_lsn 이 정체된 채 primary 의 pg_wal 을 계속 잡아 두고, 방치하면 디스크 풀(시나리오 D)로 이어진다. 이 부분은 Patroni 가 아니라 PostgreSQL 일반의 대책이 필요한 영역으로, max_slot_wal_keep_size 로 보존 상한을 두고 slot 별 누적량을 모니터링하는 것이 통상의 접근이다.

SELECT slot_name, slot_type, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
max_slot_wal_keep_size 는 디스크를 지키는 대신 상한을 넘긴 slot 을 무효화한다. 그 slot 의 소비자가 standby cluster 나 CDC 파이프라인이라면 해당 소비자 쪽에서 재구축이 필요해진다. 상한 설정과 slot 모니터링은 한 쌍으로 운영해야 한다.

시나리오 C: WAL archive 실패

archive_command 실패는 Patroni 관점에서는 장애가 아니다. Patroni 가 감시하는 것은 leader lock 과 replication 상태이고, archiving 은 PostgreSQL 설정의 영역이라 공식 문서에도 archive 실패에 대한 Patroni 의 대응은 기술되어 있지 않다. failover 도 경보도 일어나지 않은 채 조용히 진행되는 장애라는 점이 위험하다.

영향은 세 갈래다. 실패가 지속되면 archive 대기 WAL 이 pg_wal 에 쌓여 디스크 고갈(시나리오 D)로 이어지고, restore_command 로 WAL 을 받아오는 노드(standby cluster, nostream tag 노드)의 복구가 멈추며, 백업 기반 PITR 이 그 시점 이후로 불가능해진다.

식별은 PostgreSQL 쪽 지표가 기본이다. pg_stat_archiver 의 실패 카운트와 마지막 실패 시각을 본다. Patroni 쪽에서는 patronictl list 의 State 컬럼이 단서가 되는데, streaming 으로 돌아야 할 노드가 in archive recovery 상태에 오래 머물면 archive 경로에 의존해 돌고 있다는 뜻이므로 복제와 archive 양쪽을 함께 점검한다. 같은 정보가 메트릭으로는 patroni_postgres_in_archive_recovery 로 노출된다.

시나리오 D: 디스크 풀

디스크 풀 상황의 동작을 Patroni 공식 문서가 별도로 기술하지는 않는다. 일반적으로 PostgreSQL 은 WAL 을 쓸 공간이 없으면 PANIC 으로 정지하고, Patroni 는 정지한 PostgreSQL 의 재시작을 시도한다. 문제는 공간이 없다는 원인이 그대로이니 재시작도 실패를 반복하고, 그 사이 leader lock 이 만료되면 failover 가 일어난다는 점이다. WAL 누적이 원인이라면 replica 쪽 디스크도 같은 경로로 차오르고 있을 가능성이 높아, failover 가 해결이 아니라 지연에 그칠 소지가 있다.

이럴 때 통상의 접근은 pause로 자동 관리를 먼저 멈추는 것이다. pause 는 major upgrade 나 corruption recovery 같은 유지보수를 위해 만들어진 모드이고, 재시작 루프와 원치 않는 failover 를 막은 상태에서 공간을 확보하는 이 상황도 그 용례에 준한다. 공간 확보 후 원인을 짚을 때는 앞의 두 시나리오와 겹치는 항목부터 본다.

patronictl pause my-cluster --wait
# 공간 확보와 원인 제거 후
patronictl resume my-cluster --wait
  • pg_replication_slots 에서 restart_lsn 이 정체된 slot (시나리오 B)
  • pg_stat_archiver 의 archive 실패 누적 (시나리오 C)
  • log.dir 등 로그와 임시 파일의 로테이션 설정

시나리오 E: 이중 primary

pause 중에는 Patroni 가 이중 primary 를 해소하지 않는다. 공식 문서 기준으로, paused 상태에서 병렬로 뜬 primary 를 감지하면 경고 로그를 남길 뿐 demote 는 하지 않는다. pause 중에 수동으로 promote 했거나, 관리 밖에서 PostgreSQL 을 직접 조작한 경우 이 상태가 만들어진다. 평상시의 이중 primary 방지 체계는 2.4 split brain 방지와 watchdog에서 다뤘다.

이중 primary 를 남긴 채 patronictl resume 을 실행하면 안 된다. resume 전에 primary 가 하나뿐인 상태로 수동 정리를 마쳐야 한다. 두 primary 가 각자 쓰기를 받았다면 timeline 이 갈라진 것이므로, 한쪽을 내리는 것으로 끝나지 않고 갈라진 뒤의 데이터를 어떻게 합칠지도 판단해야 한다.

정리 절차를 문서가 규정하지는 않지만, 판단 재료는 분명하다. 양쪽의 timeline 과 LSN, 그리고 애플리케이션 트래픽이 실제로 어느 쪽에 붙어 있었는지를 확인해 정본을 정한다. 정본이 아닌 쪽은 PostgreSQL 을 정지하고, leader lock 이 정본 쪽에 있는지 확인한 뒤 resume 한다. 갈라진 노드는 patronictl reinit 로 재구축해 복귀시킨다.

정리

  • DCS 장애의 기본 반응은 demote 후 read-only 다. 쓰기 중단을 감수하고 이중 primary 를 막는 설계이며, 완화 장치는 failsafe_mode 다.
  • slot 은 사라져도(장기 다운 멤버의 reinit) 남아도(pg_wal 잠식) 장애가 된다. member_slots_ttl, permanent slot, max_slot_wal_keep_size 와 slot 모니터링을 짝으로 쓴다.
  • archive 실패와 디스크 풀은 Patroni 가 대신 감지해 주지 않는 영역이다. PostgreSQL 지표로 조기에 잡고, 수습 중 자동 동작이 방해되면 pause 를 쓴다.