12.2 PostgreSQL 메이저 업그레이드
PostgreSQL은 메이저 버전이 바뀌면 내부 데이터 저장 포맷이 달라집니다. 그래서 minor 업데이트처럼 이미지만 갈아 끼워서는 안 되고, 데이터 디렉토리를 새 포맷으로 변환하는 절차가 필요합니다. CloudNativePG는 이 변환을 세 방식으로 지원합니다. logical dump/restore, logical replication, 그리고 pg_upgrade를 쓰는 물리 업그레이드입니다. 이 절은 그중 클러스터를 잠시 멈추고 제자리에서 올리는 offline in-place 업그레이드를 다룹니다.
여기서 in-place는 "무중단"이 아니라 "데이터를 다른 곳으로 옮기지 않는다"는 뜻입니다. dump/restore와 logical replication은 새 메이저의 Cluster를 옆에 따로 만들고 데이터를 복사해 넣지만, 이 방법은 기존 Cluster 객체와 PVC, 이름을 그대로 두고 그 PGDATA를 제자리에서 새 포맷으로 변환합니다. pg_upgrade --link는 데이터 파일을 복사하지 않고 hard link로 새 디렉토리에 연결하므로 데이터 크기와 무관하게 빠르고 추가 용량도 거의 들지 않습니다. 대신 변환하는 동안 클러스터 전체가 멈춥니다.
방아쇠는 단순합니다. 클러스터에 지정한 operand 이미지를 더 높은 메이저 버전으로 바꾸면 operator가 업그레이드가 필요하다고 판단하고, pg_upgrade로 데이터 디렉토리를 변환합니다.
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie
image catalog를 쓰는 클러스터라면 catalog의 major 버전을 올려도 같은 결과가 됩니다.
업그레이드 흐름
operator가 새 메이저 이미지를 감지하면 다음 순서로 진행합니다.
전체 종료
데이터 일관성을 위해 클러스터의 모든 Pod가 종료됩니다. 이 시점부터 업그레이드가 끝날 때까지 primary·replica 모두 접속 불가입니다.
이전 버전 기록
직전 PostgreSQL 버전을 .status.pgDataImageInfo에 남깁니다. 롤백 판단의 기준이 됩니다.
업그레이드 Job 실행
<primary-pod>-major-upgrade 이름의 Job이 생성됩니다. 이 Job은 바이너리와 데이터 파일의 정합성을 확인하고, PGDATA·WAL·tablespace용 새 디렉토리를 만든 뒤 pg_upgrade를 --link 옵션으로 실행하고, 원본 디렉토리를 변환된 것으로 교체합니다.
primary 재기동, replica 재복제
업그레이드가 끝나면 primary Pod가 새 버전으로 재기동됩니다. replica의 PVC는 폐기되고, 업그레이드된 primary로부터 다시 clone됩니다.
replica 재복제는 데이터베이스가 크면 오래 걸립니다. 큰 클러스터일수록 업그레이드 뒤 replica가 다시 붙어 HA가 회복되기까지의 시간을 넉넉히 예상합니다.
전제와 제약
- 같은 OS 배포판 사이에서만 가능합니다. 이미지의 기반 OS가 같아야 합니다. 예를 들어
bookworm에서trixie이미지로는 메이저 업그레이드를 할 수 없습니다. - extension 호환성은 직접 확인합니다. CloudNativePG는 extension 호환성을 관리하지 않습니다. source·target 이미지의 extension이 업그레이드 경로를 지원하는지 미리 검증합니다.
- PostgreSQL 17.0–17.5에는 버그가 있습니다.
max_slot_wal_keep_size가-1이 아니면 업그레이드가 실패하는 알려진 문제입니다. 17.6 이상으로 먼저 올리거나, 업그레이드 동안만 이 값을-1로 둡니다. - image volume extension과 함께 쓸 수 있습니다. 1.30부터 image volume으로 extension(9.5)을 추가한 클러스터도 PostgreSQL 19 이상으로 in-place 업그레이드할 수 있습니다. 업그레이드 Job은 현재 버전용 extension 이미지를
/extensions/<name>에, 새 버전용을/new-extensions/<name>에 함께 마운트해 양쪽 바이너리를 모두 찾을 수 있게 합니다. 실패 시 이전 메이저로 되돌릴 때 기존 경로가 그대로 남아 있어야 하기 때문입니다.
롤백
업그레이드가 실패하면 클러스터 설정의 이미지를 이전 메이저 버전으로 되돌립니다. operator가 롤백을 감지해 실패한 업그레이드 Job을 자동으로 지우고, 원래 버전으로 클러스터를 재기동합니다. 데이터 손실은 없습니다.
백업과 WAL archive 주의
pg_upgrade는 새 System ID를 만들고 timeline을 1로 리셋합니다. 이 때문에 몇 가지가 뒤엉킵니다.
- 새 timeline 1 파일이 기존 클러스터의 timeline 1 파일과 충돌합니다.
- WAL·백업 archive에 버전이 섞입니다.
메이저 버전 경계를 넘는 point-in-time recovery(PITR)는 불가능합니다. 업그레이드 이전에 받은 백업으로는 업그레이드 이후 시점으로 복구할 수 없습니다. 업그레이드 직후 새 baseline 백업을 반드시 받습니다.
archive가 섞이지 않게 하려면 이미지와 함께 archive 경로를 분리합니다. Barman Cloud plugin이라면 imageName과 serverName을 같이 바꿔, 옛 archive를 보존하면서 새 클러스터가 별도 경로에 쓰도록 합니다.
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie
plugins:
- name: barman-cloud.cloudnative-pg.io
isWALArchiver: true
parameters:
barmanObjectName: <object-store>
serverName: cluster-example-pg18 # 이전엔 -pg16
업그레이드 후 작업
- 모든 데이터베이스에
ANALYZE를 실행합니다. 대상이 PostgreSQL 17 이하이면pg_upgrade가 optimizer 통계를 옮기지 않고, 18부터는 대부분 옮기지만 extended statistics는 빠집니다. 18에서는vacuumdb --analyze-in-stages --missing-stats-only로 빠진 통계만 채워도 됩니다. 통계가 채워지기 전까지 실행 계획이 나빠질 수 있습니다. - extension 갱신이 필요하면 operator 로그에서
update_extensions.sql스크립트 경로를 확인하고, postgres superuser로 실행합니다.
정리
- 이미지의 major 버전을 올리면 operator가
pg_upgrade기반 offline 업그레이드를 시작합니다 - 흐름: 전체 종료 → 버전 기록 → major-upgrade Job(
pg_upgrade --link) → primary 재기동 → replica 재복제 - 업그레이드 내내 클러스터 전체가 다운되므로 다운타임을 감당할 수 있을 때만 택합니다
- 전제는 같은 OS 배포판과 extension 호환성 사전 검증, 실패 시 이미지를 되돌리면 operator가 자동 롤백합니다
- timeline이 리셋되므로 직후 새 baseline 백업이 필수이고 경계를 넘는 PITR은 불가하며, 끝나면
ANALYZE로 통계를 재수집합니다