Patroni 4.1: 쿼럼 기반 동기 복제와 PostgreSQL HA 운영 기준의 재정립
왜 지금 봐야 하나
Patroni는 PostgreSQL 고가용성(HA) 구성에서 가장 널리 쓰이는 오케스트레이터다. Kubernetes 기반 Postgres 운영(Zalando Postgres Operator, CloudNative PG 이전 환경)부터 온프레미스 클러스터 관리까지 폭넓게 쓰인다.
Patroni 4.0(2024년 8월)은 두 가지 큰 변화를 가져왔다. 첫째, 쿼럼 기반 동기 복제다. 기존의 "모든 동기 노드가 확인해야 커밋" 방식에서 "M개 중 N개가 확인하면 커밋" 방식으로 바뀌어 레이턴시와 가용성 사이의 트레이드오프를 운영자가 직접 조정할 수 있게 됐다. 둘째, Kubernetes 환경 레이블 파괴적 변경이다. role: master 셀렉터를 사용하는 Services는 이 버전으로 업그레이드하는 순간 트래픽이 끊긴다.
2026년 5월과 7월에 걸쳐 Patroni 4.1.3(2026-05-05)과 4.1.4(2026-07-07)가 출시됐다. PostgreSQL 18 지원 추가, etcd 리더 소실 시 오류 처리 개선, pg_replication_slots 쿼리 통합, 그리고 클러스터 전환을 자동화하는 새 patronictl 명령이 포함됐다.
이 글은 Patroni 4.x의 운영적 의미와 3.x에서 4.x로의 안전한 전환 기준을 다룬다.
변경 사항 전체 요약
| 버전 | 날짜 | 주요 변화 |
|---|---|---|
| 4.0.0 | 2024-08-30 | 쿼럼 기반 동기 복제, master→primary 용어 변경, bootstrap.users 제거 |
| 4.1.0 | ~2025 초 | member_slots_ttl 추가, Citus 세컨더리 pg_dist_node 연동 |
| 4.1.3 | 2026-05-05 | 쿼럼 상태 머신 버그 수정, QuorumStateResolver 엣지케이스 처리 |
| 4.1.4 | 2026-07-07 | etcd 리더 소실 오류 처리, pg_replication_slots 통합, pg_rewind 개선 |
핵심 변화 1: 쿼럼 기반 동기 복제
기존 방식의 문제
Patroni 3.x에서 동기 복제를 켜면(synchronous_mode: true), Primary는 지정된 모든 동기 복제본에 WAL이 도달했다는 확인을 받은 뒤에야 커밋을 완료한다. PostgreSQL 내부적으로 synchronous_standby_names = 'standby1,standby2'처럼 설정된다.
문제는 동기 복제본 중 하나가 네트워크 지연을 겪거나 응답이 늦으면, 가장 느린 복제본의 속도에 Primary 쓰기가 종속된다는 것이다. 3개 복제본 중 하나가 느리면 나머지 두 개가 빠르더라도 커밋이 지연된다.
Patroni 4.0의 해법: 쿼럼 모드
synchronous_mode: quorum을 설정하면 Patroni는 PostgreSQL의 ANY N (standby_list) 문법을 사용한다.
synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)'이 설정은 "3개 중 어느 2개라도 확인하면 커밋 완료"를 의미한다. 가장 빠른 2개가 응답하면 된다. 느린 복제본 하나가 지연되더라도 커밋이 차단되지 않는다.
Patroni는 실행 중인 복제본 수를 감시하면서 N을 자동으로 조정한다. 복제본이 2개만 살아있으면 ANY 1로 낮추고, 다시 3개가 복구되면 ANY 2로 올린다.
커밋 대기 — 모든 동기 복제본
✓ 빠름
⚠️ 지연
✓ 빠름
커밋 대기 — ANY 2 of 3
✓ 빠름
⚠️ 지연
✓ 빠름
Replica 2 응답 기다리지 않음
페일오버 선택 기준 변화
쿼럼 모드에서 Primary가 장애가 나면, Patroni는 어느 복제본을 새 Primary로 선택할까?
기존 전통 동기 모드에서는 synchronous_standby_names에 등록된 복제본이 우선 순위를 가졌다. 쿼럼 모드에서는 최신 트랜잭션을 받은 복제본을 기준으로 선택한다. LSN(Log Sequence Number)이 가장 높은 복제본이 우선 후보가 된다.
이 차이는 중요하다. 쿼럼 커밋이 됐지만 특정 복제본이 그 트랜잭션을 아직 적용하지 못했을 수 있다. Patroni는 이 경우 pg_rewind를 사용해 새 Primary와 기존 Primary(또는 뒤처진 복제본)를 재동기화한다.
핵심 변화 2: master → primary 용어 변경
Patroni 4.0의 파괴적 변경 중 가장 즉각적인 영향을 미치는 것이다.
영향을 받는 곳
Kubernetes 환경:
- 기존:
patroni.zalando.org/role: master(또는spilo-role: master) - 변경:
patroni.zalando.org/role: primary
Kubernetes Service의 셀렉터가 role: master를 포함하고 있으면, Patroni 4.0으로 업그레이드하는 즉시 해당 Service가 Primary Pod을 찾지 못하고 트래픽이 끊긴다.
콜백 스크립트:
- 기존:
on_role_change콜백에role=master파라미터가 전달됨 - 변경:
role=primary로 전달됨
if [ "$1" == "master" ] 형태의 조건문이 있는 스크립트는 더 이상 작동하지 않는다.
환경 변수 및 메타데이터:
- REST API
/patroni엔드포인트의role필드 값이master→primary로 변경 - 이 값에 의존하는 모니터링 쿼리, 알림 규칙이 있다면 모두 업데이트 필요
업그레이드 전 점검 목록
# K8s 환경에서 'master' 셀렉터를 사용하는 리소스 확인
kubectl get services --all-namespaces -o yaml | grep -A5 "selector:" | grep "master"
kubectl get endpoints --all-namespaces -o yaml | grep "master"
# 콜백 스크립트 확인
grep -r "role=master" /etc/patroni/callbacks/
grep -r '"master"' /etc/patroni/callbacks/신규 기능: patronictl demote-cluster / promote-cluster
Patroni 4.x에서 추가된 두 명령은 Patroni 클러스터를 Primary/Standby 역할 간에 전환하는 작업을 자동화한다. 이는 재해복구(DR) 사이트 전환이나 계획된 Primary 마이그레이션에서 핵심적으로 쓰인다.
배경: Patroni의 Standby Cluster 개념
Patroni는 단일 클러스터가 아닌 Primary 클러스터 + Standby 클러스터 쌍으로 구성할 수 있다. Standby 클러스터는 Primary 클러스터의 복제본을 각자 관리하면서, 전체 클러스터 단위로 역할 전환이 가능하다.
예: 서울 데이터센터(Primary 클러스터) ↔ 부산 데이터센터(Standby 클러스터)
demote-cluster 명령
# Primary 클러스터를 Standby 클러스터로 전환
patronictl -c /etc/patroni.yml demote-cluster --wait이 명령은 다음 순서로 실행된다:
- Primary 클러스터의 현재 Leader를 graceful shutdown
- 클러스터 전체를 Standby 모드로 전환
- 기존 Primary는 다른 Standby 클러스터의 복제본이 됨
promote-cluster 명령
# Standby 클러스터를 Primary 클러스터로 승격
patronictl -c /etc/patroni.yml promote-cluster --wait이 명령은 Standby 클러스터의 Leader를 Primary로 승격하고, 클러스터 전체를 Primary 모드로 전환한다.
이 두 명령이 추가되기 전에는 클러스터 역할 전환을 Patroni REST API를 직접 호출하거나 DCS(etcd/Consul/ZooKeeper) 데이터를 수동으로 조작하는 방식으로 해야 했다.
member_slots_ttl 파라미터
Patroni 4.1.0에서 추가된 글로벌 설정 파라미터다.
문제: 복제 슬롯은 복제본이 사라진 뒤에도 PostgreSQL에 남아있어 WAL 축적을 일으킨다. Patroni는 DCS에서 멤버 키가 사라지면 해당 복제본의 슬롯을 제거하려 한다. 그런데 멤버 키 만료와 실제 프로세스 종료 사이에 타이밍 차이가 생길 수 있다.
해법: member_slots_ttl은 멤버 키가 DCS에서 사라진 후 복제 슬롯을 유지하는 최대 시간(초)을 지정한다.
# patroni.yml
bootstrap:
dcs:
member_slots_ttl: 3600 # 1시간 동안 슬롯 유지 후 삭제이 파라미터를 설정하지 않으면 기존 동작(멤버 키 소멸 즉시 슬롯 삭제)이 유지된다. 일시적 네트워크 단절로 멤버 키가 만료됐지만 복제본이 다시 연결될 가능성이 높은 환경에서는 이 값을 적절히 설정해 불필요한 슬롯 재생성을 줄일 수 있다.
Patroni 4.1.4 안정성 수정 (2026-07-07)
4.1.4는 기능 추가보다 운영 안정성 개선에 집중한 릴리스다.
etcd 리더 소실 오류 처리
etcd가 리스 업데이트 중 리더를 잃으면 Unknown 에러를 반환한다. 이전 Patroni는 이 에러를 처리하지 못해 예상치 못한 동작이 발생했다. 4.1.4는 이 경우 에러 코드를 Unavailable로 재정의해 정상적인 재시도 로직이 작동하도록 수정했다.
pg_replication_slots 쿼리 통합
논리 복제 슬롯을 정리할 때 failover와 synced 필드의 잘못된 처리로 KeyError 예외가 발생하는 문제가 수정됐다. Patroni 4.x에서 논리 복제와 물리 복제 슬롯 쿼리 경로가 통합됐다.
pg_rewind 시작 타이밍 처리
PostgreSQL 인스턴스가 Standby로 시작 중(아직 연결을 받지 않는 상태)일 때 pg_rewind를 실행해야 하는 경우, Patroni가 pg_controldata에서 정보를 가져오도록 fallback이 추가됐다. 이전에는 이 타이밍에 pg_rewind가 실패해 수동 개입이 필요했다.
systemd 관련 수정
NOTIFY_SOCKET 환경 변수가 없는 환경에서 systemd 패키지를 불필요하게 임포트하면 FileNotFoundError가 발생했다. 이제 환경 변수 존재 여부를 먼저 확인한다.
PostgreSQL 18 지원
Patroni 4.1.x에서 PostgreSQL 18 지원이 추가됐다. PostgreSQL 18은 2025년 9월 출시됐으며 인증 방식과 내부 파라미터 구조에 변화가 있었다.
4.1.4는 PostgreSQL 버전에 따라 불필요한 인증 파라미터가 설정에 섞여 들어오는 문제를 수정했다. 환경 변수에서 가져온 설정 중 현재 PostgreSQL 버전에서 지원하지 않는 파라미터가 자동으로 제외된다.
3.x에서 4.x로 업그레이드 경로
현재 Patroni 버전 ≥ 3.1.0인지 확인
3.0.x 이하에서 직접 4.x 업그레이드는 Primary 장애 시 예측 불가 동작 가능
Services, Endpoints, Ingress, ConfigMap에서 'role: master' → 'role: primary'
Patroni 업그레이드 전에 미리 적용하면 롤링 업그레이드 중 다운타임 없음
role=master → role=primary 조건문 수정
patronictl REST API 응답 파싱 코드 점검
복제본 노드 먼저 업그레이드 → 검증 → Primary 마지막 업그레이드
각 노드 업그레이드 후 patronictl list로 상태 확인
synchronous_mode: true → synchronous_mode: quorum
먼저 스테이징 환경에서 페일오버 테스트 후 프로덕션 적용
쿼럼 모드 전환 판단 기준
| 상황 | 권장 |
|---|---|
| 복제본 3개 이상, 레이턴시 민감 | synchronous_mode: quorum 권장 |
| 복제본 2개, 모든 트랜잭션에 강력한 내구성 필요 | synchronous_mode: true 유지 |
| 복제본 1개 | 쿼럼 모드 의미 없음 (ANY 1 = 전통 동기 방식과 동일) |
| 비동기 복제 허용, 성능 우선 | synchronous_mode: false |
운영 체크리스트
업그레이드 전
- [ ]
patronictl version으로 현재 버전 확인 → 3.1.0 이상인지 검증 - [ ] K8s 환경:
kubectl get svc -A -o yaml | grep -A5 selector | grep master실행하여 영향받는 Services 목록 작성 - [ ] 콜백 스크립트 디렉터리에서
role=master문자열 검색 - [ ] DCS(etcd/Consul) 버전 호환성 확인 — 4.1.4에서 etcd Unknown 에러 처리가 개선됐으므로 etcd 3.5+ 권장
업그레이드 중
- [ ] 복제본 노드 먼저 4.1.4로 업그레이드
- [ ]
patronictl list에서 복제본 노드 상태가 running인지 확인 - [ ] Primary 노드 마지막 업그레이드
- [ ] 업그레이드 직후
patronictl list에서 Primary 레이블이primary로 바뀌었는지 확인
업그레이드 후 검증
- [ ] 읽기 트래픽이 복제본으로, 쓰기 트래픽이 Primary로 정상 라우팅되는지 확인
- [ ] 모니터링 대시보드에서
role레이블 값이 변경된 것에 맞춰 쿼리 업데이트 - [ ] 테스트 환경에서 수동 페일오버(
patronictl switchover) 실행하여 새 Primary 선출 정상 동작 검증 - [ ] 쿼럼 모드 도입 시: 복제본 하나를 의도적으로 종료해 쿼럼 카운트(ANY N)가 자동으로 줄어드는지 확인
Open question
- Patroni 4.0이
synchronous_mode: quorum을 도입할 때 기존synchronous_mode: true대비 정확한 레이턴시 개선 수치는 공식 문서에 없다. 워크로드 특성에 따라 다르므로 프로덕션 적용 전 직접 측정 필요. patronictl demote-cluster와promote-cluster명령의 정확한 추가 버전(4.0인지 4.1.x인지)은 검색 결과에서 명확하지 않다. 공식 changelog 확인 필요.- member_slots_ttl과 기존 DCS TTL 설정 사이의 상호작용(복제 슬롯 유지 정책이 DCS TTL보다 길면 발생할 수 있는 WAL 축적 시나리오)에 대한 공식 가이드라인이 현재 없다.
References
- Patroni Release Notes — Patroni 4.1.4 Documentation
- Patroni Documentation Release 4.1.4 PDF — ReadTheDocs (2026-07-07)
- Patroni Documentation Release 4.1.3 PDF — ReadTheDocs (2026-05-05)
- Patroni: DCS Failsafe Mode — 4.1.4 Documentation
- Patroni Replication Modes — 4.1.4 Documentation
- patroni.quorum Module Documentation — Patroni 4.1.3
- Released 2024-08-30 — Patroni GitHub releases.rst