본문으로 건너뛰기
9.2 Role 관리(선언적)

9.2 Role 관리(선언적)

PostgreSQL의 role은 접속과 권한의 주체다. 전통적으로는 CREATE ROLE·ALTER ROLE·DROP ROLE을 손으로 실행해 관리하지만, CloudNativePG에서는 Cluster CRD의 .spec.managed.roles에 원하는 role의 상태를 적어 두면 Operator가 catalog와 주기적으로 비교해 그 상태로 맞춘다. role의 생성·수정·삭제가 매니페스트에 코드로 남으므로, 누가 어떤 권한을 가졌는지가 Git 이력에 그대로 드러난다.

managed.roles 구조

role은 .spec.managed.roles 아래에 리스트로 선언한다. 각 항목은 PostgreSQL의 role 속성을 그대로 필드로 옮긴 형태다.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg
spec:
  managed:
    roles:
      - name: dante
        ensure: present
        comment: application role
        login: true
        superuser: false
        createdb: false
        createrole: false
        inherit: true
        connectionLimit: 10
        inRoles:
          - pg_monitor
          - pg_signal_backend
        passwordSecret:
          name: pg-dante

주요 필드는 다음과 같다.

필드의미기본값
namerole 이름 (필수)
ensurepresent(생성·유지) 또는 absent(삭제)present
login로그인 가능 여부false
superusersuperuser 권한false
createdbdatabase 생성 권한false
createrolerole 생성 권한false
inherit소속 role의 권한 상속true
connectionLimit최대 동시 접속 수 (-1은 무제한)-1
validUntil비밀번호 만료 시각없음(무기한)
inRoles소속시킬 role 그룹 목록빈 목록
passwordSecret비밀번호를 담은 Secret 참조선택
disablePassword비밀번호를 NULL로 설정false

조정 흐름

DBA가 managed.roles를 수정하면 Operator가 이를 PostgreSQL catalog와 비교해 부족한 것은 만들고, 다른 것은 고친다.

    flowchart TD
  DBA["DBA"] -->|edit managed.roles| CR["Cluster CRD"]
  OP["Operator"] -->|주기 비교| CR
  OP -->|현재 상태 조회| CAT["pg_authid<br/>(catalog)"]
  OP --> DEC{"ensure 값?"}
  DEC -->|present| UPSERT["CREATE·ALTER ROLE"]
  DEC -->|absent| DROP["DROP ROLE"]

  classDef node fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
  class DBA,CR,OP,CAT,UPSERT,DROP node
  
선언적 role 관리는 매니페스트에 적히지 않은 role은 건드리지 않는다. 그래서 기존 클러스터에 이 기능을 도입할 때 한꺼번에 모든 role을 옮길 필요 없이, 관리 대상을 하나씩 늘려 가며 점진적으로 적용할 수 있다.

비밀번호 관리

role의 비밀번호는 매니페스트에 평문으로 적지 않고, kubernetes.io/basic-auth 타입의 Secret을 참조한다.

apiVersion: v1
kind: Secret
type: kubernetes.io/basic-auth
metadata:
  name: pg-dante
  labels:
    cnpg.io/reload: "true"
data:
  username: ZGFudGU=
  password: <base64-encoded-password>

Operator는 기본적으로 비밀번호를 클라이언트 측에서 SCRAM-SHA-256으로 해시한 뒤 PostgreSQL에 보낸다. 그래서 평문이 서버 로그에 남지 않는다. cnpg.io/reload: "true" label이 붙은 Secret은 비밀번호 변경이 즉시 반영되고, 없으면 다음 조정 주기에 반영된다.

  • validUntil — 비밀번호 만료 시각을 지정한다. 생략하면 Operator가 만료되지 않도록(VALID UNTIL 'infinity') 유지한다.
  • disablePassword: true — 비밀번호를 NULL로 만든다. passwordSecret과 함께 쓸 수 없다.

제약과 주의점

선언적 관리라 해도 PostgreSQL 자체의 규칙은 그대로 적용된다.

  • 삭제 제약 — role이 객체를 소유하고 있으면 DROP ROLE이 실패한다. Operator는 소유 객체를 자동으로 지우지 않으므로, DBA가 먼저 소유권을 옮기거나 객체를 정리해야 한다.
  • 실현 불가능한 설정 — 존재하지 않는 그룹에 소속시키는 등 PostgreSQL이 거부하는 요청은 조정에 실패한다. 이런 role은 아래 status의 cannotReconcile에 나타나며 사람의 개입을 요구한다.
  • 예약 role — postgres, streaming_replica, cnpg_pooler_pgbouncer는 Operator가 관리하는 예약 role이라 선언적 관리로 삭제할 수 없다.

status로 상태 확인

Cluster의 .status.managedRolesStatus에서 각 role의 조정 결과를 확인할 수 있다.

status:
  managedRolesStatus:
    byStatus:
      reconciled:
        - dante
      reserved:
        - postgres
        - streaming_replica
    cannotReconcile:
      petrarca:
        - 'could not perform UPDATE_MEMBERSHIPS: role "poets" does not exist'
  • reconciled — 매니페스트대로 반영된 role
  • reserved — Operator가 관리하는 시스템 role
  • cannotReconcile — 해결되지 않은 오류로 개입이 필요한 role
cannotReconcile에 role이 남아 있다면 그 role의 상태는 매니페스트와 다르다는 뜻이다. 메시지에 적힌 원인(위 예시에서는 존재하지 않는 poets 그룹)을 먼저 해소해야 조정이 완료된다.

정리

  • role은 .spec.managed.roles에 리스트로 선언. Operator가 catalog와 주기적으로 비교
  • ensure: present는 생성·유지, absent는 삭제
  • 매니페스트에 없는 role은 건드리지 않아 점진적 도입이 가능
  • 비밀번호는 basic-auth Secret으로 참조, 기본은 SCRAM-SHA-256 해시로 전달
  • 객체를 소유한 role은 삭제 실패, 예약 role은 관리 대상에서 제외
  • 조정 결과는 .status.managedRolesStatusreconciled·cannotReconcile로 확인

다음 절(9.3)에서는 role이 접속할 대상인 database를 선언적으로 관리하는 Database CRD를 본다.