지난주에 스테이징 Postgres 하나를 RDS에서 CloudNativePG(CNPG)로 옮겼다. 프로덕션까진 아직이고, 이번엔 진짜 스테이징 하나만. 근데 그것마저도 이틀을 통째로 갉아먹었다. 계획대로 안 된 부분을 정리해둔다. 우리 팀에서는 DB 운영 쪽이 아직 손이 많이 가는 영역이라, 나중에 프로덕션 옮길 때 또 밟지 않으려고.
배경부터 짧게. 우리 서비스에서 RDS PostgreSQL 15를 쓰고 있었는데, 스테이징 요금이 예상보다 계속 나와서 골치였다. 정확히는 스테이징 여러 개를 굴리다 보니 인스턴스가 5대쯤 됐고, 매달 청구서에서 눈에 밟혔다. Kubernetes 클러스터엔 여유 노드가 있고, CNPG가 최근 1.26 나오면서 plugin 아키텍처(CNPG-I)로 재편됐다는 얘길 듣고 이참에 한번 해보자 싶었다.
첫 삽 — barman이 왜 안 깔려 있지
가장 먼저 당황한 건 이거였다. CNPG 1.26을 설치하고 Cluster 매니페스트에 예전처럼 backup.barmanObjectStore 를 넣었는데, 오퍼레이터 로그에 barman 관련 컨트롤러가 아예 안 뜬다. 문서를 뒤져보니 1.26부터는 barman이 오퍼레이터에 번들되지 않는다. CNPG-I라는 플러그인 인터페이스가 나오면서 백업이 backup-agnostic 방식으로 바뀌었고, barman을 쓰려면 별도 플러그인을 설치해야 한다.
솔직히 이건 릴리스 노트를 대충 훑고 넘어간 내 잘못이다. 근데 예전 튜토리얼 그대로 따라 하다간 무조건 밟는다. barman-cloud 플러그인을 별도로 배포하고, Cluster 스펙에서는 .spec.plugins 아래로 백업 설정을 옮겨야 했다.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: staging-pg
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
plugins:
- name: barman-cloud.cloudnative-pg.io
parameters:
barmanObjectName: staging-pg-backup
storage:
size: 100Gi
storageClass: gp3
ObjectStore 라는 별도 CRD로 S3 대상 정보를 분리해서 관리하게 됐는데, 오히려 이 방식이 낫다. 백업 대상만 갈아 끼울 수 있고, 여러 클러스터가 공유하기도 편해졌다.
마이그레이션 자체는 의외로 조용했다
RDS에서 초기 데이터를 CNPG로 끌어오는 건 CNPG의 import 기능을 썼다. Cluster를 만들 때 .spec.bootstrap.initdb.import.source.externalCluster를 걸어두면, 오퍼레이터가 pg_dump/pg_restore로 알아서 옮겨준다. RDS 쪽에 read replica 없이도 됐고, 스키마와 데이터 400GB 정도가 3시간 반쯤 걸렸다. 이건 예상 범위였다.
문제는 그다음이었다.
Pod가 계속 죽는데 원인이 안 잡힘
import가 끝나고 프라이머리 하나 + 레플리카 2개 구성이 올라왔다. 근데 레플리카 하나가 30초마다 죽었다 살았다를 반복한다. 오퍼레이터 이벤트에 별말 없고, kubelet 로그를 봐도 그냥 readiness probe 실패라고만.
새벽 3시에 눈이 번쩍 떠졌다. 아. 1.26에서 readiness probe가 replication lag 기반으로 바뀌었지.
문서 뒤져보니 이번 릴리스부터 startup/readiness probe가 replication lag을 참조한다. 승격 후보에서 lag 있는 레플리카를 배제하려는 의도인데, 우리 스테이징은 초기 import 직후라 lag이 튀는 시간이 있었다. 기본 임계값이 촘촘해서, 스테이징의 느린 스토리지(gp3 baseline)에서 계속 튕겨나갔던 거다.
.spec.probes.readiness.maximumLag 를 늘려서 해결했다.
spec:
probes:
readiness:
maximumLag: 32MB
startup:
failureThreshold: 60
이게 정답인지는 아직 모르겠다. 프로덕션에선 lag이 튀는 게 오히려 신호로 쓸 수 있어야 하니, 이 값을 그냥 크게 잡는 게 능사는 아닌 듯. 스테이징에서만 느슨하게 두고, 프로덕션은 좀 더 고민해봐야 할 것 같다.
백업이 되고 있다고 착각한 시간들
두번째로 뒤통수. barman-cloud 플러그인은 잘 붙었고, S3에 WAL이 쌓이는 것도 확인했다. 근데 Backup 리소스를 처음 만들었을 때 상태가 completed로 뜨길래 안심하고 있었다.
이틀 뒤 아무 생각 없이 백업에서 복원 테스트를 해봤는데 실패한다. 로그를 보니 S3 오브젝트에 실제로 base backup이 없다. 뭐지 싶어 다시 파봤더니 IAM 권한 문제. ObjectStore CRD의 credentials로 IRSA를 걸어뒀는데, 서비스 어카운트에 붙은 IAM 역할이 PutObject는 되지만 PutObjectAcl 이 없었다. barman-cloud가 특정 조건에서 ACL을 명시적으로 걸려고 하면서, 실제 아카이빙은 실패하는데 상태 리포팅은 성공으로 뜨는 케이스가 있었다.
이건 좀 짜증났다. 상태와 실제가 다르니까. GitHub 이슈 트래커에서도 비슷한 리포트가 있었고, 오브젝트 스토어 정책을 명시적으로 바꾸는 걸로 우회했다.
이 사건 이후로 백업이 성공했다는 이벤트만 믿지 말고 반드시 복원 테스트를 스케줄에 넣자는 걸 다시 새겼다. 사실 다들 아는 얘긴데, 오퍼레이터가 알아서 해준다는 편안함에 잠깐 방심했다.
그래서 프로덕션은 옮길 건가
아직 결정 못 했다. 좋은 점은 명확하다. GitOps로 DB 스펙까지 관리되고, 스테이징 다섯 개를 노드 3대에 얹어서 청구서가 확 줄었다. major 업그레이드도 1.26부터는 이미지 태그만 바꾸면 declarative하게 pg_upgrade가 돌아가는데, 이건 스테이징에서 15→16 올려보니 진짜로 깔끔했다.
반면 불안한 점도 있다. 스토리지 성능이 RDS 대비 아직 미묘하고(gp3 IOPS 튜닝을 더 파야 한다), 프로덕션에서 예상 못한 페일오버가 났을 때 팀원 누구든 대응할 수 있게 런북을 다시 써야 한다. RDS는 콘솔에서 눌러도 되지만 CNPG는 kubectl로 들어가야 하니, 새벽 3시에 조는 눈으로 대응해야 하는 사람이 편할지가 아직 확신이 안 선다.
혹시 CNPG 프로덕션으로 굴리고 계신 분 있으면 페일오버 대응 어떻게 하시는지 궁금하다. 특히 replication lag 임계값을 프로덕션에서 어떻게 잡는지가 제일 궁금.
일단 다음 스프린트엔 barman 대신 pgBackRest 플러그인도 한번 붙여보려고 한다. 붙여보고 나면 또 정리해서 올릴 계획이다.
태그: CloudNativePG, PostgreSQL, Kubernetes, DB운영, 마이그레이션, CNPG
'IT > DB 운영' 카테고리의 다른 글
| Redis Sentinel에서 Cluster로 옮기다가 오후 4시간을 날린 이야기 (0) | 2026.08.30 |
|---|---|
| PostgreSQL VACUUM, 내부에서 무슨 일이 벌어지는가 - autovacuum 튜닝 전에 (0) | 2026.08.25 |
| Postgres 18 async I/O, io_uring 켜봤다가 반나절 날린 이야기 (0) | 2026.08.18 |
| PgBouncer transaction pool에서 prepared statement 쓰기, 이젠 진짜 된다 (0) | 2026.08.18 |
| PostgreSQL 논리 복제 슬롯, failover에서 살리는 법 (17 이후 실전 가이드) (0) | 2026.08.09 |