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

PostgreSQL 데이터베이스 관리

원문 보기 위키 갱신

PostgreSQL 데이터베이스 관리 (PostgreSQL Database management)

CloudNativePG는 기본적으로 app이라는 애플리케이션 데이터베이스를 자동 생성해서 PostgreSQL 데이터베이스 프로비저닝을 단순화해요. 이 기본 동작은 “빈 클러스터 부트스트랩(initdb)” 섹션에 설명되어 있어요.

더 고급 사용 사례를 위해 CloudNativePG는 선언적 데이터베이스 관리(declarative database management) 를 도입했어요. 이를 통해 사용자는 Database CRD(Custom Resource Definition)를 사용해 PostgreSQL 데이터베이스의 수명 주기를 정의하고 제어할 수 있어요. 이 방식은 Kubernetes와 매끄럽게 통합되어, PostgreSQL 데이터베이스를 관리하는 확장 가능하고 자동화되며 일관된 접근 방식을 제공해요.

출처: 문서

본문

핵심 개념 (Key Concepts)

관리 범위 (Scope of Management)

:::info[Important] CloudNativePG는 PostgreSQL 클러스터의 전역 객체(global objects) — 데이터베이스, 역할, 테이블스페이스 — 를 관리해요. 그러나 확장(extensions)과 스키마를 제외한 데이터베이스 콘텐츠(예: 테이블)는 관리하지 않아요. 데이터베이스 콘텐츠를 관리하려면 전문 도구를 사용하거나 애플리케이션에 맡겨야 해요. :::

선언적 Database 매니페스트

다음 예시는 Database 리소스가 Cluster와 어떻게 상호작용하는지 보여줘요:

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: cluster-example-one
spec:
  name: one
  owner: app
  cluster:
    name: cluster-example
  extensions:
    - name: bloom
      ensure: present

이 매니페스트를 적용하면 cluster-example-one이라는 Database 객체가 생성되어, cluster-example PostgreSQL 클러스터에 app 역할이 소유한 one이라는 데이터베이스를 요청하게 돼요.

:::info 각 Database 객체에 정의할 수 있는 전체 속성 목록은 API 레퍼런스를 참조해주세요. :::

Database 매니페스트의 필수 필드

  • metadata.name: 네임스페이스 내 Kubernetes 객체의 고유 이름
  • spec.name: PostgreSQL에 나타날 데이터베이스 이름
  • spec.owner: 데이터베이스를 소유하는 PostgreSQL 역할
  • spec.cluster.name: 대상 PostgreSQL 클러스터 이름

Database 객체는 특정 Cluster를 참조해야 하며, 이를 통해 데이터베이스가 생성될 위치가 결정돼요. 데이터베이스는 클러스터의 프라이머리 인스턴스에 의해 관리되며, 필요에 따라 생성되거나 갱신되도록 보장돼요.

:::warning spec.cluster 필드는 생성 후 불변(immutable)이에요. 다른 Cluster를 대상으로 하려면 기존 Database 리소스를 갱신하는 대신 새 Database 리소스를 만들어야 해요. :::

:::info metadata.name과 spec.name의 구분 덕분에, 같은 Kubernetes 네임스페이스 안의 서로 다른 CloudNativePG 클러스터에 같은 이름의 데이터베이스를 참조하는 여러 Database 리소스를 만들 수 있어요. :::

예약된 데이터베이스 이름 (Reserved Database Names)

PostgreSQL은 postgres, template0, template1 같은 데이터베이스를 자동으로 만들어요. 이 이름들은 예약되어 있으며, CloudNativePG에서 새 Database 객체에 사용할 수 없어요.

:::info[Important] spec.name이 postgres, template0, template1로 설정된 Database를 만드는 것은 허용되지 않아요. :::

리컨실레이션과 상태 (Reconciliation and Status)

Database 객체가 성공적으로 리컨실되면:

  • status.applied가 true로 설정돼요.
  • status.observedGeneration이 마지막으로 적용된 구성의 metadata.generation과 일치해요.

리컨실된 Database 객체 예시:

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  generation: 1
  name: cluster-example-one
spec:
  cluster:
    name: cluster-example
  name: one
  owner: app
status:
  observedGeneration: 1
  applied: true

리컨실레이션 중 오류가 발생하면 status.applied는 false가 되고, 오류 메시지가 status.message 필드에 포함돼요.

데이터베이스 삭제 (Deleting a Database)

CloudNativePG는 데이터베이스 삭제를 위한 두 가지 방법을 지원해요:

  1. delete 회수 정책(reclaim policy) 사용
  2. 데이터베이스의 ensure 필드를 absent로 선언적으로 설정

delete 회수 정책으로 삭제

databaseReclaimPolicy 필드는 Database 객체가 삭제될 때의 동작을 결정해요:

  • retain (기본값): 데이터베이스가 PostgreSQL에 남아 수동 관리를 받아요.
  • delete: 데이터베이스가 PostgreSQL에서 자동으로 제거돼요.

예시:

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: cluster-example-two
spec:
  databaseReclaimPolicy: delete
  name: two
  owner: app
  cluster:
    name: cluster-example

이 Database 객체를 삭제하면 cluster-example 클러스터에서 two 데이터베이스가 자동으로 제거돼요.

레플리카 클러스터에서는 데이터베이스가 읽기 전용이라, Database 객체를 삭제하면 databaseReclaimPolicy: delete로 설정돼 있더라도 파이널라이저를 해제하고 Kubernetes 객체만 제거할 뿐 PostgreSQL 데이터베이스는 드롭하지 않아요. 데이터베이스 드롭은 이를 소유한 프라이머리 클러스터에 맡겨져요.

선언적으로 ensure: absent 설정

데이터베이스를 제거하려면 다음 예시처럼 ensure 필드를 absent로 설정해요:

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: cluster-example-database-to-drop
spec:
  cluster:
    name: cluster-example
  name: database-to-drop
  owner: app
  ensure: absent

이 매니페스트는 database-to-drop 데이터베이스가 cluster-example 클러스터에서 제거되도록 보장해요.

데이터베이스에서 확장(extensions) 관리

:::info 확장은 전역 객체가 아니라 데이터베이스 범위 객체이지만, CloudNativePG는 이를 관리하는 선언적 인터페이스를 제공해요. 이 방식이 필요한 이유는 일부 확장을 설치하려면 슈퍼유저 권한이 필요한데, CloudNativePG는 기본적으로 슈퍼유저 권한을 비활성화할 것을 권장하기 때문이에요. 이 API를 활용하면 높은 권한 없이도 확장을 확장 가능하고 통제된 방식으로 효율적으로 관리할 수 있어요. :::

CloudNativePG는 대상 데이터베이스 안에서 PostgreSQL 확장 관리를 단순화하고 자동화해요.

이 기능을 활성화하려면 다음 예시처럼 확장 스펙 목록과 함께 spec.extensions 필드를 정의해요:

# ...
spec:
  extensions:
    - name: bloom
      ensure: present
# ...

각 확장 항목은 다음 속성을 지원해요:

  • name (필수): 확장의 이름
  • ensure: 확장이 데이터베이스에 있어야 할지(present) 없어야 할지(absent) 지정:
    • present: 확장이 설치되도록 보장 (기본값)
    • absent: 확장이 제거되도록 보장
  • version: 설치하거나 업그레이드할 확장의 특정 버전
  • schema: 확장이 설치되어야 할 스키마

:::info CloudNativePG는 PostgreSQL의 다음 SQL 명령으로 확장을 관리해요: CREATE EXTENSION, DROP EXTENSION, ALTER EXTENSION (UPDATE TO와 SET SCHEMA로 한정). :::

연산자는 spec.extensions에 명시적으로 나열된 확장만 리컨실해요. 이 목록에 없는 기존 확장은 변경되지 않은 채로 남아요.

:::warning 선언적 확장 관리가 도입되기 전에는 CloudNativePG가 구성을 통해 확장을 만드는 간단한 방법을 제공하지 않았어요. 이를 해결하기 위해 pg_stat_statements 같은 핵심 확장의 자동화되고 투명한 관리를 가능하게 하는 “managed extensions” 기능이 도입됐어요. 현재 Database CRD의 확장 지원과 managed extensions 기능 사이에 충돌이 없도록 보장하는 것은 사용자의 책임이에요. :::

데이터베이스에서 스키마 관리

:::info PostgreSQL에서의 스키마 관리는 CloudNativePG가 전역 객체 관리에 집중하는 원칙의 예외예요. 스키마는 데이터베이스 안에 존재하므로 일반적으로 애플리케이션 개발 과정의 일부로 관리돼요. 그러나 CloudNativePG는 주로 스키마 안에서의 확장 배포 지원을 완성하기 위해 스키마 관리용 선언적 인터페이스를 제공해요. :::

CloudNativePG는 대상 데이터베이스 안에서 PostgreSQL 스키마 관리를 단순화하고 자동화해요.

이 기능을 활성화하려면 다음 예시처럼 스키마 스펙 목록과 함께 spec.schemas 필드를 정의해요:

# ...
spec:
  schemas:
    - name: app
      owner: app
# ...

각 스키마 항목은 다음 속성을 지원해요:

  • name (필수): 스키마의 이름
  • owner: 스키마의 소유자
  • ensure: 스키마가 데이터베이스에 있어야 할지(present) 없어야 할지(absent) 지정:
    • present: 스키마가 설치되도록 보장 (기본값)
    • absent: 스키마가 제거되도록 보장

:::info CloudNativePG는 PostgreSQL의 다음 SQL 명령으로 스키마를 관리해요: CREATE SCHEMA, DROP SCHEMA, ALTER SCHEMA. :::

데이터베이스에서 FDW(Foreign Data Wrappers) 관리

:::info FDW(Foreign Data Wrappers)는 데이터베이스 범위 객체로, 만들거나 수정하려면 보통 슈퍼유저 권한이 필요해요. CloudNativePG는 FDW 관리를 위한 선언적 API를 제공해, 사용자가 SQL 명령을 직접 실행하거나 권한을 높이지 않고도 Kubernetes 네이티브 방식으로 통제된 상태로 정의하고 유지할 수 있게 해줘요. :::

CloudNativePG는 선언적 구성으로 대상 데이터베이스 안의 PostgreSQL 외부 데이터 래퍼(FDW)를 매끄럽고 자동화된 방식으로 관리할 수 있게 해줘요.

이 기능을 활성화하려면 다음 예시처럼 FDW 스펙 목록과 함께 spec.fdws 필드를 정의해요:

# ...
spec:
  fdws:
    - name: postgres_fdw
      usage:
        - name: app
          type: grant
# ...

각 FDW 항목은 다음 속성을 지원해요:

  • name: 외부 데이터 래퍼의 이름 (필수)
  • ensure: FDW가 데이터베이스에 present여야 할지 absent여야 할지 나타냄 (기본값은 present)
  • handler: FDW가 사용하는 핸들러 함수의 이름. 지정하지 않으면 FDW 확장이 정의한 기본 핸들러(있는 경우)를 사용해요.
  • validator: FDW가 사용하는 validator 함수의 이름. 지정하지 않으면 FDW 확장이 정의한 기본 validator(있는 경우)를 사용해요.
  • owner: FDW의 소유자 (슈퍼유저여야 함)
  • usage: FDW의 USAGE 권한 목록, 다음 필드를 가짐:
    • name: usage 권한을 부여하거나 회수할 역할의 이름 (필수)
    • type: usage 권한의 유형. grant와 revoke를 지원해요.
  • options: 관리할 FDW 전용 옵션의 맵, 각 키는 옵션 이름. 각 옵션은 다음 필드를 지원해요:
    • value: 옵션의 문자열 값
    • ensure: 옵션을 present로 할지 absent로 할지 나타냄

:::info handler와 validator는 모두 선택 사항이며, 지정하지 않으면 FDW 확장이 정의한 기본 핸들러와 validator(있는 경우)를 사용해요. handler나 validator를 "-"로 설정하면 각각 핸들러나 validator를 FDW에서 제거해요. 이는 PostgreSQL 관례, 즉 "-"가 핸들러나 validator의 부재를 나타낸다는 규칙을 따르는 거예요. :::

:::warning PostgreSQL은 외부 데이터 래퍼의 소유권을 슈퍼유저 권한을 가진 역할로만 제한해요. 비슈퍼유저(예: app 역할)에게 소유권을 부여하려 하면, PostgreSQL이 외부 데이터 래퍼의 비슈퍼유저 소유를 허용하지 않으므로 무시되거나 거부돼요. 기본적으로 postgres 사용자가 이 래퍼를 소유해요. :::

연산자는 spec.fdws에 명시적으로 나열된 FDW만 리컨실해요. 이 목록에 선언되지 않은 기존 FDW는 손대지 않고 남겨둬요.

:::info CloudNativePG는 PostgreSQL의 네이티브 SQL 명령으로 FDW를 관리해요: CREATE FOREIGN DATA WRAPPER, ALTER FOREIGN DATA WRAPPER, 그리고 DROP FOREIGN DATA WRAPPER. ALTER 명령은 옵션 갱신을 지원해요. :::

데이터베이스에서 외부 서버(Foreign Servers) 관리

CloudNativePG는 선언적 구성으로 대상 데이터베이스 안의 PostgreSQL 외부 서버를 매끄럽고 자동화된 방식으로 관리해요.

외부 서버(foreign server) 는 외부 데이터 래퍼(FDW)가 외부 데이터 소스에 접근할 때 사용하는 연결 정보를 담아요. 사용자별 연결 정보는 user mappings로 정의할 수 있어요.

:::info[Important] CloudNativePG는 현재 user mappings의 선언적 구성을 지원하지 않아요. 그러나 FDW와 그 외부 서버를 정의한 뒤에는 표준 데이터베이스 역할에 usage 권한을 부여할 수 있어요. 이렇게 하면 슈퍼유저 권한 없이도 user mappings를 SQL 스키마의 일부로 관리할 수 있어요. :::

이 기능을 활성화하려면 Database 리소스에 spec.servers 필드를 외부 서버 스펙 목록과 함께 선언해요, 예를 들어:

# ...
spec:
  servers:
    - name: angus
      fdw: postgres_fdw
      ensure: present
      usage:
        - name: app
          type: grant
      options:
        - name: host
          value: angus-rw
        - name: dbname
          value: app
# ...

각 외부 서버 항목은 다음 속성을 지원해요:

  • name: 외부 서버의 이름 (필수)
  • fdw: 서버가 속한 외부 데이터 래퍼의 이름 (필수)
  • ensure: 외부 서버가 데이터베이스에 present여야 할지 absent여야 할지 (기본값: present)
  • usage: 외부 서버의 USAGE 권한 목록, 다음 필드를 가짐:
    • name: usage 권한을 부여하거나 회수할 역할의 이름 (필수)
    • type: usage 권한의 유형. grant와 revoke를 지원해요.
  • options: FDW 전용 옵션 스펙 목록. 목록의 각 항목은 다음 키를 지원해요:
    • name: 옵션의 이름 (필수)
    • value: 옵션의 문자열 값
    • ensure: 옵션을 present로 할지 absent로 할지 나타냄

:::info[Important] fdw 필드는 데이터베이스에 이미 정의된 기존 외부 데이터 래퍼를 참조해야 해요. 지정된 FDW가 없으면 외부 서버는 생성되지 않아요. :::

CloudNativePG는 PostgreSQL의 네이티브 SQL 명령으로 외부 서버를 관리해요: CREATE SERVER, ALTER SERVER, 그리고 DROP SERVER. ALTER SERVER 명령은 서버 옵션을 갱신하는 데 사용돼요.

연산자는 spec.servers에 명시적으로 나열된 외부 서버 만 리컨실해요. 이 목록에 포함되지 않은 기존 서버는 변경되지 않은 채로 남아요.

제한사항과 주의사항 (Limitations and Caveats)

데이터베이스 이름 바꾸기 (Renaming a database)

CloudNativePG는 PostgreSQL의 CREATE DATABASE와 ALTER DATABASE 명령을 따르지만, 데이터베이스 이름 바꾸기는 지원하지 않아요. 기존 Database 객체에서 spec.name을 수정하려 하면 Kubernetes가 거부해요.

생성과 변경 (Creating vs. Altering a Database)

  • 새 데이터베이스에는 CREATE DATABASE 문을 사용해요.
  • 기존 데이터베이스에는 변경을 적용하기 위해 ALTER DATABASE를 사용해요.

이 두 Postgres 명령 사이에는 차이가 있다는 점을 알아두는 게 중요해요. 특히 ALTER가 허용하는 옵션은 CREATE가 허용하는 옵션의 부분집합이에요.

:::warning 인코딩, 콜레이션 설정 같은 일부 필드는 PostgreSQL에서 불변(immutable)이에요. 기존 데이터베이스에서 이 필드를 수정하려는 시도는 무시돼요. :::

레플리카 클러스터 (Replica Clusters)

레플리카 클러스터에 선언된 데이터베이스 객체는, 레플리카에 쓰기 권한이 없으므로 강제할 수 없어요. 이 객체들은 레플리카가 승격될 때까지 pending 상태로 남아요.

충돌 해결 (Conflict Resolution)

같은 네임스페이스의 두 Database 객체가 같은 PostgreSQL 데이터베이스(즉, 동일한 spec.name과 spec.cluster.name)를 관리하면, 두 번째 객체는 거부돼요.

상태 메시지 예시:

status:
  applied: false
  message: 'reconciliation error: database "one" is already managed by Database object "cluster-example-one"'

Postgres 버전 차이 (Postgres Version Differences)

CloudNativePG는 PostgreSQL의 기능을 따릅니다. 예를 들어 PostgreSQL 16에서 도입된 ICU_RULES 같은 기능은 이전 버전에서 사용할 수 없어요. PostgreSQL에서 오는 오류는 Database 객체의 status에 반영돼요.

수동 변경 (Manual Changes)

CloudNativePG는 데이터베이스에 대한 수동 변경을 덮어쓰지 않아요. 일단 리컨실된 Database 객체는 metadata.generation이 바뀌지 않는 한 재적용되지 않으므로, PostgreSQL을 직접 수정할 수 있는 유연성을 제공해요.

더 알아보기 (Learn more)