
Kubernetes는 컨테이너화된 애플리케이션의 배포, 확장, 관리를 자동화하는 컨테이너 오케스트레이션 플랫폼으로, 롤링 업그레이드와 확장 작업에 수반되는 많은 수동 단계를 추상화해 줍니다. 클라우드 네이티브 애플리케이션을 구축할 때는 애플리케이션이 클러스터 내에서 이러한 기능을 활용할 수 있도록 PostgreSQL과 같은 데이터베이스 애플리케이션을 배포해야 하는 경우가 많습니다.
이 가이드에서는 Kubernetes 클러스터에 PostgreSQL을 배포하는 방법을 단계별로 설명하여 구성 및 확장이 가능한 클라우드 네이티브 PostgreSQL 환경을 구축할 수 있도록 도와드립니다. Google Kubernetes Engine, Amazon EKS, IBM Cloud Kubernetes Service를 사용하든, Minikube와 같은 로컬 솔루션을 사용하든, 이 튜토리얼을 그대로 적용할 수 있습니다. Kubernetes에서 테스트용 단일 PostgreSQL 인스턴스를 배포하는 방법과 Helm, Kubernetes Operator, Bitnami PostgreSQL과 같은 도구를 사용하는 고급 배포 방식을 알아보세요.
사전 준비 사항
이 가이드를 따라 하려면 다음이 필요합니다.
- Kubernetes 클러스터(DigitalOcean, AWS, Google Cloud, IBM Cloud 등의 클라우드 환경 또는 Kind·Minikube 같은 로컬 환경)
- Kubectl 관련 지식 — Kubernetes API를 제어하는 명령줄 도구에 대한 기본 지식
시작하기 전에 Kubernetes에서 단일 PostgreSQL 인스턴스를 배포하는 기본 단계부터 살펴보겠습니다.
간단한 PostgreSQL 배포
PostgreSQL 도커라이징
Kubernetes는 레지스트리에서 도커 이미지를 가져와 구성 파일에 근거하여 배포합니다. 다음 단계에 따라 직접 PostgreSQL 도커 이미지를 빌드할 수도 있고, 도커 허브에서 제공하는 공식 오픈 소스 이미지를 사용할 수도 있습니다. 이 글에서는 최신 Postgres 15.3 이미지를 사용합니다.
연결 구성 및 시크릿(Secret) 생성
데이터베이스 자격 증명과 같은 민감한 정보를 안전하게 저장하기 위해 Kubernetes의 기본 리소스 중 하나인 시크릿 구성을 사용합니다.
Kubernetes는 기본적으로 시크릿을 base64로 인코딩된 형태로 저장하는데, 이것만으로는 안전하지 않습니다. 보안을 강화하려면 저장 시 암호화를 활성화해야 합니다.
구성이 완료되면 Kubernetes 시크릿을 위한 구성을 생성합니다. 여기서는 Postgres 비밀번호에 대해 다음 값을 사용하겠습니다.
❯ echo -n "postgres" | base64
cG9zdGdyZXMK
이제 시크릿 구성 파일을 만들고 클러스터에 적용합니다.
> cat postgres-secrets.yml
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret-config
type: Opaque
data:
password: cG9zdGdyZXMK
여기서는 Kubernetes에 데이터를 저장하기 위해 시크릿 제공자를 사용하라고 지시하는 비밀 객체인 kind를 사용했습니다. 이러한 값들을 저장할 때 사용해야 하는 이름은 postgres-secret-config라는 키 아래에 있습니다. 마지막으로 data 섹션에 비밀 값으로 저장해야 하는 키/값 쌍을 제공했습니다.
이제 이 구성을 적용한 뒤 내용이 올바르게 저장되었는지 확인합니다.
❯ kubectl apply -f postgres-secrets.yml
secret/postgres-secret-config created
❯ kubectl get secret postgres-secret-config -o yaml
apiVersion: v1
data:
password: cG9zdGdyZXMK
....
PersistentVolume과 PersistentVolumeClaim 생성하기
다음으로 데이터베이스 데이터를 영구적으로 저장할 파일 스토리지를 생성해야 합니다. 기본적으로 도커 인스턴스는 컨테이너가 더 이상 존재하지 않게 되면 어떤 정보에 대해서도 영구 스토리지가 제공되지 않기 때문입니다.
해결 방법은 PostgreSQL 데이터를 저장할 파일 시스템을 마운트하는 것입니다. Kubernetes에서는 이러한 작업을 위해 서로 다른 구성 형식을 사용하므로 다음 단계를 따릅니다.
- PersistentVolume(PV) 매니페스트를 생성하여 사용할 볼륨 유형을 정의합니다.
- PersistentVolumeClaim(PVC)을 생성하여 동일한 스토리지 클래스(storage class)를 기반으로 해당 PersistentVolume의 사용을 요청합니다.
이번 예제에서는 현재 노드의 파일 시스템을 볼륨으로 사용하지만, 실제 운영 환경에서는 standard, gp2 등 데이터베이스용 표준 스토리지 클래스나 복제 및 IOPS 최적화를 지원하는 클라우드 제공자 전용 동적 프로비저너(dynamic provisioner)를 사용하는 것이 좋습니다.
먼저 PersistentVolume의 구성을 정의합니다.
> cat pv-volume.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: postgres-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
이 구성에서는 클러스터 노드의 /mnt/data 경로에 5GB의 읽기-쓰기(ReadWrite) 스토리지를 예약하도록 지정했습니다.
이제 구성을 적용하고, PersistentVolume이 정상적으로 생성되었는지 확인합니다.
❯ kubectl apply -f pv-volume.yml
persistentvolume/postgres-pv-volume created
❯ kubectl get pv postgres-pv-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
postgres-pv-volume 5Gi RWO Retain Available manual 51s
이제 앞서 정의한 PersistentVolume과 일치하는 PersistentVolumeClaim을 구성합니다.
> cat pv-claim.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
이 구성에서는 동일한 스토리지 클래스 이름을 사용해 1GB 용량의 PersistentVolumeClaim을 요청했습니다. 이 매개변수는 Kubernetes가 전체 5GB 중 이 클레임을 위해 동일한 스토리지 클래스에서 1GB를 따로 예약할 수 있게 해주기 때문에 중요합니다.
이제 구성을 적용하고 PersistentVolumeClaim이 바인딩되었는지 확인합니다.
❯ kubectl apply -f pv-claim.yml
persistentvolumeclaim/postgres-pv-claim created
❯ kubectl get pvc postgres-pv-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
postgres-pv-claim Bound postgres-pv-volume 1Gi RWO manual 5m32s
Kubernetes에서 PostgreSQL 배포하기
앞서 생성한 postgres-secret-config 시크릿 이름과 PersistentVolume, PersistentVolumeClaim 구성을 참조해 이제 Kubernetes 인스턴스의 배포(Deployment) 구성을 만들어 보겠습니다.
> cat postgres-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
volumes:
- name: postgres-pv-storage
persistentVolumeClaim:
claimName: postgres-pv-claim
containers:
- name: postgres
image: postgres:11
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret-config
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgres-pv-storage
여기서는 앞에서 정의한 모든 구성을 Kubernetes 시크릿 구성과 퍼시스턴트 볼륨 마운트에 모두 결합했습니다. 또한 selector와 metadata 필드처럼 여러 줄의 항목을 별도로 지정해 주어야 하는 apiVersion: apps/v1 형식의 배포 구성을 사용했습니다. 그 후 컨테이너 이미지와 이미지 풀(pull) 정책에 대한 세부 정보도 추가했습니다. 이는 해당 컨테이너에 올바른 볼륨과 시크릿이 사용되도록 보장하기 위해 모두 필요한 작업입니다.
이제 배포를 적용하고 정상적으로 사용 가능한지 확인하겠습니다.
❯ kubectl apply -f postgres-deployment.yml
deployment.apps/postgres created
❯ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
postgres 1/1 1 1 28s
PostgreSQL 서버 노출을 위한 서비스 만들기
Kubernetes Service를 사용하여 PostgreSQL 파드를 외부로 노출합니다. 이를 위해 다른 포트를 구성하거나 NodePort 또는 LoadBalancer로 노출하도록 구성할 수 있습니다. 여기서는 단순하게 하기 위해 노드의 IP에서 고정 포트로 서비스를 노출하는 NodePort를 사용하는 방법을 보여 드리겠습니다.
아래와 같은 서비스 매니페스트를 사용하실 수 있습니다.
> cat postgres-service.yml
apiVersion: v1
kind: Service
metadata:
name: postgres
labels:
app: postgres
spec:
type: NodePort
ports:
- port: 5432
selector:
app: postgres
이제 서비스의 구성을 적용하고, 포트가 할당되었는지 또는 제대로 사용할 수 있는지 확인하겠습니다.
❯ kubectl apply -f postgres-service.yml
service/postgres created
❯ kubectl get service postgres
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
PostgreSQL 데이터베이스 연결 테스트하기
이제 내부적으로 PostgreSQL 데이터베이스에 연결할 수 있어야 합니다. 아래 명령어를 사용해 확인해 볼 수 있습니다.
❯ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-57f4746d96-7z5q8 1/1 Running 0 30m
❯ kubectl exec -it postgres-57f4746d96-7z5q8 -- psql -U postgres
또는 Kubernetes Pod 이름을 변수로 저장해 더 편리하게 사용할 수도 있습니다
POD=$(kubectl get pods -l app=postgres -o jsonpath="{.items[0].metadata.name}")
다른 도커 컨테이너를 이용해 psql 명령으로 연결할 수도 있습니다.
export POSTGRES_PASSWORD=$(kubectl get secret postgres-secret-config -o jsonpath="{.data.password}" | base64 --decode)
❯ kubectl run postgres-client --rm --tty -i --restart='Never' --image postgres:11 --env="PGPASSWORD=$POSTGRES_PASSWORD" --command -- psql -h postgres -U postgres
만약 프롬프트가 표시되지 않는다면 Enter 키를 한 번 눌러보세요.
postgres=#
이제 쿼리를 실행할 준비가 되었습니다.
고급 PostgreSQL 배포
앞선 예시에서는 개발 목적으로 단일 PostgreSQL 인스턴스만 배포했습니다. 그러나 실제 프로덕션이나 하이브리드 클라우드 환경에서는 다음과 같은 고급 배포 옵션을 사용할 수 있습니다.
- 비트나미(Bitnami) PostgreSQL 배포: Bitnami는 여러 유형의 배포 방식(커뮤니티에서 유지하는 Kubernetes 패키지 관리자 Helm 포함)을 지원하며, 대규모 배포를 위한 다양한 구성 옵션을 제공합니다.
- 잘란도(Zalando) PostgreSQL 오퍼레이터: 기존 Patroni 기반 클러스터 템플릿을 활용하는 고가용성 Kubernetes 오퍼레이터로, 안정적이고 확장 가능한 PostgreSQL 클러스터 운영을 지원합니다.
이러한 옵션들은 엔터프라이즈급 인프라 구축과 클라우드 네이티브 컴퓨팅 기반의 모범 사례를 따르는 데 도움이 됩니다.
다음 단계
이제 Kubernetes에 간단한 PostgreSQL 인스턴스를 배포했으므로 모니터링과 로깅을 통합할 준비가 되었습니다. Sumo Logic과 같은 도구는 고급 Kubernetes 모니터링을 지원하여 클러스터 리소스를 관리하고, Kubernetes 파드에서 생성되는 로그를 분석하며, 클라우드 네이티브 애플리케이션의 옵저버빌리티를 보장하는 데 도움을 줍니다. 기본 설정에서는 클레임이 삭제되면 볼륨도 함께 삭제될 수 있습니다. 재클레임 정책(Reclaim Policy)을 Retain(보존)으로 설정하면 PVC가 삭제되더라도 데이터가 그대로 유지되도록 할 수 있습니다.
Sumo Logic이 Kubernetes 모니터링에 어떻게 도움이 되는지 알아보세요.



