PostgreSQL 장애 조치(Failover) vs. 전환(Switchover) 시나리오 이해 및 실행
PostgreSQL 전환 또는 장애 조치를 사용해야 하는 시기, 복제본 안전성 확인 방법, HA 이벤트 중 분할-뇌(Split-Brain)를 방지하는 방법을 알아보세요.
PostgreSQL 장애 조치(Failover) vs. 전환(Switchover) 시나리오 이해 및 실행
PostgreSQL 장애 조치와 전환의 차이는 당신이 페이지를 들고 있는 사람일 때 학문적이지 않습니다. 전환은 계획된 것입니다: 여전히 건강한 프라이머리가 있고, 쓰기를 드레인할 수 있으며, 쓰기 가능한 역할을 대기 서버로 이동할 최적의 순간을 선택할 수 있습니다. 장애 조치는 프라이머리가 사라졌거나, 연결할 수 없거나, 트래픽을 계속 제공하기에 안전하지 않을 때 수행하는 작업입니다.
그 한 가지 차이가 모든 것을 바꿉니다. 전환 중에는 인내가 주요 작업입니다: 승격 전에 대기 서버가 따라잡았는지 증명하세요. 장애 조치 중에는 차단이 주요 작업입니다: 대기 서버가 승격된 후에도 이전 프라이머리가 쓰기를 계속 수락할 수 없도록 하세요. 대부분의 추한 PostgreSQL HA 사고는 이 두 작업 중 하나를 서두르는 데서 발생합니다.
복제 기본 사항: HA의 기초
PostgreSQL 고가용성은 스트리밍 복제를 기반으로 구축되며, 여기서 하나의 서버가 프라이머리(또는 마스터) 역할을 하고 하나 이상의 서버가 대기 서버(또는 복제본) 역할을 합니다. 프라이머리는 쓰기-앞선 로그(WAL) 레코드를 대기 서버로 스트리밍하여 동기화 상태를 유지합니다.
이러한 역할을 효과적으로 관리하려면 프라이머리 및 복제본 노드 모두에 특정 구성 설정이 필요합니다:
중요한 구성 설정
이러한 설정은 복제가 작동하는 방식과 노드가 서로를 식별하는 방식을 제어합니다:
wal_level: 프라이머리에서replica이상으로 설정해야 합니다(논리적 디코딩이 필요한 도구를 사용하는 경우 이상적으로logical).max_wal_senders: 동시 WAL 발신자 연결의 최대 수를 정의합니다. 모든 물리적 대기 서버, 기본 백업 및 동시에 연결될 수 있는 복제 도구에 맞게 크기를 조정하세요.hot_standby: 복제 중 읽기 전용 쿼리를 허용하려면 대기 서버의postgresql.conf에서on으로 설정해야 합니다.synchronous_commit: 트랜잭션이 승인되는 시점을 제어합니다. 동기 복제가 올바르게 구성된 경우에만 더 강력한 내구성을 제공합니다. 자체적으로는 대기 서버를 최신 상태로 만들지 않습니다.primary_conninfo: 대기 서버에 설정되며, 현재 프라이머리에 연결하기 위한 연결 정보(호스트, 포트, 사용자, 비밀번호)를 자세히 설명합니다.
모범 사례: HAProxy, 가상 IP 뒤의 PgBouncer, 서비스 검색 레코드 또는 플랫폼의 서비스 추상화와 같은 안정적인 엔드포인트를 PostgreSQL 앞에 두세요. 애플리케이션은 오늘 어떤 노드가 프라이머리인지 알 필요가 없어야 합니다.
전환: 계획된 전환
전환은 제어되고 우아한 프로세스로, 활성 프라이머리 노드가 의도적으로 폐기되고 지정된 대기 서버가 승격되어 그 자리를 대체합니다. 이 절차는 일반적으로 계획된 유지 관리, 버전 업그레이드 또는 하드웨어 교체에 사용됩니다.
제어된 전환을 위한 단계
전환의 목표는 승격 전에 모든 진행 중인 트랜잭션이 복제될 때까지 기다려 데이터 손실 제로를 보장하는 것입니다.
- 현재 프라이머리에 쓰기 중지: 첫 번째 단계는 현재 프라이머리에서 새 트랜잭션이 커밋되는 것을 방지하는 것입니다. 이는 종종
default_transaction_read_only = on을 설정하거나 클라이언트 연결을 일시적으로 종료하여 달성됩니다. - 복제 따라잡기 대기: 지정된 대기 서버가 프라이머리에서 남은 모든 WAL 레코드를 수신하고 적용했는지 확인하세요. 프라이머리에서
pg_stat_replication을 사용하거나 대기 서버의 복구 상태를 검사하여 복제 지연을 확인할 수 있습니다. - 대기 서버 승격 시작: 선택한 대기 서버를 프라이머리 역할로 승격하는 명령을 실행하세요. 특정 명령은 사용된 관리 도구에 따라 다릅니다(예:
pg_ctl promote또는 클러스터 관리자 명령). - 이전 프라이머리 재구성: 대기 서버가 성공적으로 승격되면 이전 프라이머리는 새 프라이머리를 대기 서버로 따르도록 재구성되어야 합니다. 여기에는
primary_conninfo업데이트가 포함됩니다. - 애플리케이션 리디렉션: 로드 밸런서 또는 연결 풀러를 업데이트하여 트래픽을 새 프라이머리 서버로 전달하세요.
실용적인 전환 체크리스트는 일반적으로 극적이기보다는 평범해 보입니다. 짧은 쓰기 일시 중지를 발표하고, 계속 쓰는 백그라운드 작업자를 중지하고, 애플리케이션을 유지 관리 모드로 전환하거나 작성자 풀을 드레인한 다음 복제 위치를 확인하세요. 이전 프라이머리에서 pg_stat_replication은 대기 서버가 WAL을 수신하고 플러시했는지 여부를 보여줍니다. 대기 서버에서 pg_last_wal_receive_lsn() 및 pg_last_wal_replay_lsn()은 WAL이 단지 도착했는지 아니면 실제로 재생되었는지 확인하는 데 도움이 됩니다.
연결되어 있다고 해서 대기 서버를 승격하지 마세요. 대기 서버는 연결되어 있지만 큰 트랜잭션을 재생 중이거나, 디스크 I/O를 기다리거나, 네트워크 일시 중지 후 복구 중인 경우 몇 초 또는 몇 분 뒤쳐질 수 있습니다. 계획된 전환의 경우 승격 전에 재생이 따라잡히기를 원합니다. 대기 서버에서 읽기 전용 세션이 실행 중인 경우 장기 실행 쿼리가 WAL 재생을 지연시키는지도 확인하세요.
승격 후 역할을 직접 테스트하세요:
SELECT pg_is_in_recovery();
승격된 노드는 false를 반환해야 합니다. 강등된 노드는 대기 서버로 재구축되거나 재연결된 후 true를 반환해야 합니다.
애플리케이션 측도 동일한 주의가 필요합니다. 전환 전에 클라이언트가 작성자를 어떻게 발견하는지 알아두세요. DNS 이름에 연결하는 경우 DNS TTL과 클라이언트가 예상보다 오래 주소를 캐시하는지 이해하세요. PgBouncer를 통해 연결하는 경우 풀을 일시 중지, 다시 로드 또는 다시 시작해야 하는지 결정하세요. HAProxy를 사용하는 경우 상태 확인이 포트 5432가 열려 있는지 여부뿐만 아니라 쓰기 가능 상태를 테스트하는지 확인하세요. PostgreSQL이 실행 중인 대기 서버는 유효한 쓰기 대상이 아닙니다.
또한 롤백 지점을 기록해 두는 것이 좋습니다. 승격 전에는 일반적으로 중지하고, 이전 프라이머리에서 쓰기를 다시 열고, 나중에 다시 시도할 수 있습니다. 승격 후에는 롤백이 단순한 실행 취소가 아닌 새로운 역할 변경이 됩니다. 이것이 승격이 위험하다는 것을 의미하지는 않습니다. 운영자가 자신이 어느 쪽에 있는지 알아야 한다는 것을 의미합니다.
장애 조치: 비상 대응
장애 조치는 현재 프라이머리 서버가 예기치 않게 실패하고(예: 하드웨어 충돌, 네트워크 파티션, 소프트웨어 오류) 신속하게 다시 온라인 상태로 전환할 수 없을 때 트리거되는 즉각적이고 반응적인 절차입니다.
장애 조치는 본질적으로 데이터 손실 위험이 더 높습니다. 장애 발생 전에 마지막 몇 개의 커밋된 트랜잭션이 대기 서버로 스트리밍될 시간이 있었는지 보장할 수 없기 때문입니다.
비상 장애 조치 실행
장애 조치 절차는 속도와 복구를 위해 설계되었으며, 종종 승격을 자동화하기 위해 특수 도구를 활용합니다.
- 이전 프라이머리의 상태 확인: 원래 프라이머리가 실제로 사용 불가능한지, 아니면 일시적인 네트워크 문제(위험한 '분할-뇌' 시나리오 방지)를 겪고 있는지 확인하세요.
- 최상의 대기 서버 선택: 복제 지연이 가장 적은 대기 서버(WAL 스트림에서 가장 앞서 있는 대기 서버)를 선택하세요.
- 대기 서버 승격: 승격 명령(
pg_ctl promote)을 사용하여 선택한 대기 서버를 즉시 승격하세요. - 데이터 손실 처리(필요한 경우): 클러스터가 비동기 복제를 사용하는 경우, 실패한 프라이머리에서 손실된 데이터는 애플리케이션의 허용 오차에 따라 수동으로 조정되거나 단순히 수용되어야 할 수 있습니다.
- 이전 프라이머리 재구성: 원래 프라이머리가 복구되면 정리되고, 재초기화(종종 새 프라이머리의 기본 백업 필요)된 후 새 프라이머리를 따르도록 구성되어야 합니다.
장애 조치의 어려운 부분은 pg_ctl promote를 입력하는 것이 아닙니다. 어려운 부분은 이전 프라이머리가 반대가 증명될 때까지 안전하지 않은 것으로 취급되어야 한다고 결정하는 것입니다. 이전 프라이머리가 여전히 실행 중이지만 애플리케이션이나 대기 서버와 차단된 경우 분할-뇌가 발생할 수 있습니다: 서로 다른 기록을 수락하는 두 개의 쓰기 가능한 PostgreSQL 서버. 일단 그렇게 되면 PostgreSQL이 기록을 병합해 주지 않습니다. 수동 데이터 조정을 하거나 한 쪽을 백업에서 복원해야 합니다.
실제 사고에서는 다음 날 두 개의 주문 기록이 일치하지 않는 이유를 설명하는 것보다 이전 프라이머리를 차단하는 데 1분을 더 쓰는 것이 낫습니다. 차단은 이전 VM의 전원을 끄거나, 네트워크 인터페이스를 분리하거나, 작성자 엔드포인트를 비활성화하거나, 이전 호스트가 쓰기를 수신할 수 없음을 보장하는 클라우드/공급자 메커니즘을 사용하는 것을 의미할 수 있습니다. 정확한 방법은 인프라에 따라 다르지만 요구 사항은 간단합니다: 클라이언트가 새 프라이머리에 쓰기 전에 이전 프라이머리는 해당 클라이언트가 쓸 수 없어야 합니다.
장애 조치 후에는 정리를 예상하세요. 이전 프라이머리가 돌아오면, 그것을 새 프라이머리에 가리키고 따라잡기를 기대하지 마세요. 이전 타임라인에 속하는 WAL이 포함될 수 있습니다. 많은 환경에서 가장 안전한 경로는 전제 조건이 충족되면 pg_rewind를 사용하거나, 그렇지 않으면 새 프라이머리에서 새 기본 백업을 수행하는 것입니다.
비상 작업 중에 놓치는 한 가지 세부 사항은 복제 슬롯 이야기입니다. 이전 프라이머리가 대기 서버에 물리적 복제 슬롯을 사용한 경우, HA 도구가 관리하지 않는 한 해당 슬롯은 승격된 대기 서버와 함께 마술처럼 이동하지 않습니다. 장애 조치 후 새 프라이머리에 생존하는 대기 서버에 필요한 슬롯이 있는지, 그리고 버려진 슬롯이 WAL을 영원히 보유하고 있지 않은지 확인하세요. 잊혀진 슬롯은 눈에 보이는 중단이 끝난 후 몇 시간 후에 디스크를 채울 수 있습니다.
백업에도 동일한 규율을 적용하세요. 클러스터에 새 프라이머리가 생기면 백업과 WAL 아카이빙이 이제 해당 프라이머리를 따르는지 확인하세요. 서비스를 복원하지만 조용히 백업을 중단하는 장애 조치는 절반의 복구에 불과합니다.
안전한 승격을 위한 도구: Repmgr vs. Patroni
pg_ctl을 사용한 수동 승격이 가능하지만, 강력한 HA 환경은 장애 조치 및 전환에 필요한 복잡한 안무를 관리하고 구성 변경 및 클러스터 상태 관리를 자동으로 처리하는 전용 도구에 의존합니다.
Repmgr (복제 관리자)
repmgr은 노드를 등록하고, 복제를 모니터링하고, 제어된 역할 변경을 수행하는 데 도움이 되는 경량 도구입니다. 정확한 명령은 버전과 클러스터 레이아웃에 따라 다르지만 일반적인 패턴은 다음과 같습니다:
- 전환: 복제 상태를 확인한 후 프라이머리가 되어야 하는 대기 서버에서 계획된
repmgr standby switchover를 실행하세요. - 장애 조치: 차단 및 증인/쿼럼 동작이 이해되고 테스트된 경우에만
repmgrd가 자동 장애 조치를 수행하도록 하세요.
Patroni
Patroni는 분산 합의 저장소(etcd, ZooKeeper 또는 Consul 등)를 활용하여 클러스터 상태를 관리하고, 장애 감지 시 자동으로 새 프라이머리를 선출합니다. Patroni는 API 호출 또는 Kubernetes 연산자를 통해 전환과 장애 조치를 모두 대부분 자동화하여 수동 개입을 크게 줄입니다.
Patroni 사용 예(개념적 승격 명령):
# Patroni의 REST API를 통해 전환 트리거
curl -X POST http://patroni-api-endpoint/switchover -H "Content-Type: application/json" -d '{"target": "standby_node_name"}'
분할-뇌 경고: 자동 장애 조치 중 가장 큰 위험은 네트워크 분할로 인해 두 노드가 실수로 자신이 프라이머리라고 믿는 '분할-뇌' 시나리오입니다. Patroni와 같은 도구는 쿼럼 메커니즘을 사용하여 이를 완화하는 반면, 수동 설정은 하나의 프라이머리만 존재하도록 보장하기 위해 엄격한 차단 메커니즘(전원 제어 등)이 필요합니다.
차이점 요약
| 특징 | 전환(계획됨) | 장애 조치(비상) |
|---|---|---|
| 트리거 | 유지 관리, 업그레이드, 관리상 선택 | 프라이머리 실패(충돌, 중단) |
| 데이터 손실 위험 | 거의 제로(적절한 시점에) | 중간에서 높음(복제 모드에 따라 다름) |
| 다운타임 예상 | 짧고 제어된 다운타임 | 즉각적이고 반응적인 다운타임 |
| 준비 | 사전 조정 및 WAL 동기화 확인 필요 | 즉각적인 조치 및 대기 서버 상태에 의존 필요 |
적용 가능한 소규모 런북
계획된 전환의 경우 간결한 런북은 다음과 같을 수 있습니다:
- 선택한 대기 서버가 정상이고 WAL을 재생 중인지 확인하세요.
- 애플리케이션 쓰기 및 백그라운드 작업을 일시 중지하세요.
- 복제 재생이 따라잡았는지 확인하세요.
- HA 도구를 통해 대기 서버를 승격하세요.
- 작성자 엔드포인트를 이동하세요.
- 새 프라이머리에서
pg_is_in_recovery()가false인지 확인하세요. - 이전 프라이머리를 대기 서버로 재구축 또는 되감기하세요.
- 쓰기를 재개하고 오류, 복제 및 연결 수를 모니터링하세요.
장애 조치의 경우 순서가 변경됩니다:
- 프라이머리가 실패했거나 안전하지 않은지 확인하세요.
- 이전 프라이머리를 차단하세요.
- 가장 진보된 대기 서버를 선택하세요.
- HA 도구를 통해 승격하세요.
- 작성자 엔드포인트를 한 번 이동하세요.
- 새 프라이머리에서 쓰기가 작동하는지 확인하세요.
- 복제본, 슬롯, 백업 및 WAL 아카이빙을 확인하세요.
- 되감기 또는 재구축을 통해서만 이전 프라이머리를 다시 도입하세요.
명령은 도구에 따라 다르지만 안전 속성은 그렇지 않습니다. 하나의 쓰기 가능한 프라이머리, 알려진 복제 상태, 테스트된 클라이언트 라우팅, 그리고 실패한 노드를 다시 가져오는 깔끔한 방법.
HA 설계를 신뢰하기 전에 비-프로덕션 환경에서 두 경로를 모두 연습하세요. 전환 훈련은 애플리케이션이 깔끔하게 재연결되고, 이전 프라이머리가 다시 대기 서버가 될 수 있으며, 모니터링이 새 역할을 따르는 것을 증명해야 합니다. 장애 조치 훈련은 더 엄격한 것을 증명해야 합니다: 실패한 프라이머리가 차단되고, 승격을 위해 선택된 대기 서버가 사용 가능한 최상의 후보이며, 애플리케이션 작성자 엔드포인트가 한 번 이동하고, 이전 프라이머리가 되감기 또는 재구축 없이는 다시 합류할 수 없습니다.
가장 안전한 PostgreSQL HA 팀은 장애 조치를 중단 중에 입력하는 영웅적인 명령이 아니라 테스트된 운영 워크플로로 취급합니다.