본문 바로가기!

K8S Operator

[K8S Operator] Cloud Native PostgreSQL 오퍼레이터

728x90
반응형

1. PostgreSQL이란?

  • Global Development Group 에서 개발한 오픈소스 RDBMS
    • 국제 표준화 기구 ISO의 SQL 표준(SQL:2016 - Wiki)을 가장 잘 준수한 RDBMS로 179 필수 기능 중 170개 준수
    • 트랜잭션 지원 : 트랜잭션의 안전한 수행을 위한 MVCC 와 ACID 속성을 지원
    • 오픈소스 라이선스 : PostgresQL 라이선스를 사용하고 BSD나 MIT 라이선스와 비슷, 복사 사용 연구 수정 배포 등의 제한이 없다! - 링크

 

2. CloudNativePG(CNPG)란?

  • CloudNativePG 는 EDB가 개발 후, Apache License 2.0 공개 및 ‘22.4 CNCF Sandbox 제출함, 오퍼레이터 Level V - Auto Pilot 지원 - 링크
  • ‘23.10.29 CNPG 버전 : 1.21.x - 쿠버네티스 지원 버전(1.25 ~ 1.28), PostgreSQL 지원 버전(12 ~ 16) - 링크

 

2-1) 구조와 아키텍쳐

  • 보통 복제 전략에는 스토리지 수준 복제 또는 애플리케이션 수준 복제가 존재
  • PostgreSQL는 스토리지 수준 복제를 권장하지 않음 : https://youtu.be/99uSJXkKpeI?si=4Shn7hdxmCt4IDnO
  • PostgreSQL 는 비동기 혹은 동기 스트리밍 복제를 지원, 복제서버는 Hot Standby 기능으로 읽기 처리가 가능
  • CloudNativePG 는 비동기 혹은 동기 스트리밍 복제 구성의 클러스터를 지원
  • 하나의 Primary 노드와, 다수의 standby replicas로 동작
  • postgresql DB인스턴스는 3가지 역할이 존재
    • rw: 쓰기를 위한 primary DB인스턴스 접근
    • r: 읽기를 위한 아무 DB인스턴스 접근
    • ro: 읽기를 위한 standby DB인스턴스 접근
  • Read-Write workloads : -RW 요청을 프라이머리로 전달

 

 

  • Read-Only : -RO 요청을 Standby 로 Round robin 방식으로 전달 , 참고로 -R 요청은 any 전달

 

  • Multi-cluster deployments : 2개의 다른 쿠버네티스 클러스터에서, Replica Cluster 에 Designated Primary 는 Primary 클러스터의 Primary 의 복제

 

 

3. CloudNativePG(CNPG) 배포 테스트

  • Operator Lifecycle Manager(OLM) 설치
    • OLM은 k8s cluster 상에서 운영중인 Operator 운영의 Lifecycle을 자동으로 관리해주어 좀더 편하게 운영관리가 가능
curl -sL <https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.25.0/install.sh> | bash -s v0.25.0
$ kubectl get all -n olm
NAME                                    READY   STATUS    RESTARTS   AGE
pod/catalog-operator-6db85b79dd-5p7z5   1/1     Running   0          93s
pod/olm-operator-b59c56b74-k7h8j        1/1     Running   0          93s
pod/operatorhubio-catalog-62t9f         1/1     Running   0          85s
pod/packageserver-655db5dc4c-7k2gd      1/1     Running   0          84s
pod/packageserver-655db5dc4c-zjr8w      1/1     Running   0          84s

NAME                            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
service/operatorhubio-catalog   ClusterIP   172.20.192.106   <none>        50051/TCP   85s
service/packageserver-service   ClusterIP   172.20.189.58    <none>        5443/TCP    84s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/catalog-operator   1/1     1            1           93s
deployment.apps/olm-operator       1/1     1            1           93s
deployment.apps/packageserver      2/2     2            2           84s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/catalog-operator-6db85b79dd   1         1         1       93s
replicaset.apps/olm-operator-b59c56b74        1         1         1       93s
replicaset.apps/packageserver-655db5dc4c      2         2         2       84s

 

 

  • CloudNativePG Operator 설치
curl -s -O https://operatorhub.io/install/cloudnative-pg.yaml
cat cloudnative-pg.yaml | yh
kubectl create -f cloudnative-pg.yaml

 

  • operator 네임스페이스 확인 및 CRD확인
$ kubectl get all -n operators
NAME                                           READY   STATUS    RESTARTS   AGE
pod/cnpg-controller-manager-55c4c6d8f6-p55qj   1/1     Running   0          24s

NAME                                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/cnpg-controller-manager-service   ClusterIP   172.20.211.123   <none>        443/TCP   25s
service/cnpg-webhook-service              ClusterIP   172.20.141.196   <none>        443/TCP   28s

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cnpg-controller-manager   1/1     1            1           25s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/cnpg-controller-manager-55c4c6d8f6   1         1         1       26s


$ kubectl get-all -n operators | grep -v packagemanifest
NAME                                                                                    NAMESPACE  AGE
configmap/cnpg-default-monitoring                                                       operators  51s    
configmap/kube-root-ca.crt                                                              operators  4m53s  
endpoints/cnpg-controller-manager-service                                               operators  48s    
endpoints/cnpg-webhook-service                                                          operators  51s    
pod/cnpg-controller-manager-55c4c6d8f6-p55qj                                            operators  47s    
secret/cnpg-controller-manager-service-cert                                             operators  48s    
serviceaccount/cnpg-manager                                                             operators  49s    
serviceaccount/default                                                                  operators  4m53s  
service/cnpg-controller-manager-service                                                 operators  48s    
service/cnpg-webhook-service                                                            operators  51s    
deployment.apps/cnpg-controller-manager                                                 operators  48s    
replicaset.apps/cnpg-controller-manager-55c4c6d8f6                                      operators  48s    
lease.coordination.k8s.io/db9c8771.cnpg.io                                              operators  33s    
endpointslice.discovery.k8s.io/cnpg-controller-manager-service-c6vq5                    operators  48s    
endpointslice.discovery.k8s.io/cnpg-webhook-service-tgzgm                               operators  51s    
clusterserviceversion.operators.coreos.com/cloudnative-pg.v1.21.1                       operators  51s    
installplan.operators.coreos.com/install-cnnls                                          operators  52s    
operatorcondition.operators.coreos.com/cloudnative-pg.v1.21.1                           operators  51s    
operatorgroup.operators.coreos.com/global-operators                                     operators  4m53s  
subscription.operators.coreos.com/my-cloudnative-pg                                     operators  70s    
rolebinding.rbac.authorization.k8s.io/cloudnative-pg.v1.21.1                            operators  51s    
rolebinding.rbac.authorization.k8s.io/cnpg-controller-manager-service-cert              operators  48s    
role.rbac.authorization.k8s.io/cloudnative-pg.v1.21.1                                   operators  51s    
role.rbac.authorization.k8s.io/cnpg-controller-manager-service-cert                     operators  48s


$ kubectl get crd | grep cnpg
backups.postgresql.cnpg.io                    2023-11-04T12:27:59Z
clusters.postgresql.cnpg.io                   2023-11-04T12:28:00Z
poolers.postgresql.cnpg.io                    2023-11-04T12:28:00Z
scheduledbackups.postgresql.cnpg.io           2023-11-04T12:27:59Z

 

 

  • PostgreSQL Cluster 설치
    • Cluster란 CR을 생성하면 Operator가 이를 Watch 한 뒤 그에 맞는 리소스를 생성 가능 
$ 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

$ kubectl apply -f mycluster1.yaml

 

  • PostgreSQL Cluster 설치 확인
    • 해당 설치 과정은 1번 파드에서 Initializing(초기화)를 담당하는 Job이 뜬 뒤 종료되고 mycluster-1 파드가 생성되며, 이후 mycluster-2-join 이란 Job Pod가 생성되어 Join 작업을 수행한 뒤 mycluster-2 파드가 생성되는 식으로 진행
$ kubectl get pod,pvc,pv,svc
NAME                                    READY   STATUS    RESTARTS   AGE
pod/mycluster-1                         2/2     Running   0          4m28s
pod/mycluster-2                         2/2     Running   0          2m20s
pod/mycluster-3                         2/2     Running   0          1m32s

NAME                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mycluster-1   Bound    pvc-c9382d6f-sdf9-832d-129f-23cfa34f09c3   2Gi        RWO            gp3            4m40s
persistentvolumeclaim/mycluster-2   Bound    pvc-4924a34b-9cb0-8d0s-b102-a7fae4b97287   2Gi        RWO            gp3            2m52s
persistentvolumeclaim/mycluster-3   Bound    pvc-734a0a91-0a27-01cc-befa-b74eb1cc2c19   2Gi        RWO            gp3            1m55s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE 
persistentvolume/pvc-c9382d6f-sdf9-832d-129f-23cfa34f09c3   3Gi        RWO            Delete           Bound    default/mycluster-1   gp3                     4m30s
persistentvolume/pvc-4924a34b-9cb0-8d0s-b102-a7fae4b97287   3Gi        RWO            Delete           Bound    default/mycluster-2   gp3                     2m43s
persistentvolume/pvc-734a0a91-0a27-01cc-befa-b74eb1cc2c19   3Gi        RWO            Delete           Bound    default/mycluster-3   gp3                     1m30s

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes     ClusterIP   172.20.0.1       <none>        443/TCP    26m
service/mycluster-r    ClusterIP   172.20.37.238    <none>        5432/TCP   4m43s
service/mycluster-ro   ClusterIP   172.20.160.200   <none>        5432/TCP   4m40s
service/mycluster-rw   ClusterIP   172.20.93.78     <none>        5432/TCP   4m40s

 

 

3. 접속 테스트

3-1) secret 을 통해 Username, password 확인

$ kubectl get secret -l cnpg.io/cluster=mycluster
NAME                  TYPE                       DATA   AGE
mycluster-app         kubernetes.io/basic-auth   3      35m
mycluster-superuser   kubernetes.io/basic-auth   3      35m

# superuser 계정명 확인
kubectl get secrets mycluster-superuser -o jsonpath={.data.username} | base64 -d ;echo
postgres

# superuser 계정 암호 확인
kubectl get secrets mycluster-superuser -o jsonpath={.data.password} | base64 -d ;echo

 

3-2) Client Pod 배포

# myclient 파드 3대 배포 : envsubst 활용
## PODNAME=myclient1 VERSION=15.3.0 envsubst < myclient.yaml | kubectl apply -f -
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

 

3-3) superuser 계정으로 mycluster-rw 서비스 접속 및 DB 확인

$ kubectl exec -it myclient1 -- psql -U postgres -h mycluster-rw -p 5432 --variable=HISTFILE=/tmp/.psql_history

# 연결 정보 확인
$ postgres=# \conninfo
You are connected to database "postgres" as user "postgres" on host "mycluster-rw" (address "10.200.1.40") at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)

# 데이터베이스 조회
$ postgres=# \l
List of databases
   Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges
-----------+----------+----------+---------+-------+-----------------------
 app       | app      | UTF8     | C       | C     |
 postgres  | postgres | UTF8     | C       | C     |
 template0 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 template1 | postgres | UTF8     | C       | C     | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres

 

4. 장애 테스트

4-1) 장애 테스트를 위해 test DB에 데이터 Insert

for ((i=3; i<=100; i++)); do kubectl exec -it myclient1 -- psql -U postgres -h mycluster-rw -p 5432 -d test -c "INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
kubectl exec -it myclient1 -- psql -U postgres -h mycluster-ro -p 5432 -d test -c "SELECT COUNT(*) FROM t1"

 

4-2) 프라이머리 파드(인스턴스) 1대 강제 삭제 및 동작 확인

# 프라이머리 파드 정보 확인
kubectl cnpg status mycluster

# [터미널1] 모니터링
watch kubectl get pod -l cnpg.io/cluster=mycluster

# [터미널2] 모니터링
while true; do kubectl exec -it myclient2 -- psql -U postgres -h mycluster-ro -p 5432 -d test -c "SELECT COUNT(*) FROM t1"; date;sleep 1; done

# [터미널3] test 데이터베이스에 다량의 데이터 INSERT
for ((i=301; i<=10000; i++)); do kubectl exec -it myclient2 -- psql -U postgres -h mycluster-rw -p 5432 -d test -c "INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
for ((i=10001; i<=20000; i++)); do kubectl exec -it myclient2 -- psql -U postgres -h mycluster-rw -p 5432 -d test -c "INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
혹은
for ((i=301; i<=10000; i++)); do psql -U postgres -h psql.$MyDomain -p 5432 -d test -c "INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done
for ((i=10001; i<=20000; i++)); do psql -U postgres -h psql.$MyDomain -p 5432 -d test -c "INSERT INTO t1 VALUES ($i, 'Luis$i');";echo; done

# [터미널4] 파드 삭제 >> INSERT 가 중간에 끊어지나요?
kubectl get pod -l cnpg.io/cluster=mycluster -owide
kubectl delete pvc/mycluster-1 pod/mycluster-1
kubectl cnpg status mycluster

# 파드 정보 확인 : 파드의 이름과 배치 위치 비교 확인
kubectl get pod -l cnpg.io/cluster=mycluster -owide
kubectl get pod -l cnpg.io/cluster=mycluster
NAME          READY   STATUS    RESTARTS   AGE
mycluster-2   1/1     Running   0          125m
mycluster-3   1/1     Running   0          125m
mycluster-4   1/1     Running   0          2m18s

 

4-3) 프라이머리 파드(인스턴스) 가 배포된 노드 1대 drain 설정 및 동작 확인

# (옵션) 오퍼레이터 로그 확인
kubectl get pod -n operators -l app.kubernetes.io/name=cloudnative-pg
kubectl logs -n operators -l app.kubernetes.io/name=cloudnative-pg -f

# 워커노드 drain
# kubectl drain <<노드>> --ignore-daemonsets --delete-emptydir-data
kubectl get node
NODE=<각자 자신의 EC2 노드 이름 지정>
NODE=ip-192-168-3-231.ap-northeast-2.compute.internal
kubectl drain k8s-w1 --delete-emptydir-data --force --ignore-daemonsets && kubectl get node -w
혹은
kubectl drain $NODE --delete-emptydir-data --force --ignore-daemonsets && kubectl get node -w

# 클러스터 정보 확인 : 파드가 Pending 된 주 원인은 무엇일까요? >> 예를 들어 동일AZ에 정상 워커노드가 있었다면 어떻게 될까요?
kubectl get pod -owide
kubectl cnpg status mycluster

# 동작 확인 후 uncordon 설정
kubectl uncordon $NODE

 

 

ps) 4-2, 4-3 장애 테스트는 실컷 테스트 한후.. 해당 내용을 복사(캡쳐)하지않고 EKS 클러스터를 삭제하여.. 추후 업로드 예정....ㅠㅠ

728x90
반응형