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

Postgres 데이터베이스 가져오기

원문 보기 위키 갱신

Postgres 데이터베이스 가져오기 (Importing Postgres databases)

이 섹션에서는 기존 PostgreSQL 데이터베이스 하나 이상을 새 CloudNativePG 클러스터 안으로 가져오는 방법을 설명해요.

가져오기(import) 작업은 PostgreSQL의 온라인 논리 백업 개념에 기반을 두고, 원본 호스트로의 네트워크 연결을 통한 pg_dump와 pg_restore에 의존해요. PostgreSQL은 네이티브 MVCC(Multi-Version Concurrency Control)와 스냅샷 덕분에, 쓰기 활동을 중단하지 않고도 네트워크를 통해 일관된 백업을 동시에 수행할 수 있어요.

논리 백업은 또한 PostgreSQL 메이저 업그레이드를 수행하는 가장 일반적이고 유연하며 신뢰할 수 있는 기법이기도 해요.

따라서 이 섹션의 지침은 다음 두 경우 모두에 적합해요:

  • Kubernetes 밖에 있더라도, 기존 PostgreSQL 인스턴스에서 데이터베이스 하나 이상을 가져오기
  • 어떤 PostgreSQL 버전에서든 동일하거나 더 새로운 버전으로 데이터베이스를 가져오기 (즉 PostgreSQL의 메이저 업그레이드 수행, 예: 14.x에서 18.x로)

:::warning PostgreSQL 메이저 업그레이드를 수행할 때, 애플리케이션이 새 버전과 호환되는지, 그리고 데이터베이스에 포함된 객체(확장 포함)의 업그레이드 경로가 실현 가능한지는 사용자 책임이에요. :::

두 경우 모두, 작업은 원본 데이터베이스의 일관된 스냅샷에 대해 수행돼요.

:::info[Important] 따라서 최종적으로 Cluster 리소스로 가져오기 전에는 원본에 대한 쓰기 작업을 중단할 것을 권장해요. 백업 시작 이후 원본 데이터베이스에 생긴 변경은 대상 클러스터에 반영되지 않기 때문이에요. 그래서 이 기능을 “오프라인 가져오기(offline import)” 또는 “오프라인 메이저 업그레이드(offline major upgrade)”라고 부르는 거예요. :::

출처: 문서

본문

어떻게 동작하나요 (How it works)

개념적으로 가져오기는 initdb 부트스트랩 방식을 사용해 처음부터 새 클러스터(대상 클러스터)를 만든 다음, initdb.import 하위 섹션을 채워 기존 Postgres 클러스터(원본 클러스터)에서 객체를 가져오는 방식이에요. PostgreSQL 권장사항에 따라, 대상 클러스터의 PostgreSQL 메이저 버전이 원본 클러스터의 버전보다 크거나 같도록 권장해요.

CloudNativePG는 원본 클러스터에서 대상 클러스터로 객체를 가져오는 두 가지 주요 방식을 제공해요:

  • 마이크로서비스 방식(microservice approach): 대상 클러스터가 지정된 애플리케이션 사용자가 소유한 단일 애플리케이션 데이터베이스를 호스팅하도록 설계되는 방식으로, CloudNativePG 프로젝트가 권장해요
  • 모놀리스 방식(monolith approach): 대상 클러스터가 원본 클러스터에서 가져온 여러 데이터베이스와 다양한 사용자를 호스팅하도록 설계되는 방식

첫 번째 가져오기 방식은 microservice 타입으로, 두 번째는 monolith 타입으로 사용할 수 있어요.

:::warning 대상 클러스터가 원본 클러스터에, pg_dump로 논리 백업을 뜰 수 있을 만큼 충분한 권한을 가진 슈퍼유저 또는 사용자로 접근할 수 있도록 보장하는 것은 사용자의 책임이에요. 자세한 내용은 PostgreSQL pg_dump 문서를 참조해주세요. :::

microservice 타입

마이크로서비스 방식으로는, 원본 클러스터에서 대상 클러스터로 가져오려는 단일 데이터베이스를 지정할 수 있어요. 작업은 4단계로 수행돼요:

  • 새 클러스터의 initdb 부트스트랩
  • pg_dump -Fd를 사용해 선택한 데이터베이스(initdb.import.databases에 지정) 내보내기
  • pg_restore --no-acl --no-owner를 사용해 데이터베이스를 initdb.owner 사용자가 소유한 initdb.database(애플리케이션 데이터베이스)로 가져오기
  • 데이터베이스 덤프 파일 정리
  • postImportApplicationSQL 파라미터를 통해 애플리케이션 데이터베이스에서 사용자 정의 SQL 쿼리를 선택적으로 실행
  • 가져온 데이터베이스에 ANALYZE VERBOSE 실행

아래 그림에서 N개의 데이터베이스를 담고 있는 단일 PostgreSQL 클러스터가 여러 CloudNativePG 클러스터로 가져와지며, 각 클러스터는 N개의 원본 데이터베이스 중 하나에 대해 마이크로서비스 가져오기를 사용해요.

Example of microservice import type

예를 들어, 아래 YAML은 cluster-microservice라는 이름의 3인스턴스 PostgreSQL 클러스터(연산자가 릴리스된 시점의 최신 메이저 버전)를 만들면서, cluster-pg96-superuser 시크릿에 저장된 비밀번호로 postgres 사용자를 통해 postgres 데이터베이스에 연결해서 cluster-pg96 클러스터(더 이상 지원되지 않는 PostgreSQL 9.6)에서 angus 데이터베이스를 가져와요.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-microservice
spec:
  instances: 3

  bootstrap:
    initdb:
      import:
        type: microservice
        databases:
          - angus
        source:
          externalCluster: cluster-pg96
        #postImportApplicationSQL:
        #- |
        #  INSERT YOUR SQL QUERIES HERE
  storage:
    size: 1Gi
  externalClusters:
    - name: cluster-pg96
      connectionParameters:
        # Use the correct IP or host name for the source database
        host: pg96.local
        user: postgres
        dbname: postgres
      password:
        name: cluster-pg96-superuser
        key: password

:::warning 위 예시는 의도적으로 커뮤니티에서 더 이상 지원하지 않는(그래서 CloudNativePG도 지원하지 않는) PostgreSQL 버전으로 실행되는 원본 데이터베이스를 사용하는데, 이는 기존·지원되지 않는 Postgres 버전에서도 이 데이터 내보내기 방식이 동작해서, 레거시 데이터를 Kubernetes 안의 더 나은 시스템으로 옮길 기회를 준다는 것을 보여주기 위해서예요. 원본 인스턴스에서 데이터를 내보낼 때는 대상 클러스터의 pg_dump 버전을 사용하며, 이 버전은 지원되는 버전이어야 하고 원본 버전과 같거나 더 커야 해요. 우리 경험상 이 방식은 더 오래되고 지원되지 않는 Postgres 버전에서도 동작하며, 레거시 데이터를 Kubernetes 안의 더 나은 시스템으로 옮길 기회를 줘요. 이 섹션의 예시에서 9.6을 사용한 주요 이유가 바로 이것이에요. 이 영역에서 문제가 생기면 여러분의 의견을 듣고 싶어요. :::

microservice 타입을 사용할 때 알아둬야 할 몇 가지 사항이 있어요:

  • 가져올 데이터를 담고 있는 기존 PostgreSQL 인스턴스를 가리키는 externalCluster가 필요해요 (자세한 내용은 “externalClusters 섹션” 참조)
  • 작업 중에 Kubernetes 클러스터와 externalCluster 사이에 트래픽이 허용되어야 해요
  • pg_dump를 실행하고 역할(role) 정보를 읽을 수 있는 지정된 사용자로 원본 데이터베이스에 대한 연결이 허용되어야 해요 (superuser면 충분해요)
  • 현재, pg_dump -Fd 결과물은 PGDATA 볼륨 안의 dumps 폴더에 임시로 저장돼요. 따라서 할당된 노드에 덤프 결과물과 복원된 데이터·인덱스를 일시적으로 담을 수 있을 만큼 충분한 공간이 있어야 해요. 가져오기 작업이 완료되면 이 폴더는 연산자가 자동으로 삭제해요.
  • initdb.import.databases 배열에 단 하나의 데이터베이스만 지정할 수 있어요
  • 역할은 가져오지 않으므로 initdb.import.roles에 지정할 수 없어요

:::tip[Hint] 마이크로서비스 방식은 대상 클러스터에 대해 CloudNativePG의 관례와 기본값을 따릅니다. 대상 클러스터에 initdb.database나 initdb.owner를 설정하지 않으면 두 파라미터 모두 app으로 기본 설정돼요. :::

monolith 타입

모놀리스 방식으로는, 원본 클러스터에서 대상 클러스터로 가져오려는 역할들과 데이터베이스들의 묶음을 지정할 수 있어요. 작업은 다음 단계로 수행돼요:

  • 새 클러스터의 initdb 부트스트랩
  • 선택한 역할 내보내기·가져오기
  • pg_dump -Fd를 사용해 선택한 데이터베이스(initdb.import.databases에 지정)를 하나씩 내보내기
  • 선택한 각 데이터베이스를 만들고 pg_restore로 데이터 가져오기
  • 가져온 각 데이터베이스에 ANALYZE 실행
  • 데이터베이스 덤프 파일 정리

Example of monolith import type

예를 들어, 아래 YAML은 cluster-monolith라는 이름의 3인스턴스 PostgreSQL 클러스터(연산자가 릴리스된 시점의 최신 메이저 버전)를 만들면서, cluster-pg96-superuser 시크릿에 저장된 비밀번호로 postgres 사용자를 통해 postgres 데이터베이스에 연결해서 cluster-pg96 클러스터(더 이상 지원되지 않는 PostgreSQL 9.6)에서 accountant와 bank_user 역할, 그리고 accounting, banking, resort 데이터베이스를 가져와요.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-monolith
spec:
  instances: 3
  bootstrap:
    initdb:
      import:
        type: monolith
        databases:
          - accounting
          - banking
          - resort
        roles:
          - accountant
          - bank_user
        source:
          externalCluster: cluster-pg96
  storage:
    size: 1Gi
  externalClusters:
    - name: cluster-pg96
      connectionParameters:
        # Use the correct IP or host name for the source database
        host: pg96.local
        user: postgres
        dbname: postgres
        sslmode: require
      password:
        name: cluster-pg96-superuser
        key: password

monolith 타입을 사용할 때 알아둬야 할 몇 가지 사항이 있어요:

  • 가져올 데이터를 담고 있는 기존 PostgreSQL 인스턴스를 가리키는 externalCluster가 필요해요 (자세한 내용은 “externalClusters 섹션” 참조)
  • 작업 중에 Kubernetes 클러스터와 externalCluster 사이에 트래픽이 허용되어야 해요
  • pg_dump를 실행하고 역할 정보를 검색할 수 있는 지정된 사용자로 원본 데이터베이스에 대한 연결이 허용되어야 해요 (superuser면 충분해요)
  • 현재, pg_dump -Fd 결과물은 대상 클러스터 인스턴스의 PGDATA 볼륨 안의 dumps 폴더에 임시로 저장돼요. 따라서 할당된 노드에 덤프 결과물과 복원된 데이터·인덱스를 일시적으로 담을 수 있을 만큼 충분한 공간이 있어야 해요. 가져오기 작업이 완료되면 이 폴더는 연산자가 자동으로 삭제해요.
  • initdb.import.databases 배열에 최소한 하나의 데이터베이스를 지정해야 해요
  • 가져온 데이터베이스에 필요한 역할은 아래 제한사항과 함께 initdb.import.roles에 지정해야 해요:
    • 다음 역할은 존재하더라도 가져오지 않아요: postgres, streaming_replica, cnpg_pooler_pgbouncer
    • 가져오는 모든 역할에서 SUPERUSER 옵션이 제거돼요
  • 와일드카드 "*"를 databases 및/또는 roles 배열의 유일한 요소로 사용해서 해당 종류의 모든 객체를 가져올 수 있어요. 데이터베이스 매칭 시 와일드카드는 postgres 데이터베이스, 템플릿 데이터베이스, 그리고 연결이 허용되지 않는 데이터베이스는 무시해요
  • 클론 절차가 끝난 뒤 모든 데이터베이스에 대해 ANALYZE VERBOSE가 실행돼요.
  • postImportApplicationSQL 필드는 지원되지 않아요

:::tip[Hint] 데이터베이스와 그 소유자는 원본 클러스터에 존재하는 그대로 정확히 보존돼요—가져오기 중에 app 데이터베이스나 사용자가 생성되지 않아요. bootstrap.initdb 스탠자가 가져오는 데이터베이스나 사용자와 일치하지 않는 커스텀 database·owner 값을 지정하면, 인스턴스 매니저는 그 지정된 이름으로 새롭고 비어 있는 애플리케이션 데이터베이스와 소유자 역할을 만들되, 가져온 데이터베이스와 소유자는 그대로 둬요. :::

실용적인 예시 (A practical example)

monolith 방식을 사용해 단일 데이터베이스를 가져오는 것을 막을 이유는 없어요. 그렇게 했을 때 결과가 microservice 방식과 어떻게 달라지는지 보는 건 흥미로워요.

원본 클러스터가, 예를 들어 다음과 같다고 해볼게요. 역할 me가 소유한 mydb라는 데이터베이스가 있을 때:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  instances: 1

  postgresql:
    pg_hba:
      - host all all all trust

  storage:
    size: 1Gi

  bootstrap:
    initdb:
      database: mydb
      owner: me

microservice로 가져올 수 있어요:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example-microservice
spec:
  instances: 1
  
  storage:
    size: 1Gi

  bootstrap:
    initdb:
      import:
        type: microservice
        databases:
          - mydb
        source:
          externalCluster: cluster-example

  externalClusters:
    - name: cluster-example
      connectionParameters:
        host: cluster-example-rw
        dbname: postgres

그리고 monolith로도 가져올 수 있어요:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example-monolith
spec:
  instances: 1
  
  storage:
    size: 1Gi

  bootstrap:
    initdb:
      import:
        type: monolith
        databases:
          - mydb
        roles:
          - me
        source:
          externalCluster: cluster-example

  externalClusters:
    - name: cluster-example
      connectionParameters:
        host: cluster-example-rw
        dbname: postgres

두 경우 모두 데이터베이스 내용은 가져와지지만:

  • 마이크로서비스의 경우 가져온 데이터베이스의 이름과 소유자가 모두 app이 되거나, bootstrap.initdb 스탠자에 설정된 database·owner 필드 구성값이 됩니다.
  • 모놀리스의 경우 데이터베이스와 소유자가 원본 클러스터와 동일하게, 즉 각각 mydb와 me로 유지돼요. app 데이터베이스나 사용자는 생성되지 않아요. bootstrap.initdb 스탠자에 가져올 원본 데이터베이스/소유자와 일치하지 않는 database·owner 커스텀 설정이 있으면, 인스턴스 매니저는 새롭고 비어 있는 애플리케이션 데이터베이스와 소유자 역할을 만들지만 가져온 데이터베이스/소유자는 그대로 둬요.

가져오기 최적화 (Import optimizations)

데이터베이스의 논리적 가져오기 중에 CloudNativePG는 속도를 데이터 내구성보다 우선시하도록 PostgreSQL 구성을 최적화하며, 다음을 강제해요:

  • archive_mode → off
  • fsync → off
  • full_page_writes → off
  • max_wal_senders → 0
  • wal_level → minimal

가져오기 작업을 완료하기 전에 CloudNativePG는 기대되는 구성을 복원한 다음, 데이터가 디스크에 영구적으로 기록되도록 initdb --sync-only를 실행해요.

:::info[Important] 요청된 경우 WAL 아카이빙과 WAL 레벨은 데이터베이스 가져오기 과정이 완료된 이후에 반영돼요. 마찬가지로 레플리카는 실제 클러스터 리소스가 시작되는 부트스트랩 단계 이후에 클론돼요. :::

가져오기 단계 동안 할 수 있는 다른 최적화도 있어요. 이 주제는 CloudNativePG의 범위를 벗어나지만, shared_buffers, max_wal_size, checkpoint_timeout 같은 Postgres GUC들을 Cluster 구성에서 직접 튜닝해 체크포인트 영역의 불필요한 쓰기를 줄이는 것을 권장해요.

pg_dump와 pg_restore 동작 커스터마이징

pgDumpExtraOptions와 pgRestoreExtraOptions 파라미터로 추가 옵션을 지정해서 pg_dump와 pg_restore의 동작을 커스터마이징할 수 있어요. 특히 성능 향상이나 가져오기/내보내기 복잡성 관리에 유용해요.

예를 들어 병렬 작업을 활성화하면 데이터 전송을 크게 빨라지게 할 수 있어요:

bootstrap:
  initdb:
    import:
      type: microservice
      databases:
      - app
      source:
        externalCluster: cluster-example
      pgDumpExtraOptions:
      - '--jobs=2'
      pgRestoreExtraOptions:
      - '--jobs=2'

단계별 pg_restore 옵션 (Stage-Specific pg_restore options)

가져오기 과정을 더 세밀하게 제어하기 위해 CloudNativePG는 다음 단계에 대해 단계별 pg_restore 옵션을 지원해요:

  • pre-data – 예: 스키마 정의
  • data – 예: 테이블 내용
  • post-data – 예: 인덱스, 제약 조건, 트리거

각 단계에 대해 옵션을 지정하면 병렬 처리를 최적화하고, 복원되는 객체의 성격에 맞는 플래그를 적용할 수 있어요.

bootstrap:
  initdb:
    import:
      type: microservice
      schemaOnly: false
      databases:
        - mynewdb
      source:
        externalCluster: sourcedb-external
      pgRestorePredataOptions:
        - '--jobs=1'
      pgRestoreDataOptions:
        - '--jobs=4'
      pgRestorePostdataOptions:
        - '--jobs=2'

위 예시에서:

  • --jobs=1은 pre-data 단계에 적용되어 스키마 생성 순서를 보존해요.
  • --jobs=4는 data 단계 동안 병렬 처리를 증가시켜 대규모 데이터 가져오기를 빠르게 해요.
  • --jobs=2는 post-data 단계에서 성능과 의존성 처리를 균형 있게 해요.

이런 단계별 설정은 대규모 데이터베이스나 동시성을 튜닝하면 성능이 크게 개선되는 리소스에 민감한 환경에서 특히 유용해요.

:::note 단계별 옵션이 제공되면 일반 pgRestoreExtraOptions보다 우선해요. :::

:::warning pgDumpExtraOptions, pgRestoreExtraOptions, 그리고 모든 단계별 복원 옵션(pgRestorePredataOptions, pgRestoreDataOptions, pgRestorePostdataOptions)은 연산자의 검증 없이 기본 PostgreSQL 도구에 직접 전달돼요. 일부 플래그는 연산자의 의도된 기능이나 설계와 충돌할 수 있어요. 이 옵션들은 주의해서 사용하고, 운영 환경에 적용하기 전에 항상 안전하고 통제된 환경에서 철저히 테스트해주세요. :::

온라인 가져오기와 업그레이드 (Online Import and Upgrades)

논리 복제(Logical replication)는 다음 방식으로 네트워크를 통해 접근 가능한 모든 PostgreSQL 데이터베이스를 가져오는 강력한 방법을 제공해요:

  • 스키마 전용 옵션을 사용한 가져오기 부트스트랩(Import Bootstrap with Schema-Only Option): 복제가 시작되기 전에 대상 데이터베이스에 스키마를 초기화해요.
  • Subscription 리소스: 데이터 변경을 동기화하는 연속 복제를 설정해요.

이 기법은 최소 다운타임으로 PostgreSQL 메이저 업그레이드를 수행하는 데에도 활용할 수 있어, 원활한 마이그레이션과 시스템 업그레이드에 이상적이에요.

제한사항과 모범 사례를 포함한 자세한 내용은 문서의 논리 복제(Logical Replication) 섹션을 참조해주세요.

더 알아보기 (Learn more)