페일오버가 안전하지 않을 때: Kubernetes 기반 고가용성 PostgreSQL 구축
(datadoghq.com)- k8s 기반 PostgreSQL 클러스터에서 네트워크 장애 시 복제 지연(replication lag) 이 누적되며 안전한 페일오버가 불가능해지는 구조적 약점을 해결한 방법
- 기존 구조는 가용성(availability) 을 내구성(durability) 보다 우선시해, 프라이머리가 쓰기를 계속 받는 동안 복제본이 뒤처지면서 데이터 손실 없이 승격할 후보가 사라짐
- 해결책으로 페일오버 후보에 동기식 복제(synchronous replication) 를 적용하고, 오픈소스 고가용성 관리자 Patroni로 조율
- 리더 풀 스탠바이만 동기식 복제에 참여하고 읽기 복제본은 비동기를 유지하는 하이브리드 복제 모델로, 내구성과 지연 시간 사이 균형 확보
remote_apply모드 적용 시 쓰기 지연 53% 증가 등 성능 비용에도, 5가지 장애 시나리오 검증을 통해 안전한 자동 페일오버 달성
게임데이에서 드러난 문제
- Datadog은 시스템과 프로세스의 빈틈을 선제적으로 찾기 위해 게임데이를 정기 실시, 플랫폼에 의도적 부하를 가해 실제 조건에서의 반응 학습
- 한 게임데이에서 스테이징 환경에 가용 영역(AZ) 장애를 시뮬레이션하며 네트워크 지연을 유발, PostgreSQL 아키텍처의 약점 노출
- 여러 Kubernetes 기반 PostgreSQL 클러스터의 프라이머리/라이터 노드가 영향받은 AZ에서 동작 중
- 네트워크 지연 급증으로 프라이머리가 복제본과 안정적으로 통신 불가, 복제 지연 증가 → 쓰기 정체 → 애플리케이션이 오래된 데이터 제공
- 충분히 최신 상태인 복제본이 없어 페일오버가 안전하지 않았고, 클러스터가 사실상 멈춤
- 이 구조는 정상 조건에서는 잘 작동했으나, 특정 네트워크 장애 상황에서 가용성을 내구성보다 우선시해 안전한 복구 경로가 없음
- 프라이머리는 복제가 지연되는 동안에도 쓰기를 계속 수용, 복제 지연이 커지며 복제본이 더 뒤처짐
- 결과적으로 데이터 손실 위험 없이는 페일오버 후보를 승격할 수 없었고, 지연이 잦아들고 복제본이 따라잡기를 기다리는 것이 유일한 선택지
- 목표는 PostgreSQL 성능 특성을 필요 이상으로 훼손하지 않으면서 페일오버를 자동이면서 안전하게 만드는 것
기준 아키텍처: Kubernetes 위의 PostgreSQL
- Kubernetes 기반 PostgreSQL 클러스터는 leader pool과 read replica pool 두 풀로 구성, PostgreSQL은 단일 라이터(single-writer) 시스템
- 읽기/쓰기 분리로 리더 부담 없이 읽기 확장 가능, 쓰기 지연은 예측 가능하고 안정적으로 유지
- leader pool은 모든 쓰기를 처리하는 단일 활성 라이터 노드 1개와 스탠바이 노드 2개로 구성
- 스탠바이는 애플리케이션 트래픽을 처리하지 않으나 리더 장애 시 승격 가능
- read replica pool은 읽기 전용 트래픽을 처리하는 다수 노드로 구성, 읽기 확장성과 쿼리 격리에 최적화되어 페일오버 대상에서 의도적으로 제외
Patroni와 ZooKeeper의 역할
- Patroni가 복제, 페일오버, 리더 선출을 관리하며, 분산 구성 저장소(DCS)로 ZooKeeper 사용
- ZooKeeper는 현재 리더 키/락, 클러스터 구성, 각 멤버의 복제 상태(최신 LSN 등) 메타데이터 저장
- Patroni는 이 정보로 승격·강등을 보수적으로 판단, 공격적 페일오버보다 데이터 일관성 우선
- 새 노드가 클러스터에 합류하면 먼저 ZooKeeper에서 리더 존재 여부 확인
- 리더가 없으면 임시 znode 생성으로 리더 키 획득 시도, ZooKeeper가 단일 노드만 키 획득을 보장해 다중 프라이머리 형성 방지
- 리더가 이미 있으면 자신을 복제본으로 구성하고 스트리밍 복제 시작
- 네트워크 분할(network partition) 시 리더 또는 ZooKeeper와의 연결이 끊긴 복제본은 클러스터 상태를 확인할 수 없어, Patroni가 해당 노드를 일시 중지 또는 강등
- 리더가 연결을 잃으면 Patroni가 ZooKeeper와 협력해 적격 스탠바이 하나만 리더 락 획득, 부분 네트워크 장애에서도 통제된 페일오버 보장
- 연결 복구 후 기존 리더는 리더 락 재획득에 실패하면 스스로 스탠바이로 강등, split brain 방지
안전한 페일오버가 불가능했던 이유
- 단일 라이터 모델에서 장애 시 Patroni가 정상 스탠바이 중 새 리더를 선출
- 데이터 손실 방지를 위해 Patroni는 승격 전 안전 점검 수행, 핵심은 복제 지연이
maximum_lag_on_failover임계값 이내인지 확인- 스탠바이가 리더보다 뒤처졌으면 승격 시 데이터 누락·불일치 발생 가능
- 게임데이에서 프라이머리가 연결을 잃자 모든 스탠바이의 복제 지연이 임계값 초과, Patroni가 페일오버를 정확히 거부
- 클러스터가 안전한 쓰기 가능 프라이머리 없이 남은 것은 Patroni 때문이 아니라 안전한 승격 후보가 없었기 때문
스트리밍 복제의 두 가지 모드
- 스트리밍 복제에서 리더는 모든 변경을 담은 write-ahead log(WAL) 를 복제본에 지속 전송, 복제본은 WAL을 로컬에 적용해 동기화 유지
-
비동기식 복제 (기본값)
- 리더가 트랜잭션 커밋 전 복제본의 확인을 기다리지 않음
- 쓰기 지연 최소화, 높은 처리량 지원
- 단, 리더 장애 시 프라이머리에 커밋됐으나 아직 복제되지 않은 트랜잭션이 승격 과정에서 손실 가능
-
동기식 복제
- 리더가 클라이언트에 응답을 보내기 전 최소 1개 복제본의 확인 대기
- 적어도 하나의 복제본이 너무 뒤처질 위험을 크게 줄이고, 커밋된 트랜잭션이 다른 노드에 존재함을 확인 후 응답하므로 더 강한 내구성 보장
- 페일오버 후보가 최신 상태일 가능성이 높아 데이터 분기 위험 없이 승격 가능
하이브리드 복제 설정
- 내구성·지연·처리량 균형을 위해 하이브리드 복제 모델 채택
- leader pool의 스탠바이 노드는 동기식 복제에 참여, 리더가 지정 동기 스탠바이의 확인 후 쓰기 커밋
- read replica는 비동기식 복제 유지, 읽기 전용 트래픽만 처리하고 페일오버 대상이 아니므로 리더 풀 복제 부담 제한
- 이를 통해 읽기 복제본에 동일한 지연 비용을 부과하지 않으면서 페일오버 후보에만 엄격한 내구성 보장 적용
안전한 페일오버를 위한 PostgreSQL·Patroni 튜닝
- 동기식 복제 활성화를 위해 PostgreSQL과 Patroni 양쪽에서 파라미터 조정
-
조정한 주요 파라미터
synchronous_mode: Patroni 동기식 복제 활성화,true면synchronous_node_count에 따라 동기 스탠바이 확인 후 커밋 (기본값 false → true, Patroni 관리, 필수)synchronous_node_count: 동기 스탠바이 노드 수,synchronous_standby_names목록 생성에 사용 (기본값 1 → 1, Patroni 관리, 선택)synchronous_mode_strict: 엄격 동기 모드 강제,true이고 가용 복제본이 없으면 비동기 전환 대신 쓰기 차단 (기본값 false → true, Patroni 관리, 선택)synchronous_commit: PostgreSQL 커밋 내구성 설정 (기본값 on → remote_apply, PostgreSQL 관리, 선택)
- 적용 후 리더는 동기 스탠바이가 데이터 수신·적용을 확인한 뒤에만 클라이언트에 트랜잭션 응답 전송
내구성과 지연 시간의 균형
- 동기식 복제는 내구성을 개선하나, 리더가 동기 스탠바이의 확인을 기다리므로 쓰기 지연 증가, 지속 부하 시 처리량 영향 가능
- 성능 영향은 동기 스탠바이 수(
synchronous_node_count)와synchronous_commit로 설정한 내구성 수준에 좌우 -
synchronous_commit내구성 단계별 트레이드오프remote_apply: 복제본이 WAL을 쓰고 플러시하고 재생할 때까지 대기, 가장 강한 일관성·가장 높은 지연on(내부적으로 remote_flush): 복제본이 WAL을 디스크에 플러시할 때까지 대기, 강한 내구성이나 스탠바이에서 아직 읽기 불가remote_write: WAL이 복제본 OS 캐시(디스크 아님)에 도달할 때까지 대기, 낮은 지연이나 OS 크래시에 취약local: 스탠바이 대기 없이 로컬 디스크 플러시 후 커밋, 노드 간 내구성 보장 없음off: 로컬 WAL 플러시 전 커밋, 가장 낮은 지연·가장 높은 데이터 손실 위험
동기식 복제 성능 벤치마킹
- 각 커밋이 하나 이상 스탠바이의 확인을 기다리므로 동기식 복제는 지연을 추가, 영향 정량화를 위해 PostgreSQL 표준 부하 테스트 도구 pgbench로 벤치마크 수행 (Patroni 버전 3.2.1)
- 단순 읽기·쓰기 혼합을 시뮬레이션하는 TPC-B 트랜잭션 스위트 사용, 두 지표 측정
- 평균 지연: 트랜잭션당 평균 처리 시간(ms)
- 초당 트랜잭션 수(tps): 연결 설정 시간 제외 완료 트랜잭션 수
-
테스트 파라미터
- 스케일 팩터(데이터베이스 크기), 클라이언트 수(동시 사용자 트래픽), 스레드 수(CPU·병렬성), 트랜잭션 수(워크로드 강도)를 변화시켜 운영 유사 조건 모사
- 쿼럼 커밋 모드의 동기식 복제는 이번 벤치마크에서 테스트 미실시
-
벤치마크 결과
- 쓰기 지연 증가율:
remote_apply53%,on46%,remote_write38%,local32% - 처리량(tps) 감소율:
remote_apply34%,on31%,remote_write27%,local23% remote_apply는 복제본의 WAL 재생·적용까지 대기해 일관되게 가장 높은 지연·가장 낮은 처리량을 보였으나, 가장 강한 일관성으로 안전한 페일오버에 적합
- 쓰기 지연 증가율:
-
프로덕션 적용
- 벤치마킹 후 여러 고쓰기 클러스터에
remote_apply배포, 지속적인 프로덕션 부하에서도 애플리케이션 수준 쓰기 지연·처리량에 유의미한 영향 없음 - 성능 위험 완화를 위해 데이터센터·워크로드 계층별로 단계적 롤아웃, 단계 사이에 베이크인 기간과 지속 모니터링 적용
- 예: 고처리량 리소스 처리 워크로드는 동기식 복제 활성화 후 DB 쓰기 지연 증가에도 처리 지연이나 하류 백로그 없이 계속 동작
synchronous_commit은 다운타임 없이patronictl edit-config로 즉시 조정 가능, 초고처리량 워크로드의 커밋 내구성을 신속히 낮출 유연성 제공
- 벤치마킹 후 여러 고쓰기 클러스터에
장애 시나리오를 통한 페일오버 검증
- 동기식 복제와 엄격한 페일오버 제어가 데이터 무결성 보호, split-brain 방지, 자동 복구를 보장하는지 검증
-
시나리오 1: 동기 스탠바이 1개 손실
- 동기 스탠바이 손실 시 Patroni가 다른 적격 스탠바이를 할당해 동기식 복제 유지 시도
- 리더 노드의 Patroni가
pg_stat_replication으로 끊김·정체·지연 스트리밍 연결을 감지하고 ZooKeeper로 복제본 멤버십 추적 - 정상·적격 스트리밍 복제본 목록을 재계산하고
synchronous_node_count에 따라synchronous_standby_names갱신, 동기식 복제 활성 상태로 계속 동작
-
시나리오 2: 모든 동기 스탠바이 손실
- 동작은
synchronous_mode_strict값에 좌우 -
비엄격 모드: 쓰기 가용성 우선
- Patroni가
synchronous_standby_names를 비워 동기식 복제를 일시 비활성화, 정상 복제본 재합류 전까지 리더가 비동기로 전환해 쓰기 계속 허용
- Patroni가
-
엄격 모드: 안전을 위해 쓰기 차단
- Patroni가
synchronous_standby_names를*로 설정, 명시적 동기 스탠바이가 없어도 PostgreSQL이 쓰기 트랜잭션을 수용·로컬 커밋하나 복제본이 WAL을 확인할 때까지 클라이언트 응답 차단 - 동기식 복제에 합류 가능한 적합 복제본이 재연결되면 Patroni가 동기 스탠바이 역할 부여
- Patroni가
- 동작은
-
시나리오 3: 모든 스탠바이·복제본 노드 불가용
- 모든 복제본이 불가용하고
synchronous_mode_strict = true이면 PostgreSQL이 최소 1개 적격 복제본 복귀까지 트랜잭션 확인 보류 - 데이터 일관성은 유지되나 애플리케이션 수준에서 일시적 쓰기 불가 발생
- 모든 복제본이 불가용하고
-
시나리오 4: 동기 커밋 중 리더 장애
- 리더가 동기 스탠바이 확인을 기다리다 확인 수신 전 중단되는 엣지 케이스
- 흔한 원인: 커밋 중 클라이언트의 트랜잭션 취소, 리더 PostgreSQL 프로세스 크래시·종료, 커밋 단계 중 네트워크 분할
- PostgreSQL이 WAL을 로컬에 플러시했으나 스탠바이로 복제 실패 시, 확인이 없어 트랜잭션이 복제본에 보이지 않음
- 리더가 동기 스탠바이로 WAL 복제 전 크래시하고 해당 스탠바이가 승격되면 트랜잭션 손실 가능, 기존 리더와 신규 프라이머리의 히스토리 분기
- 기존 리더는 pg_rewind로 타임라인 분기 지점을 식별하고 데이터 디렉터리를 신규 프라이머리 타임라인에 맞춰 되감아, 복제되지 않은 로컬 변경을 폐기 후 스탠바이로 재합류
- 이 동작은 Patroni가 아닌 PostgreSQL의 동기 커밋 내부 처리 결과, 쿼럼 설정과 확인 메커니즘의 신중한 튜닝·모니터링 필요성 부각
-
시나리오 5: ZooKeeper 불가용
- ZooKeeper 불가용 시 Patroni가 리더십 확인·신규 선출 불가, 데이터 불일치 방지를 위해 보수적 동작으로 전환
-
failsafe 모드 비활성화 시
- ZooKeeper 도달 불가여도 리더가 접근 가능하고 모든 노드가 정상이면 쓰기 계속, 단 리더 락 TTL 만료까지만 유지
- 리더 키 갱신 루프 시간이 지나고 락 갱신 불가 시 Patroni가 리더를 강등하고 클러스터를 읽기 전용으로 전환
-
failsafe 모드 활성화 시
- 리더가 ZooKeeper 연결을 잃으면 Patroni가 REST API로 모든 클러스터 멤버 도달 가능 여부를 지속 확인
- 모든 멤버가 접근 가능할 때만 쓰기 계속, 그렇지 않으면 읽기 전용으로 강등
동기식 복제하의 수동 페일오버·스위치오버
- Patroni는 헬스 체크와 ZooKeeper 조율 기반 자동 페일오버·스위치오버 외에
patronictl명령으로 수동 작업도 지원, 동기식 복제 활성 시 모든 스탠바이가 유효 후보는 아니므로 데이터 무결성 보호용 가드레일 적용 -
비동기 스탠바이로의 페일오버
patronictl failover: 선택 노드가 비동기면 실패patronictl switchover: 선택 노드가 비동기면 실패- 동기식 복제 활성 중 비동기 노드로 페일오버를 강제하면 내구성 보장을 우회해 데이터 손실 가능
-
동기 스탠바이를 대상으로 할 때
patronictl failover: 성공, 리더가 동기 스탠바이로 전환patronictl switchover: 성공, 리더와 동기 스탠바이 간 우아한 핸드오프 수행
- 다양한
synchronous_commit모드 동작과 Patroni 가드레일 검증 후, 고쓰기·고읽기·혼합 워크로드 프로덕션 클러스터에 동기식 복제 활성화, 지연·처리량에 측정 가능한 영향 없음 - 문제가 생기면
synchronous_mode: false설정으로 다운타임 없이 비동기 복제로 안전하게 되돌리기 가능
DRBD를 선택하지 않은 이유
- 고가용성 평가에서 블록 수준 복제 시스템 DRBD(Distributed Replicated Block Device) 도 검토, PostgreSQL 데이터 디렉터리와 WAL 파일을 포함한 전체 볼륨을 서버 간 미러링해 거의 실시간 스탠바이 복제본 생성
- DRBD는 PostgreSQL 내장 스트리밍 복제보다 낮은 지연을 제공할 수 있으나, 신규 인프라·모니터링·운영 플레이북을 포함한 상당한 아키텍처 전환 필요
- 성숙한 Kubernetes 기반 설정과 PostgreSQL 동기식 복제의 세밀한 제어를 고려해, 가시성·유연성·운영 신뢰가 더 나은 데이터베이스 수준 복제 선택
동기식 복제 모니터링
- 동기식 복제 활성화 후 복제 상태와 페일오버 준비도를 면밀히 모니터링, 특히 두 신호가 대규모 안정성 유지에 기여
-
SyncRep 대기 이벤트
- 프라이머리가 커밋 완료·상태 반환 전 동기 스탠바이의 확인을 기다릴 때 발생, 일부는 정상이나 길거나 잦은 대기는 복제본 성능 문제나 노드 간 네트워크 지연을 시사
- 중요한 이유: 장기 대기는 쓰기 지연 증가·처리량 감소 유발
- 추적 대상:
SyncRep·WalSenderWaitForReply대기 이벤트의 지속 시간·빈도,wait_event:SyncRep태그로 필터링한postgresql.activity.waitsDatadog 메트릭(내부적으로pg_stat_activity테이블 쿼리)에서 수집
-
동기 스탠바이 미감지
- Patroni가 정상 동기 스탠바이를 장기간 감지하지 못하면 클러스터가 안전한 페일오버 능력 상실
- 중요한 이유: 동기 스탠바이가 없으면 페일오버 시 데이터 손실에 취약
- 알림 기준:
patroni_sync_standby가 비어 있는 상태가 지속되면 고가용성(HA) 헬스 알림 발생, OpenMetrics 데이터에서 수집하며 네이티브 Datadog 통합 없음
- 동기식 복제는 내구성을 개선하나 복제본이 비정상·도달 불가일 때 가용성·성능 저하, 대기 시간과 스탠바이 가용성 모니터링이 부하 상황에서 가용성·성능 유지에 필수
설계 단계부터 안전한 페일오버
- 시뮬레이션된 AZ 장애가 PostgreSQL 아키텍처의 치명적 약점을 드러냄, 복제본이 리더보다 뒤처져 네트워크 장애를 기다리거나 데이터 분기를 감수하는 양자택일 강요, 프로덕션에서 수용 불가한 트레이드오프
- Patroni 기반 동기식 복제 채택과 내구성·지연 튜닝으로 저하된 네트워크 조건에서도 페일오버를 가능하고 안전하게 구현, 벤치마킹과 반복 장애 시뮬레이션으로 대규모 성능 훼손 없이 예측 가능한 복구 확인
- 동기식 복제 장애 중 쓰기를 차단함으로써 실패를 상류 서비스에 명시적으로 노출, 비동기 복제처럼 쓰기가 조용히 손실되지 않고 재시도·큐잉 등으로 대응 가능해 장애 모드가 더 가시적이고 복구 가능
- 향후 쿼럼 기반 커밋 모드와 복제 상태에 대한 더 깊은 관측성 탐색 중