CNPG-I
CloudNativePG Interface(이하 CNPG-I)는 CloudNativePG의 코어 코드베이스를 수정하지 않고도 확장하고 커스터마이즈할 수 있는 표준적인 방법이에요. gRPC 기반 확장 지점과 플러그인 등록 방식에 대해 살펴볼게요.
출처: 문서
본문
CloudNativePG Interface(CNPG-I)는 CloudNativePG의 코어 코드베이스를 수정하지 않고 확장하고 커스터마이즈하는 표준 방법이에요.
왜 CNPG-I인가요?
CloudNativePG는 광범위한 사용 사례를 지원하지만, 때로는 내장 기능만으로 부족하거나 특정 기능을 메인 프로젝트에 직접 추가하는 것이 실용적이지 않을 수 있어요.
CNPG-I 이전에는 사용자에게 두 가지 주요 옵션이 있었어요:
- 커스텀 동작을 추가하기 위해 프로젝트를 포크(Fork)하거나,
- 상위 코드베이스 위에 커스텀 컴포넌트를 작성해 업스트림 코드베이스를 확장하는 것이에요.
두 접근 방식 모두 유지 관리 부담을 만들고, 업그레이드를 느리게 하며, 중요한 기능의 전달을 지연시켰어요.
CNPG-I는 클러스터 수명 주기의 핵심 지점(백업, 복구, 하위 리소스 정합성 조정 등)에서 CloudNativePG를 확장할 수 있는 안정적인 gRPC 기반 통합 지점을 제공해 이 문제를 해결해요. 그리고 코어 프로젝트를 방해하지 않아요.
CNPG-I는 다음을 확장할 수 있어요:
- 오퍼레이터, 그리고/또는
- PostgreSQL 파드 안에서 실행되는 인스턴스 매니저.
플러그인 등록하기
CNPG-I는 Kubernetes의 Container Storage Interface (CSI)에서 영감을 받았어요. 오퍼레이터는 CNPG-I 프로토콜을 따라 gRPC로 등록된 플러그인과 통신해요.
플러그인은 다음 두 가지 방법 중 하나로 등록할 수 있어요:
- 사이드카 컨테이너(Sidecar container) – 오퍼레이터의 Deployment 안에서 플러그인 실행
- 독립형 Deployment(Standalone Deployment) – 같은 네임스페이스에서 별도의 워크로드로 플러그인 실행
두 경우 모두 플러그인은 컨테이너 이미지로 패키징되어야 해요.
사이드카 컨테이너(Sidecar Container)
사이드카 플러그인은 오퍼레이터 시작 시 한 번 검색돼요.
플러그인은 gRPC 서버를 Unix domain socket으로 노출해야 해요. 이 소켓은 오퍼레이터 컨테이너와 공유되는 디렉터리에 배치되어야 하며, PLUGIN_SOCKET_DIR에 설정된 경로(기본값: /plugin)에 마운트돼야 해요.
예시:
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
spec:
template:
spec:
containers:
- image: cloudnative-pg:latest
[...]
name: manager
volumeMounts:
- mountPath: /plugins
name: cnpg-i-plugins
- image: cnpg-i-plugin-example:latest
name: cnpg-i-plugin-example
volumeMounts:
- mountPath: /plugins
name: cnpg-i-plugins
volumes:
- name: cnpg-i-plugins
emptyDir: {}
독립형 Deployment (권장)
플러그인을 자체 Deployment로 실행하면 그 수명 주기가 오퍼레이터와 분리되고 독립적인 확장이 가능해져요. 이 설정에서 플러그인은 Service 뒤에 TCP gRPC 엔드포인트를 노출하며, 보안 통신을 위해 mTLS를 사용해요. 독립형 플러그인은 필요한 레이블과 어노테이션이 있는 Service를 감시해 동적으로 검색되며, 오퍼레이터 재시작이 필요 없어요.
독립형 플러그인의 파드가 롤아웃되면(예: 이미지 업그레이드 후), 오퍼레이터는 플러그인 Service를 뒷받침하는 EndpointSlides를 통해 변경을 감지하고 해당 플러그인을 사용하는 모든 클러스터를 정합성 조정해, 주기적 리싱크를 기다리는 대신 새 파드와 즉시 상호작용을 시작해요.
Deployment 예시:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cnpg-i-plugin-example
spec:
template:
[...]
spec:
containers:
- name: cnpg-i-plugin-example
image: cnpg-i-plugin-example:latest
ports:
- containerPort: 9090
protocol: TCP
플러그인 관련 Service에는 다음이 포함되어야 해요:
- 레이블
cnpg.io/plugin: <plugin-name>— CloudNativePG가 플러그인을 검색하는 데 필요 - 어노테이션
cnpg.io/pluginPort: <port>— 플러그인의 gRPC 서버가 노출되는 포트를 지정
Service 예시:
apiVersion: v1
kind: Service
metadata:
annotations:
cnpg.io/pluginPort: "9090"
labels:
cnpg.io/pluginName: cnpg-i-plugin-example.my-org.io
name: cnpg-i-plugin-example
spec:
ports:
- port: 9090
protocol: TCP
targetPort: 9090
selector:
app: cnpg-i-plugin-example
TLS 인증서 구성하기
플러그인이 Deployment로 실행될 때 CloudNativePG와의 통신은 네트워크를 통해 이루어져요. 이를 보호하기 위해 mTLS가 강제되며, 양쪽 모두 TLS 인증서가 필요해요.
인증서는 Kubernetes TLS Secrets로 저장되어야 하고 플러그인의 Service 어노테이션(cnpg.io/pluginClientSecret과 cnpg.io/pluginServerSecret)에서 참조돼야 해요:
apiVersion: v1
kind: Service
metadata:
annotations:
cnpg.io/pluginClientSecret: cnpg-i-plugin-example-client-tls
cnpg.io/pluginServerSecret: cnpg-i-plugin-example-server-tls
cnpg.io/pluginPort: "9090"
name: barman-cloud
namespace: postgresql-operator-system
spec:
[...]
:::note 자체 인증서 번들을 제공할 수 있지만, 권장하는 방법은 Cert-manager를 사용하는 것이에요. :::
인증서 DNS 이름 커스터마이즈하기
기본적으로 CloudNativePG는 플러그인에 연결할 때 TLS 검증용 서버 이름으로 Service 이름을 사용해요. 환경에서 인증서가 다른 DNS 이름(예: barman-cloud.svc)을 가져야 한다면 cnpg.io/pluginServerName 어노테이션으로 커스터마이즈할 수 있어요:
apiVersion: v1
kind: Service
metadata:
annotations:
cnpg.io/pluginClientSecret: cnpg-i-plugin-example-client-tls
cnpg.io/pluginServerSecret: cnpg-i-plugin-example-server-tls
cnpg.io/pluginPort: "9090"
cnpg.io/pluginServerName: barman-cloud.svc
name: barman-cloud
namespace: postgresql-operator-system
spec:
[...]
이렇게 하면 오퍼레이터가 기본 Service 이름 대신 지정된 DNS 이름으로 플러그인 인증서를 검증할 수 있어요. 서버 인증서는 Subject Alternative Names(SAN)에 이 DNS 이름을 포함해야 해요.
플러그인 사용하기
플러그인을 활성화하려면 Cluster 리소스에서 .spec.plugins 섹션을 구성해요. 전체 PluginConfiguration 사양은 CloudNativePG API Reference를 참고해 주세요.
예시:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-with-plugins
spec:
instances: 1
storage:
size: 1Gi
plugins:
- name: cnpg-i-plugin-example.my-org.io
enabled: true
parameters:
key1: value1
key2: value2
각 플러그인은 자체 파라미터를 가질 수 있어요. 자세한 내용은 플러그인 문서를 확인해 주세요. spec.plugins의 name 필드는 플러그인 배포 방식에 따라 달라져요:
- 사이드카 컨테이너: Unix 소켓 파일 이름을 사용
- Deployment: Service의
cnpg.io/pluginName레이블 값을 사용
커뮤니티 플러그인
CNPG-I 프로토콜은 코어 프로젝트를 유지 관리 가능하게 유지하면서 CloudNativePG를 확장하는 검증되고 신뢰할 수 있는 패턴으로 빠르게 자리 잡았어요. 시간이 지나면서 커뮤니티는 실제 요구를 해결하고 개발자에게 예시가 되는 플러그인을 만들고 공유했어요.
CNPG-I로 구축된 플러그인의 완전하고 최신 목록은 CNPG-I GitHub 페이지에서 확인할 수 있어요.