본문으로 건너뛰기

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 백업을 반드시 받습니다.

PostgreSQL 메이저 업그레이드 전후 복구 이력 경계

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로 통계를 재수집합니다