MathJax

MathJax-2

MathJax-3

Google Code Prettify

置頂入手筆記

EnterproseDB Quickstart — 快速入門筆記

由於考慮採用 EnterpriseDB 或是直接用 PostgreSQL 的人,通常需要一些入手的資料。這邊紀錄便提供相關快速上手的簡單筆記 ~ 這篇筆記以 資料庫安裝完畢後的快速使用 為目標,基本紀錄登入使用的範例:

2021年9月10日 星期五

初步認識 EDB Postgres 的 K8s 容器化解決方案 — Cloud Native PostgreSQL Operator

基於上一篇筆記所找到的好心測試 K8s 工具,這邊要來嘗試 EDB Postgres 的 PostgreSQL 容器化方案 — Cloud Native PostgreSQL Operator。

在前一陣子 EDB 曾經在 K8s/Openshift 平台還沒變成壓倒性主流容器平台方案時,就已經提供 Docker Container 的資料庫解決方案,當時也做了一點點練習(筆記在這)。

不過當時得努力的搞清楚最原生的 K8s YAML 設置,其實很複雜。。。

隨著 K8s 整體的推進,出現了 HelmOperator 這類「套件管理」的工具,加上 K8s 強化 Stateful Container 的支援,資料庫陸陸續續也可以輕易登陸 K8s 平台。

EDB 團隊基於這些變化,加上原 2ndQ 團隊的一同合作,便推出這個成熟的 K8s 解決方案。

這邊就要來初步體驗一下~

EDB 公司提供的 K8s Operator 稱作 Cloud Native PostgreSQL(縮寫 CNP,後面常出現),提供以下的功能,以便使 PGSQL 可以舒適的活在 K8s 裡面:
  • 原生版 PostgreSQL 與企業版 EDB Postgres Advanced Server 資料庫
  • 基於 K8s 調度機制的資料庫高可用功能
  • 支援 S3 協定的資料庫全備份、WAL 備份
  • 自動設置資料傳輸安全性
  • 滾動式小版本更新支援、K8s 底層維護支援
  • K8s 節點磁碟壓測工具 cnp-bench

此外,EDB 的 CNP Operator 提供可以不斷再生的 30 天試用期~~大家可以多多測試~
這邊就使用好心的 K8s 測試環境 KinD 作簡易的三節點主從 PGSQL 資料庫練習,並且偷偷潛入容器裡面喵一下。
以下會進行的步驟大致如下
  1. 操作預備好的 K8s in Docker 三節點環境
  2. 安裝 CNP Operator:有這個才能佈署 EDB 資料庫解決方案
  3. 設置 1 Primary/2 Standby 的 PGSQL 資料庫
  4. 偷偷溜進去 Container 觀察一下
  5. 觸發簡單的高可用切換

以上的第一步就是沿用上一篇筆記環境
現在要安裝 CNP Operator:這需要利用 kubectl apply 指令佈署 Operator,利用 -f 指定 YAML 檔案。這邊直接參照以下頁面操作。
EDB Docs - Installation and upgrades
EDB Docs - Installation, Configuration and Deployment Demo
[goodgame@k8sindocker ~]$ curl https://get.enterprisedb.io/cnp/postgresql-operator-1.7.1.yaml -o postgresql-operator-1.7.1.yaml
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  218k  100  218k    0     0   221k      0 --:--:-- --:--:-- --:--:--  220k
[goodgame@k8sindocker ~]$ kubectl apply -f postgresql-operator-1.7.1.yaml
namespace/postgresql-operator-system created
customresourcedefinition.apiextensions.k8s.io/backups.postgresql.k8s.enterprisedb.io created
customresourcedefinition.apiextensions.k8s.io/clusters.postgresql.k8s.enterprisedb.io created
customresourcedefinition.apiextensions.k8s.io/scheduledbackups.postgresql.k8s.enterprisedb.io created
Warning: admissionregistration.k8s.io/v1beta1 MutatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 MutatingWebhookConfiguration
mutatingwebhookconfiguration.admissionregistration.k8s.io/postgresql-operator-mutating-webhook-configuration created
serviceaccount/postgresql-operator-manager created
clusterrole.rbac.authorization.k8s.io/postgresql-operator-manager created
clusterrolebinding.rbac.authorization.k8s.io/postgresql-operator-manager-rolebinding created
service/postgresql-operator-webhook-service created
deployment.apps/postgresql-operator-controller-manager created
Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration
validatingwebhookconfiguration.admissionregistration.k8s.io/postgresql-operator-validating-webhook-configuration created
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ kubectl get deploy -n postgresql-operator-system postgresql-operator-controller-manager
NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
postgresql-operator-controller-manager   1/1     1            1           72s
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ kubectl get pods -n postgresql-operator-system
NAME                                                      READY   STATUS    RESTARTS   AGE
postgresql-operator-controller-manager-684bb9c549-5rjbh   1/1     Running   0          83s
[goodgame@k8sindocker ~]$ 

CNP Operator 安裝完成之後,就可以佈署 DB 叢集了。相關的叢集範例可以參照 Configuration Samples - Cloud Native PostgreSQLEDB Docs - Configuration Samples)。基本上都是高可用配置的範本。單節點不需要用 Operator。
資料庫初始化選項可以參考 EDB Docs - Bootstrap,這邊直接選用下面這個範本
https://docs.enterprisedb.io/cloud-native-postgresql/1.7.1/samples/cluster-example.yaml
執行後就一邊觀察 pod 狀態。這邊顯示一開始跟完成後的狀況:可以撘配 watch 觀察會更清楚~
[goodgame@k8sindocker ~]$ curl https://docs.enterprisedb.io/cloud-native-postgresql/1.7.1/samples/cluster-example.yaml
# Example of PostgreSQL cluster
apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Cluster
metadata:
  name: cluster-example
spec:
  instances: 3

  # Require 1Gi of space
  storage:
    size: 1Gi
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ curl https://docs.enterprisedb.io/cloud-native-postgresql/1.7.1/samples/cluster-example.yaml -o cluster-example.yaml
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   141  100   141    0     0    173      0 --:--:-- --:--:-- --:--:--   173
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ kubectl apply -f cluster-example.yaml 
cluster.postgresql.k8s.enterprisedb.io/cluster-example created
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ # 馬上觀察
[goodgame@k8sindocker ~]$ kubectl get pods
NAME                             READY   STATUS            RESTARTS   AGE
cluster-example-1-initdb-nvhp8   0/1     PodInitializing   0          19s
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ # 中間過程
[goodgame@k8sindocker ~]$ kubectl get pods
NAME                             READY   STATUS      RESTARTS   AGE
cluster-example-1                1/1     Running     0          2m6s
cluster-example-1-initdb-nvhp8   0/1     Completed   0          3m37s
cluster-example-2                1/1     Running     0          8s
cluster-example-2-join-8xn92     0/1     Completed   0          2m4s
cluster-example-3-join-lnq24     0/1     Pending     0          5s
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ #叢集完成
[goodgame@k8sindocker ~]$ kubectl get pods
NAME                READY   STATUS    RESTARTS   AGE
cluster-example-1   1/1     Running   0          2m22s
cluster-example-2   1/1     Running   0          24s
cluster-example-3   1/1     Running   0          12s
[goodgame@k8sindocker ~]$ 

等到完成後,就可以用 kubectl 原生指令查看資料庫叢集資訊

[goodgame@k8sindocker ~]$ kubectl get cluster cluster-example -o yaml
apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Cluster
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"postgresql.k8s.enterprisedb.io/v1","kind":"Cluster","metadata":{"annotations":{},"name":"cluster-example","namespace":"default"},"spec":{"instances":3,"storage":{"size":"1Gi"}}}
  creationTimestamp: "2021-09-10T10:30:29Z"
  generation: 1
  name: cluster-example
  namespace: default
  resourceVersion: "8495"
  uid: 465XXX96-9e2c-4O8f-8OO0-7b6XXXX28a2e
spec:
  affinity:
    podAntiAffinityType: preferred
    topologyKey: ""
  bootstrap:
    initdb:
      database: app
      owner: app
  imageName: quay.io/enterprisedb/postgresql:13.3
  instances: 3
  postgresql:
    parameters:
      log_destination: csvlog
      log_directory: /controller/log
      log_filename: postgres
      log_rotation_age: "0"
      log_rotation_size: "0"
      log_truncate_on_rotation: "false"
      logging_collector: "on"
      max_parallel_workers: "32"
      max_replication_slots: "32"
      max_worker_processes: "32"
      shared_preload_libraries: ""
      wal_keep_size: 512MB
  resources: {}
  storage:
    size: 1Gi
status:
  certificates:
    clientCASecret: cluster-example-ca
    expirations:
      cluster-example-ca: 2021-12-09 10:25:29 +0000 UTC
      cluster-example-replication: 2021-12-09 10:25:29 +0000 UTC
      cluster-example-server: 2021-12-09 10:25:29 +0000 UTC
    replicationTLSSecret: cluster-example-replication
    serverAltDNSNames:
    - cluster-example-rw
    - cluster-example-rw.default
    - cluster-example-rw.default.svc
    - cluster-example-r
    - cluster-example-r.default
    - cluster-example-r.default.svc
    - cluster-example-ro
    - cluster-example-ro.default
    - cluster-example-ro.default.svc
    serverCASecret: cluster-example-ca
    serverTLSSecret: cluster-example-server
  configMapResourceVersion: {}
  currentPrimary: cluster-example-1
  healthyPVC:
  - cluster-example-1
  - cluster-example-2
  - cluster-example-3
  instances: 3
  instancesStatus:
    healthy:
    - cluster-example-1
    - cluster-example-2
    - cluster-example-3
  latestGeneratedNode: 3
  licenseStatus:
    isImplicit: true
    isTrial: true
    licenseExpiration: "2021-10-10T10:30:29Z"
    licenseStatus: Implicit trial license
    repositoryAccess: false
    valid: true
  phase: Cluster in healthy state
  pvcCount: 3
  readService: cluster-example-r
  readyInstances: 3
  secretsResourceVersion:
    applicationSecretVersion: "6616"
    clientCaSecretVersion: "6612"
    replicationSecretVersion: "6614"
    serverCaSecretVersion: "6612"
    serverSecretVersion: "6613"
    superuserSecretVersion: "6615"
  targetPrimary: cluster-example-1
  writeService: cluster-example-rw
[goodgame@k8sindocker ~]$ 

觀察一下 service
[goodgame@k8sindocker ~]$ kubectl get services
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
cluster-example-any   ClusterIP   10.96.194.146   <none>        5432/TCP   5h50m
cluster-example-r     ClusterIP   10.96.173.199   <none>        5432/TCP   5h50m
cluster-example-ro    ClusterIP   10.96.238.83    <none>        5432/TCP   5h50m
cluster-example-rw    ClusterIP   10.96.24.26     <none>        5432/TCP   5h50m
kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP    6h32m
[goodgame@k8sindocker ~]$ 

以上就完成資料庫叢集的設置了。


不過,為了接觸這組服務,EDB K8s operator 有附一個管理 CNP 資料庫叢集的 kubectl 外掛 kubectl cnp,這邊就把這裝起來。
[goodgame@k8sindocker ~]$ curl -sSfL https://github.com/EnterpriseDB/kubectl-cnp/raw/main/install.sh -o install.sh
[goodgame@k8sindocker ~]$ 
[goodgame@k8sindocker ~]$ chmod +x install.sh 
[goodgame@k8sindocker ~]$ sudo ./install.sh  -b /usr/local/bin
EnterpriseDB/kubectl-cnp info checking GitHub for latest tag
EnterpriseDB/kubectl-cnp info found version: 1.7.1 for v1.7.1/linux/x86_64
EnterpriseDB/kubectl-cnp info installed /usr/local/bin/kubectl-cnp
[goodgame@k8sindocker ~]$ 

查看資料庫叢集狀態
[goodgame@k8sindocker ~]$ kubectl cnp status cluster-example
Cluster in healthy state   
Name:              cluster-example
Namespace:         default
PostgreSQL Image:  quay.io/enterprisedb/postgresql:13.3
Primary instance:  cluster-example-1
Instances:         3
Ready instances:   3

Instances status
Pod name           Current LSN  Received LSN  Replay LSN  System ID            Primary  Replicating  Replay paused  Pending restart  Status
--------           -----------  ------------  ----------  ---------            -------  -----------  -------------  ---------------  ------
cluster-example-1  0/5000060                              7006250831794221074  ✓        ✗            ✗              ✗                OK
cluster-example-2               0/5000060     0/5000060   7006250831794221074  ✗        ✓            ✗              ✗                OK
cluster-example-3               0/5000060     0/5000060   7006250831794221074  ✗        ✓            ✗              ✗                OK
[goodgame@k8sindocker ~]$ 

來看一下這個 plugin 提供的功能。
[goodgame@k8sindocker ~]$ kubectl cnp --help
A plugin to manage your Cloud Native PostgreSQL clusters

Usage:
  kubectl-cnp [command]

Available Commands:
  certificate Create a client certificate to connect to PostgreSQL using TLS and Certificate authentication
  completion  generate the autocompletion script for the specified shell
  help        Help about any command
  promote     Promote the pod named [cluster]-[node] or [node] to primary
  reload      Reload the cluster
  restart     Restart the cluster
  status      Get the status of a PostgreSQL cluster
  version     Prints version, commit sha and date of the build

Flags:
      --as string                      Username to impersonate for the operation
      --as-group stringArray           Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
      --cache-dir string               Default cache directory (default "/home/goodgame/.kube/cache")
      --certificate-authority string   Path to a cert file for the certificate authority
      --client-certificate string      Path to a client certificate file for TLS
      --client-key string              Path to a client key file for TLS
      --cluster string                 The name of the kubeconfig cluster to use
      --context string                 The name of the kubeconfig context to use
  -h, --help                           help for kubectl-cnp
      --insecure-skip-tls-verify       If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
      --kubeconfig string              Path to the kubeconfig file to use for CLI requests.
  -n, --namespace string               If present, the namespace scope for this CLI request
      --request-timeout string         The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
  -s, --server string                  The address and port of the Kubernetes API server
      --tls-server-name string         Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used
      --token string                   Bearer token for authentication to the API server
      --user string                    The name of the kubeconfig user to use

Use "kubectl-cnp [command] --help" for more information about a command.
[goodgame@k8sindocker ~]$ 

以上從 K8s 觀察資料庫同步資訊之後,然後再來是想辦法進去資料庫裡面,檢驗一下同步。進入 Container 的方法在這 Get a Shell to a Running Container | Kubernetes
不過值得一提的是,除了資料目錄(PGDATA)之外,Container 裡面是唯讀的 OS 環境。
[goodgame@k8sindocker ~]$ kubectl exec --stdin --tty cluster-example-1 -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
bash-4.4$ hostname
cluster-example-1
bash-4.4$ 
bash-4.4$ psql 
psql (13.3)
Type "help" for help.

postgres=# \l
                             List of databases
   Name    |  Owner   | Encoding  | Collate | Ctype |   Access privileges   
-----------+----------+-----------+---------+-------+-----------------------
 app       | app      | SQL_ASCII | C       | C     | 
 postgres  | postgres | SQL_ASCII | C       | C     | 
 template0 | postgres | SQL_ASCII | C       | C     | =c/postgres          +
           |          |           |         |       | postgres=CTc/postgres
 template1 | postgres | SQL_ASCII | C       | C     | =c/postgres          +
           |          |           |         |       | postgres=CTc/postgres
(4 rows)

postgres=# select * from pg_stat_replication ;
 pid | usesysid |      usename      | application_name  | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state |          reply_time           
-----+----------+-------------------+-------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+------------+-----------+-----------+------------+---------------+------------+-------------------------------
 201 |    16386 | streaming_replica | cluster-example-2 | 10.244.1.5  |                 |       49320 | 2021-09-10 10:34:02.023046+00 |              | streaming | 0/8000000 | 0/8000000 | 0/8000000 | 0/8000000  |           |           |            |             0 | async      | 2021-09-10 13:11:19.97608+00
 343 |    16386 | streaming_replica | cluster-example-3 | 10.244.1.8  |                 |       44016 | 2021-09-10 10:34:14.565879+00 |              | streaming | 0/8000000 | 0/8000000 | 0/8000000 | 0/8000000  |           |           |            |             0 | async      | 2021-09-10 13:11:20.267473+00
(2 rows)

postgres=# 
postgres=# select * from pg_hba_file_rules ;
 line_number |  type   |   database    |      user_name      | address | netmask | auth_method |        options         | error 
-------------+---------+---------------+---------------------+---------+---------+-------------+------------------------+-------
           2 | local   | {all}         | {all}               |         |         | peer        | {map=local}            | 
           5 | hostssl | {postgres}    | {streaming_replica} | all     |         | cert        | {clientcert=verify-ca} | 
           6 | hostssl | {replication} | {streaming_replica} | all     |         | cert        | {clientcert=verify-ca} | 
           9 | host    | {all}         | {all}               | all     |         | md5         |                        | 
(4 rows)

postgres=# \q
bash-4.4$ ls /var/lib/pgsql/13/data/
bash-4.4$ ls /var/lib/postgresql/data/pgdata/
PG_VERSION        pg_commit_ts   pg_notify     pg_subtrans           postgresql.conf
base              pg_dynshmem    pg_replslot   pg_tblspc             postmaster.opts
current_logfiles  pg_hba.conf    pg_serial     pg_twophase           postmaster.pid
custom.conf       pg_ident.conf  pg_snapshots  pg_wal
global            pg_logical     pg_stat       pg_xact
log               pg_multixact   pg_stat_tmp   postgresql.auto.conf
bash-4.4$ 
bash-4.4$ cat /var/lib/postgresql/data/pgdata/custom.conf 
archive_command = '/controller/manager wal-archive %p'
archive_mode = 'on'
archive_timeout = '5min'
cluster_name = 'cluster-example'
full_page_writes = 'on'
hot_standby = 'true'
listen_addresses = '*'
log_destination = 'csvlog'
log_directory = '/controller/log'
log_filename = 'postgres'
log_rotation_age = '0'
log_rotation_size = '0'
log_truncate_on_rotation = 'false'
logging_collector = 'on'
max_parallel_workers = '32'
max_replication_slots = '32'
max_worker_processes = '32'
port = '5432'
shared_preload_libraries = ''
ssl = 'on'
ssl_ca_file = '/controller/certificates/client-ca.crt'
ssl_cert_file = '/controller/certificates/server.crt'
ssl_key_file = '/controller/certificates/server.key'
unix_socket_directories = '/controller/run'
wal_keep_size = '512MB'
wal_level = 'logical'
wal_log_hints = 'on'
cnp.config_sha256 = '058ae20f8aa96974535cbf449X1X4XcXabaXb74e6Xe0XXcb81a85Xe77d34197f'
bash-4.4$ 
[goodgame@k8sindocker ~]$ kubectl exec --stdin --tty cluster-example-2 -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
bash-4.4$ hostname
cluster-example-2
bash-4.4$ psql -c 'select pg_is_in_recovery();' -c 'show restore_command ;'
 pg_is_in_recovery 
-------------------
 t
(1 row)

            restore_command            
---------------------------------------
 /controller/manager wal-restore %f %p
(1 row)

bash-4.4$ 

最後,作一個簡易高可用切換測試:這邊進入 primary DB 的 container 執行 pg_ctl stop 指令後,在回到外面觀察 CNP 叢集狀態
[brandon_hsu@k8sindocker ~]$ kubectl exec --stdin --tty cluster-example-1 -- /bin/bash
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
bash-4.4$ pg_ctl stop
waiting for server to shut down....command terminated with exit code 137
[brandon_hsu@k8sindocker ~]$ 
[brandon_hsu@k8sindocker ~]$ kubectl cnp status cluster-example
Cluster in healthy state   
Name:              cluster-example
Namespace:         default
PostgreSQL Image:  quay.io/enterprisedb/postgresql:13.3
Primary instance:  cluster-example-2
Instances:         3
Ready instances:   3

Instances status
Pod name           Current LSN  Received LSN  Replay LSN  System ID            Primary  Replicating  Replay paused  Pending restart  Status
--------           -----------  ------------  ----------  ---------            -------  -----------  -------------  ---------------  ------
cluster-example-1               0/8004958     0/8004958   7006250831794221074  ✗        ✓            ✗              ✗                OK
cluster-example-2  0/8004958                              7006250831794221074  ✓        ✗            ✗              ✗                OK
cluster-example-3               0/8004958     0/8004958   7006250831794221074  ✗        ✓            ✗              ✗                OK
[brandon_hsu@k8sindocker ~]$ 

以上觀察到,Primary 切換到第二個 pod,而第一個 pod 也自動轉換為 standby DB 復活了~

以上簡單測試就到這結束~


參考資料
都附在上面惹~

沒有留言:

張貼留言