본문 바로가기
WIKI 기술 지식 베이스

PostgreSQL 업그레이드

원문 보기 위키 갱신

PostgreSQL 업그레이드 (PostgreSQL upgrades)

PostgreSQL 클러스터를 업그레이드하는 방법을 정리해 드릴게요. 마이너 버전 업그레이드(예: 18.0 → 18.1)와 메이저 버전 업그레이드(예: 16.x → 18.0)로 나뉘며, 특히 CloudNativePG의 오프라인 인플레이스 메이저 업그레이드 과정을 자세히 알아볼게요.

출처: 문서

본문

PostgreSQL 업그레이드는 두 가지 범주로 나뉘어요:

마이너 버전 업그레이드 (Minor Version Upgrades)

PostgreSQL 버전 번호는 major.minor 형식을 따라요. 예를 들어 18.1에서:

  • 18은 메이저 버전
  • 1은 마이너 버전

마이너 릴리스는 같은 메이저 버전의 이전 및 이후 마이너 릴리스와 완전히 호환돼요. 버그 수정과 보안 업데이트를 포함하지만 내부 스토리지 형식에는 변경을 도입하지 않아요.

CloudNativePG에서 마이너 버전 업그레이드 (Upgrading a Minor Version in CloudNativePG)

더 새로운 마이너 버전으로 업그레이드하려면 클러스터 정의의 PostgreSQL 컨테이너 이미지 참조를 직접 또는 이미지 카탈로그를 통해 업데이트하면 돼요. CloudNativePG는 클러스터의 롤링 업데이트를 트리거하고, 레플리카부터 시작해 각 인스턴스를 하나씩 교체해요. 모든 레플리카가 업데이트되면 primary의 스위치오버 또는 재시작을 수행해 프로세스를 완료해요.

메이저 버전 업그레이드 (Major Version Upgrades)

PostgreSQL 메이저 릴리스는 내부 데이터 스토리지 형식에 변경을 도입하므로, 더 구조화된 업그레이드 프로세스가 필요해요.

CloudNativePG는 메이저 업그레이드를 수행하는 세 가지 방법을 지원해요:

  1. 로지컬 덤프/복원 – Blue/green 배포, 오프라인.
  2. 네이티브 로지컬 리플리케이션 – Blue/green 배포, 온라인.
  3. pg_upgrade를 사용한 물리적 방식 – 인플레이스 업그레이드, 오프라인 (아래의 "오프라인 인플레이스 메이저 업그레이드" 섹션에서 다룸).

각 방법은 다운타임, 복잡성, 데이터 볼륨 처리 측면에서 트레이드오프가 있어요. 최선의 접근 방식은 업그레이드 전략과 운영 제약에 따라 달라져요.

:::info[Important] 프로덕션 업그레이드를 진행하기 전에 모든 방법을 통제된 환경에서 테스트할 것을 강력히 권장해요. :::

오프라인 인플레이스 메이저 업그레이드 (Offline In-Place Major Upgrades)

CloudNativePG는 클러스터에 더 높은 PostgreSQL 메이저 버전의 새 오퍼랜드 컨테이너 이미지가 선언적으로 요청될 때 오프라인 인플레이스 메이저 업그레이드를 수행해요.

:::info[Important] 메이저 업그레이드는 같은 운영체제 배포판에 기반한 이미지 사이에서만 지원돼요. 예를 들어 이전 버전이 bullseye 이미지를 사용한다면 bookworm 이미지로 업그레이드할 수 없어요. :::

:::warning PostgreSQL 17.0부터 17.5까지는 max_slot_wal_keep_size 파라미터가 -1이 아닌 다른 값으로 설정되면 업그레이드가 성공하지 못하게 하는 버그가 있어요. 업그레이드 프로세스는 리플리케이션 슬롯 구성과 관련된 오류로 실패해요. 이 문제는 PostgreSQL 17.6 및 18 이상에서 수정됐어요. PostgreSQL 17.0부터 17.5를 사용한다면 메이저 업그레이드를 시도하기 전에 최소 PostgreSQL 17.6으로 업그레이드하거나, 클러스터 구성에서 max_slot_wal_keep_size 파라미터를 임시로 -1로 설정해야 해요. :::

업그레이드는 두 가지 방법 중 하나로 트리거할 수 있어요:

지원되는 이미지 태그에 대한 자세한 내용은 "Image Tag Requirements(이미지 태그 요구사항)"를 참고해 주세요.

:::warning CloudNativePG는 PostgreSQL 확장에 대해 책임지지 않아요. 소스 PostgreSQL 이미지의 확장이 대상 이미지의 확장과 호환되고 업그레이드 경로가 지원되는지 반드시 확인해야 해요. 예상치 못한 문제를 피하기 위해 업그레이드 프로세스를 사전에 철저히 테스트하세요. 확장 관리 기능이 확장 업그레이드를 선언적으로 관리하는 데 도움이 될 수 있어요. :::

업그레이드 프로세스 (Upgrade Process)

  1. 데이터 일관성을 보장하기 위해 모든 클러스터 파드를 종료해요.
  2. 이전 PostgreSQL 버전과 이미지를 클러스터 상태의 .status.pgDataImageInfo 아래에 기록해요.
  3. 새 업그레이드 잡(job)을 시작하며, 이 잡은:
    • 이미지의 바이너리와 데이터 파일이 메이저 업그레이드 요청과 일치하는지 검증해요.
    • PGDATA 및 해당하는 경우 WAL 파일과 테이블스페이스용 새 디렉토리를 생성해요.
    • --link 옵션과 함께 pg_upgrade를 사용해 업그레이드를 수행해요.
    • 성공적으로 완료되면 원래 디렉토리를 업그레이드된 버전으로 교체해요.

:::warning 업그레이드 프로세스 동안 레플리카를 포함한 전체 PostgreSQL 클러스터는 애플리케이션에 사용 불가능해요. 진행하기 전에 시스템이 이 다운타임을 견딜 수 있는지 확인하세요. :::

:::warning 인플레이스 업그레이드를 수행하는 것은 고유한 위험이 수반되는 예외적인 작업이에요. 업그레이드 프로세스를 시작하기 전에 클러스터의 전체 백업을 받을 것을 강력히 권장해요. :::

:::info pg_upgrade에 대한 상세한 지침은 공식 PostgreSQL 문서를 참고해 주세요. :::

업그레이드 후 작업 (Post-Upgrade Actions)

업그레이드가 성공하면 CloudNativePG는:

  • 레플리카의 PVC(사용 가능한 경우)를 파괴해요.
  • 필요한 대로 레플리카를 스케일 업해요.

:::warning 레플리카 재복제는 특히 매우 큰 데이터베이스에서 시간이 걸릴 수 있어요. 잠재적 지연을 수용하도록 계획하세요. 업그레이드 완료 후 가능한 한 빨리 새 베이스 백업을 받으세요. 업그레이드 전 백업과 WAL 파일은 메이저 버전 경계를 넘어 시점 복구(PITR)에 사용할 수 없어요. 자세한 내용은 백업 및 WAL 아카이브 고려사항을 참고해 주세요. :::

:::warning pg_upgrade는 옵티마이저 통계를 전송하지 않아요. 업그레이드 후 통계를 업데이트하려면 데이터베이스에서 ANALYZE를 실행하는 것이 좋아요. :::

업그레이드가 실패하면 클러스터 구성의 이미지를 이전 메이저 버전으로 되돌리세요. 오퍼레이터는 롤백을 감지하고 실패한 업그레이드 잡을 자동으로 삭제해 클러스터가 원래 버전으로 재시작할 수 있게 해줘요.

:::info[Important] 이 프로세스는 업그레이드 중에 어떤 데이터도 수정되지 않으므로 기존 데이터베이스를 데이터 손실로부터 보호해요. 업그레이드가 실패하면 백업에서 전체 복구를 수행하지 않고도 보통 롤백이 가능해요. 프로세스를 면밀히 모니터링하고 필요한 경우 시정 조치를 취하세요. :::

메이저 업그레이드 중 확장 처리 (Extensions during a major upgrade)

클러스터가 이미지-볼륨 확장을 선언하면 업그레이드 Job은 소스 버전과 대상 버전의 확장 이미지를 나란히 마운트해요. 소스 버전 확장은 이전 서버가 기존 공유 라이브러리를 이미 갖춘 채로 시작할 수 있도록 필요해요. 대상 버전 확장(새 PostgreSQL 메이저용으로 빌드된)도 pg_upgrade가 새 메이저로의 업그레이드를 제대로 수행할 수 있도록 존재해야 해요:

  • 소스 버전 확장은 /extensions/<name>(정상 상태 경로)에 마운트되므로, 이전 PGDATA의 dynamic_library_path와 extension_control_path GUC가 기존 값을 유지해요. 이렇게 하면 메이저 업그레이드 실패 시 이전 메이저 버전으로의 복귀가 원활하게 작동해요.
  • 대상 버전 확장은 업그레이드 잡 동안에만 /new-extensions/<name>에 임시로 마운트되고, 새 메이저의 PGDATA에 맞게 dynamic_library_path와 extension_control_path가 구성돼요.

LD_LIBRARY_PATH와 PATH는 두 세트 모두로 확장되므로, 어느 버전의 바이너리와 공유 객체라도 접근 가능해요. extensions[].env를 통해 선언된 환경 변수는 Precedence and Conflict Resolution(우선순위 및 충돌 해결)에 설명된 규칙을 따릅니다.

:::note 대상 버전 항목은 소스 버전 항목 이후에 적용돼요. 같은 변수가 둘 다에 정의되면 대상 버전 값이 우선해요. :::

pg_upgrade가 update_extensions.sql을 생성할 때 (When pg_upgrade emits update_extensions.sql)

대상 클러스터에 pg_upgrade가 설치해 둔 버전보다 default_version이 더 새로운 확장이 포함되어 있으면 pg_upgrade는 PGDATA 안에 update_extensions.sql 스크립트를 생성해요. 그 스크립트가 적용될 때까지 pg_catalog의 SQL 레벨 확장 메타데이터는 실행 중인 서버가 실제로 로드한 공유 라이브러리보다 뒤처져 있어요.

스크립트는 primary 파드의 PGDATA 안에 있어요. 데이터베이스 슈퍼유저(postgres)로 이를 실행해 모든 데이터베이스에서 그 확장들을 업데이트할 수 있어요:

PRIMARY=$(kubectl get cluster <name> -o jsonpath='{.status.currentPrimary}')
SCRIPT=/var/lib/postgresql/data/pgdata/update_extensions.sql

kubectl exec -i "$PRIMARY" -- psql -f "$SCRIPT"

:::note 위 스크립트 경로는 CloudNativePG 오퍼랜드 이미지가 사용하는 기본 PGDATA 위치를 가정해요. 오퍼레이터는 pg_upgrade가 스크립트를 생성할 때 해결된 경로도 기록하므로, 오퍼랜드 이미지가 다른 PGDATA를 사용한다면 오퍼레이터 로그를 확인하세요. :::

백업 및 WAL 아카이브 고려사항 (Backup and WAL Archive Considerations)

메이저 업그레이드를 수행하면 pg_upgrade는 새로운 System ID를 가진 새 데이터베이스 시스템을 만들고 PostgreSQL 타임라인을 1로 재설정해요. 이는 백업과 WAL 아카이빙에 영향을 줘요:

  • 타임라인 파일 충돌: 새 타임라인 1 파일이 원래 클러스터의 타임라인 1 파일을 덮어쓸 수 있어요.
  • 혼합 버전 아카이브: 조치가 없으면 아카이브에는 두 PostgreSQL 버전의 WAL 파일과 백업이 모두 포함돼요.

:::warning 시점 복구(PITR)는 PostgreSQL 메이저 버전 경계를 넘어 지원되지 않아요. 업그레이드 전 백업으로 업그레이드 이후의 시점으로 복구할 수 없어요. 업그레이드 직후 가능한 한 빨리 새 베이스 백업을 받아 새 메이저 버전의 복구 기준선을 수립하세요. :::

백업 시스템이 메이저 업그레이드를 처리하는 방법은 플러그인 구현에 따라 달라져요. 일부 플러그인은 업그레이드 중 아카이브 분리를 자동으로 관리하지만, 일부는 각 메이저 버전에 다른 아카이브 경로를 사용하도록 수동 구성이 필요해요. 메이저 업그레이드 중 특정 동작은 백업 플러그인 문서를 참고하세요.

예시: Barman Cloud 플러그인으로 수동 아카이브 경로 분리 (Example: Manual archive path separation with the Barman Cloud plugin)

Barman Cloud 플러그인은 메이저 업그레이드 중 아카이브를 자동으로 분리하지 않아요. 업그레이드 전 백업을 보존하고 아카이브를 깨끗하게 유지하려면 업그레이드를 트리거할 때 serverName 파라미터를 변경하세요.

업그레이드 전 (PostgreSQL 16):

spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:16-minimal-trixie
  plugins:
    - name: plugin-barman-cloud
      enabled: true
      parameters:
        destinationPath: s3://my-bucket/
        serverName: cluster-example-pg16

업그레이드를 트리거하려면 imageName과 serverName을 함께 변경하세요:

spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie
  plugins:
    - name: plugin-barman-cloud
      enabled: true
      parameters:
        destinationPath: s3://my-bucket/
        serverName: cluster-example-pg18

이 구성에서는 cluster-example-pg16의 이전 아카이브가 업그레이드 전 복구를 위해 그대로 유지되고, 업그레이드된 클러스터는 cluster-example-pg18에 기록해요.

:::info deprecated된 in-tree barmanObjectStore 구현도 메이저 업그레이드 중 아카이브를 분리하려면 수동 serverName 변경이 필요해요. :::

예시: 메이저 업그레이드 수행 (Example: Performing a Major Upgrade)

버전 16을 실행 중인 다음 PostgreSQL 클러스터를 고려해 보세요:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:16-minimal-trixie
  instances: 3
  storage:
    size: 1Gi

다음 명령으로 현재 PostgreSQL 버전을 확인할 수 있어요:

kubectl cnpg psql cluster-example -- -qAt -c 'SELECT version()'

다음과 비슷한 출력이 반환돼요:

PostgreSQL 16.x ...

클러스터를 버전 18로 업그레이드하려면 메이저 버전 태그를 16에서 18로 바꿔 imageName 필드를 업데이트하세요:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie
  instances: 3
  storage:
    size: 1Gi

업그레이드 프로세스 (Upgrade Process)

  1. 클러스터 종료 – 일관된 업그레이드를 보장하기 위해 모든 클러스터 파드가 종료돼요.
  2. 업그레이드 잡 실행 – primary 파드의 이름에 접미사 -major-upgrade가 붙은 이름으로 새 잡이 생성돼요. 이 잡은 primary의 영구 볼륨 그룹에서 pg_upgrade를 실행해요.
  3. 업그레이드 후 단계:
    • 레플리카(cluster-example-2 및 cluster-example-3)의 PVC 그룹이 제거돼요.
    • primary 파드가 재시작돼요.
    • 두 개의 새 레플리카(cluster-example-2 및 cluster-example-3)가 업그레이드된 primary에서 재복제돼요.

업그레이드가 완료되면 같은 명령을 실행해 새 메이저 버전을 확인할 수 있어요:

kubectl cnpg psql cluster-example -- -qAt -c 'SELECT version()'

이제 다음처럼 반환될 거예요:

PostgreSQL 18.x ...

이제 app 데이터베이스에서 ANALYZE를 실행해 통계를 업데이트할 수 있어요:

kubectl cnpg psql cluster-example -- app -c 'ANALYZE'