[DOIK2] 쿠버네티스 오퍼레이터 @PostgreSQL

2023. 11. 5. 00:45DOIK@CloudNet

- cloudnet에서 주관하는 쿠버네티스 데이터베이스 오퍼레이터 스터디 2기 내용입니다

- 목적: 다양한 db 오퍼레이터 실습으로 eks 환경에서 db 배포 및 운영을 위한 다양한 db 오퍼레이터 실습

이번주에는 Kubernates 환경에서 PostgreSQL 을 배포하고 관련 내용을 조금 정리해 보았다.

 

1. PostgreSQL이란?

- Cloud Native 를 잘 지원하는 오픈소스 RDBMS

- 완전한 오픈소스로서 상용 사용이 가능해 요즘 핫하게 떠오르고 있다

- 데이터베이스 계층 구조는 테이블 ⊂ 스키마 ⊂ 클러스터 로 구성된다.

- pgAdmin4라는 자체 GUI 툴을 지원해서 편리하게 사용할 수 있다.

- pg_hba.conf 를 조정해서 설정을 바꿀 수 있다.

 

 

2. CloudNativePG란?

그런 PostgreSQL을 클라우드에서 운영하기 위한 오퍼레이터로, 지난주에 소개한 오퍼레이터허브에서 무려 lv5를 달성했을 만큼 성숙한 오퍼레이터이다(참고로 오퍼레이터허브에서 lv5는 15개뿐이다). 쿠버네티스 API와 완전 연동된다는 점이 큰 장점이고 cnpg 플러그인도 지원한다. (https://operatorhub.io/)

 

 

- 가능한 서비스는 rw, ro, r 3가지가 있다. (r이 특이 사항!)

rw: read-write 요청을 primary로 전달

https://cloudnative-pg.io/documentation/current/architecture/

ro: read only 요청을 Round robin 방식으로 전달

https://cloudnative-pg.io/documentation/current/architecture/

r: 어떤 인스턴스에도 접근할 수 있다.

 

아래 실습에서 cnpg를 설치하고 나서 확인해보니 과연 rw에는 primary pod의 주소만, 그리고 ro에는 standby의 주소만 assign되어 있었고 r에는 모든 pod의 주소가 assign되어 있었다.

 

- 유연한 라이프사이클 관리를 위해 OLM(Operator Lifecycle Manager) 설치

#설치
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.25.0/install.sh | bash -s v0.25.0

# namespace 확인
kubectl get ns

# olm 확인
kubectl get all -n olm

# operator 확인
kubectl get-all -n operators | grep -v packagemanifest

kubectl get ns
kubectl get all -n olm

 

 

- 이제 OLM을 받았으니 오퍼레이터를 설치한다. 

#Operatorhub에서 CloudNative PG 가져오기
curl -s -O https://operatorhub.io/install/cloudnative-pg.yaml

#내용 확인
cat cloudnative-pg.yaml | yh

#설치
kubectl create -f cloudnative-pg.yaml

 

잘 설치되었음을 확인할 수 있다.

-커스텀리소스(crd)중 cnpg(CloudNativePostGresql) 플러그인을 사용하고 있는 내용들을 볼 수 있다. 위 사진에서 각각은 backup~: 백업, cluster: postgresql cluster, pooler: PGBouncer, schedulebackup: cron 에 관련된 CRD가 있음을 확인한다.

 

 

위에서 오퍼레이터를 배포했으므로 실제 클러스터를 배포해 보자

- 클러스터 배포를 위한 .yaml 내용

더보기
cat <<EOT> mycluster1.yaml
# Example of PostgreSQL cluster
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: mycluster
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:15.3
  instances: 3  
  storage:
    size: 3Gi
  postgresql:
    parameters:
      max_worker_processes: "40"
      timezone: "Asia/Seoul"
    pg_hba:
      - host all postgres all trust
  primaryUpdateStrategy: unsupervised 
  enableSuperuserAccess: true
  bootstrap:
    initdb:
      database: app
      encoding: UTF8
      localeCType: C
      localeCollate: C
      owner: app

  monitoring:
    enablePodMonitor: true
EOT

 

- 순서: initdb 실행(job) -> psql pod1 Initialize -> pod1 Running -> mycluster2부터는 원래 있던 pod에 JOIN 으로 동작한다

- 생성된 pod는 PostgreSQL의 기본 포트인 :5432 외에도 :9187을 메트릭 확인용 포트로 제공한다.

-> prometheus & grafana로 시각화 가능!

# pod IP 확인
kubectl get pod -l cnpg.io/cluster=mycluster -owide

# port 9187에서 전송된 metric 확인
curl -s <파드IP>:9187/metrics

 

 

+ 클러스터 정보 확인시 --since 0m 을 추가하면 최근 액션만 확인할 수 있어 유용하다

kubectl get-all --since 5m

 

 

- krew를 활용해 cnpg 플러그인을 설치하고 이것으로 클러스터 상태를 확인할 수 있다. 사용한 이미지(postgresql 15.3), streaming sync status, instance 정보 등 유용한 정보를 한번에 확인할 수 있다.

- -verbose 옵션은 더 다양한 정보를 보여준다.

kubectl krew install cnpg
kubectl cnpg status mycluster
kubectl cnpg status mycluster -verbose

 

- 그리고 statefulset을 사용하지 않는다. 특히 DB처럼 민감한 대상은 statefulset 대신 custom controller 를 만들어 더 세밀하게 조정하고자 한다고. (DB에서는 치명적인) 인덱스로 접근이 안되는 것도 stateful의 단점 중 하나이다.  

 

 

 

실제 PostgreSQL에 접속해 쿼리를 쏴보자

기본적으로 superuser(postgres), user(app) 2개가 생성되어 있다. 각각의 계정 암호를 확인하고 쓸수있게 미리 저장해놓는다.

# 계정명 확인
root@myeks-bastion-EC2 ~]# kubectl get secrets mycluster-superuser -o jsonpath={.data.username} | base64 -d ;echo

# 암호 확인
root@myeks-bastion-EC2 ~]# kubectl get secrets mycluster-superuser -o jsonpath={.data.password} | base64 -d ;echo

# app 계정 암호 지정
AUSERPW=$(kubectl get secrets mycluster-app -o jsonpath={.data.password} | base64 -d)

 

 

그리고 접근할 myclient pod를 3개 배포하고 superuser 계정으로 -rw 서비스에 접속한다

# 접근할 pod를 3개 배포한다
curl -s https://raw.githubusercontent.com/gasida/DOIK/main/5/myclient-new.yaml -o myclient.yaml
for ((i=1; i<=3; i++)); do PODNAME=myclient$i VERSION=15.3.0 envsubst < myclient.yaml | kubectl apply -f - ; done

# superuser 계정으로 mycluster-rw 접속
kubectl exec -it myclient1 -- psql -U postgres -h mycluster-rw -p 5432

 

하고 나면 이제 PostgreSQL 서비스를 이용할 수 있다

#접속정보 확인
\conninfo

#db 조회
\l

#...and other SQL commands

 

 

 

3. 복제에 대해서

- DB를 복제하는 이유는 부하분산, 고가용성, 백업이 대표적이다. 

- Cloud Native PostgreSQL에서는 복제 동작의 기반으로 WAL Shipping 을 사용한다. WAL(Write Ahead Log) 은 데이터 무결성(중에서도 물리적 무결성) 을 보장하기 위한 방식이다. 사용자는 디스크에 썼다고 생각했는데 실제로는 쓰여지지 않은 상황이 대표적이다. 속도가 메모리>>>디스크 이고, 메모리와 캐시는 휘발성이기 때문에 주로 발생한다.

 

- 따라서 이런 상황을 해결하기 위해 말 그대로 '먼저 로그를 쓰고 데이터파일을 변경한다' 는 WAL의 개념이 도입되었다. 만약 문제가 발생해도 로그가 남아있으면 복구할 수 있기 때문이다. 로그도 없다면? 사용자는 화나겠지만 다시하면 되긴 하니까...

 

 

- Streaming Replication: WAL 파일 저장 여부와 상관없이, 메인서버 WAL이 채워질때까지 기다리지 않고 실시간으로 WAL record 를 전달한다. 다른 방식에 비해 구성이 간단하지만 메인서버를 계속 streaming 하므로 리소스를 소모한다.

- Synchronous commit: 해당 transaction의 WAL record 가 master disk 까지 write된 것을 보장한다.

 

- PostgreSQL은 스토리지 수준 복제 대신 애플리케이션 수준 복제를 권장한다

 

 

하면서 든 생각

- 역시 다양한 경험을 해 보는 게 중요하다: 사용해 본 DB가 스터디에서 처음으로 등장했다. 잘 알고 사용한게 아니었음에도 훨씬 따라가기 수월했다. 

- 도전과제는 다른 포스트로 올려볼게요...!

 

References

- WAL : https://oss.tibero.com/d391cda9-ec4c-4f3c-a730-1149e7b7e4fd

 

10. PostgreSQL의 WAL(Write Ahead Log)

데이터베이스 관리 시스템은 데이터 무결성(Data Integrity)를 보장해야만 합니다. 다수의 데이터베이스는 데이터 무결성을 보장하기 위해 WAL(Write Ahead Log)이라는 방식을 사용합니다.

oss.tibero.com

- 복제 : https://blog.ex-em.com/1781

 

DB 인사이드 | PostgreSQL Replication - 종류

Replication은 Data 저장과 백업하는 방법과 관련이 있는 Data를 호스트 컴퓨터에서 다른 컴퓨터로 복사하는 것을 말한다. Replication은 RDBMS에서 추가적으로 제공하거나 여러 대의 Database Server의 부하를

blog.ex-em.com

- synchronous commit : https://minsql.com/postgres/PostgreSQL-synchronous_commit-%EA%B0%9C%EB%85%90%EB%8F%84/

 

PostgreSQL synchronous_commit 개념도

PostgreSQL synchronous_commit 개념도

minsql.com