使用 CloudNativePG 將 PostgreSQL 部署至 GKE


本指南說明如何使用 CloudNativePG 運算子,在 Google Kubernetes Engine (GKE) 上部署 PostgreSQL 叢集。

PostgreSQL 是開放原始碼的物件關聯式資料庫,經過數十年的積極開發,可確保用戶端效能穩定。這項服務提供多種功能,包括複製、時間點復原、安全防護功能和擴充性。PostgreSQL 與主要作業系統相容,且完全符合 ACID (不可分割性、一致性、隔離性、耐用性) 標準。

本指南適用於有興趣在 GKE 上部署 Postgres 叢集的平台管理員、雲端架構師和營運專員。與使用 Cloud SQL 相比,在 GKE 中執行 Postgres 可為經驗豐富的資料庫管理員提供更多彈性和設定控制權。

優點

CloudNativePG 是由 EDB 根據 Apache 2 授權開發的開放原始碼運算子。為 PostgreSQL 部署作業帶來下列功能:

  • 以 Kubernetes 原生的宣告式方式管理及設定 PostgreSQL 叢集
  • 使用磁碟區快照Cloud Storage 管理備份
  • 傳輸中加密 TLS 連線、使用自有憑證授權單位,以及與 Certificate Manager 整合,自動核發及輪替 TLS 憑證
  • PostgreSQL 次要版本更新
  • 使用 Kubernetes API 伺服器維護 PostgreSQL 叢集狀態和容錯移轉,以提供高可用性,不需額外工具
  • 透過以 SQL 編寫的使用者定義指標,內建 Prometheus 匯出工具設定

目標

  • 規劃及部署 Postgres 適用的 GKE 基礎架構
  • 使用 Helm 部署及設定 CloudNativePG Postgres 運算子
  • 部署 PostgreSQL 叢集
  • 設定 PostgreSQL 驗證和可觀測性

部署架構

PostgreSQL 提供多種部署選項,從獨立資料庫伺服器到複寫的高可用性叢集都有。本教學課程的重點是將高可用性叢集部署至 GKE。

在這個部署作業中,PostgreSQL 叢集工作負載會分散在地區性 GKE 叢集內的多個可用區,確保高可用性和備援能力。詳情請參閱區域叢集

下圖顯示在 GKE 叢集的多個節點和區域中執行的 Postgres 叢集:

GKE 上的 Postgres 叢集

  • 預設設定包含一個主要 PostgreSQL 伺服器和兩個備份伺服器,一旦主要伺服器發生故障,備份伺服器就會接管,確保資料庫持續可用。

  • CloudNativePG 運算子資源會使用 GKE 叢集的獨立命名空間,以利資源隔離,並採用建議的微服務方法,也就是每個 PostgreSQL 叢集使用一個資料庫。資料庫和對應的使用者 (應用程式使用者) 會在代表叢集的 Kubernetes 自訂資源中定義。

  • 討論資料庫時,儲存空間是不可或缺的元件。儲存空間必須有效率地運作、確保持續可用性,並保證資料一致性。因此,我們建議使用以 SSD 磁碟為基礎的 premium-rwo 儲存空間類別。設定 PostgreSQL 叢集的 Pod 時,CloudNativePG 運算子會視需要自動建立 PersistentVolumeClaims

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

完成本文所述工作後,您可以刪除已建立的資源,避免繼續計費。詳情請參閱清除所用資源一節。

事前準備

Cloud Shell 已預先安裝本教學課程所需的軟體,包括 kubectlgcloud CLIHelmTerraform。如果您未使用 Cloud Shell,則必須安裝 gcloud CLI。

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.

  3. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  4. To initialize the gcloud CLI, run the following command:

    gcloud init
  5. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Compute Engine, IAM, GKE, Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com cloudresourcemanager.googleapis.com
  8. Install the Google Cloud CLI.

  9. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  10. To initialize the gcloud CLI, run the following command:

    gcloud init
  11. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  12. Make sure that billing is enabled for your Google Cloud project.

  13. Enable the Compute Engine, IAM, GKE, Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com cloudresourcemanager.googleapis.com
  14. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/compute.securityAdmin, roles/compute.viewer, roles/container.clusterAdmin, roles/container.admin, roles/iam.serviceAccountAdmin, roles/iam.serviceAccountUser

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

設定環境

如要設定環境,請按照下列步驟操作:

  1. 設定環境變數:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    

    PROJECT_ID 替換為您的 Google Cloud 專案 ID

  2. 複製 GitHub 存放區:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. 變更為工作目錄:

    cd kubernetes-engine-samples/databases/postgresql-cloudnativepg
    

建立叢集基礎架構

在本節中,您將執行 Terraform 指令碼,建立高可用性的私人地區 GKE 叢集。

您可以使用標準或 Autopilot 叢集安裝運算子。

標準

下圖顯示部署在三個不同可用區的私有區域標準 GKE 叢集:

如要部署這項基礎架構,請執行下列指令:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply \
-var project_id=${PROJECT_ID}   \
-var region=${REGION}  \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

系統顯示提示訊息時,請輸入 yes。這個指令可能需要幾分鐘才能完成,且叢集會顯示就緒狀態。

Terraform 會建立下列資源:

  • Kubernetes 節點的虛擬私有雲網路和私有子網路
  • 透過 NAT 存取網際網路的路由器
  • us-central1 地區中的私人 GKE 叢集
  • 啟用自動調度的節點集區 (每個可用區一到兩個節點,每個可用區至少一個節點)

輸出結果會與下列內容相似:

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...

Autopilot

下圖顯示私人區域 Autopilot GKE 叢集:

如要部署基礎架構,請執行下列指令:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply \
-var project_id=${PROJECT_ID} \
-var region=${REGION} \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

系統顯示提示訊息時,請輸入 yes。這個指令可能需要幾分鐘才能完成,且叢集會顯示就緒狀態。

Terraform 會建立下列資源:

  • Kubernetes 節點的虛擬私有雲網路和私有子網路
  • 透過 NAT 存取網際網路的路由器
  • us-central1 地區中的私人 GKE 叢集
  • 具備記錄與監控權限的 ServiceAccount
  • 使用 Google Cloud Managed Service for Prometheus 監控叢集

輸出結果會與下列內容相似:

...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
...

連線至叢集

設定 kubectl 與叢集通訊:

gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION}

部署 CloudNativePG 運算子

使用 Helm 資訊套件將 CloudNativePG 部署至 Kubernetes 叢集:

  1. 新增 CloudNativePG 運算子 Helm 資訊套件存放區:

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    
  2. 使用 Helm 指令列工具部署 CloudNativePG 運算子:

    helm upgrade --install cnpg \
        --namespace cnpg-system \
        --create-namespace \
        cnpg/cloudnative-pg
    

    輸出結果會與下列內容相似:

    Release "cnpg" does not exist. Installing it now.
    NAME: cnpg
    LAST DEPLOYED: Fri Oct 13 13:52:36 2023
    NAMESPACE: cnpg-system
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    ...
    

部署 Postgres

下列資訊清單說明 CloudNativePG 運算子的自訂資源所定義的 PostgreSQL 叢集:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: gke-pg-cluster
spec:
  description: "Standard GKE PostgreSQL cluster"
  imageName: ghcr.io/cloudnative-pg/postgresql:16.2
  enableSuperuserAccess: true
  instances: 3
  startDelay: 300
  primaryUpdateStrategy: unsupervised
  postgresql:
    pg_hba:
      - host all all 10.48.0.0/20 md5
  bootstrap:
    initdb:
      database: app
  storage:
    storageClass: premium-rwo
    size: 2Gi
  resources:
    requests:
      memory: "1Gi"
      cpu: "1000m"
    limits:
      memory: "1Gi"
      cpu: "1000m"
  affinity:
    enablePodAntiAffinity: true
    tolerations:
    - key: cnpg.io/cluster
      effect: NoSchedule
      value: gke-pg-cluster
      operator: Equal
    additionalPodAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app.component
              operator: In
              values:
              - "pg-cluster"
          topologyKey: topology.kubernetes.io/zone
  monitoring:
    enablePodMonitor: true

這個資訊清單包含下列欄位:

  • spec.instances:叢集 Pod 數量
  • spec.primaryUpdateStrategy:滾動式更新策略:
    • Unsupervised:在副本節點更新後,自動更新主要叢集節點
    • Supervised:主要叢集節點必須手動切換
  • spec.postgresqlpostgres.conf檔案參數覆寫,例如 pg-hba 規則、LDAP,以及同步複本的必要條件。
  • spec.storage:儲存空間相關設定,例如儲存空間類別、磁碟區大小和預先寫入記錄設定。
  • spec.bootstrap:叢集中建立的初始資料庫參數、使用者憑證和資料庫還原選項
  • spec.resources:叢集 Pod 的要求和限制
  • spec.affinity:叢集工作負載的相依性和反相依性規則

建立基本 Postgres 叢集

  1. 建立命名空間:

    kubectl create ns pg-ns
    
  2. 使用自訂資源建立 PostgreSQL 叢集:

    kubectl apply -n pg-ns -f manifests/01-basic-cluster/postgreSQL_cluster.yaml
    

    這個指令可能需要幾分鐘才能完成。

  3. 檢查叢集的狀態:

    kubectl get cluster -n pg-ns --watch
    

    請等待輸出內容顯示 Cluster in healthy state 狀態,再繼續下一個步驟。

    NAME             AGE     INSTANCES   READY   STATUS                     PRIMARY
    gke-pg-cluster   2m53s   3           3       Cluster in healthy state   gke-pg-cluster-1
    

檢查資源

確認 GKE 已為叢集建立資源:

kubectl get cluster,pod,svc,pvc,pdb,secret,cm -n pg-ns

輸出結果會與下列內容相似:

NAME                                        AGE   INSTANCES   READY   STATUS                     PRIMARY
cluster.postgresql.cnpg.io/gke-pg-cluster   32m   3           3       Cluster in healthy state   gke-pg-cluster-1

NAME                   READY   STATUS    RESTARTS   AGE
pod/gke-pg-cluster-1   1/1     Running   0          31m
pod/gke-pg-cluster-2   1/1     Running   0          30m
pod/gke-pg-cluster-3   1/1     Running   0          29m

NAME                        TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/gke-pg-cluster-r    ClusterIP   10.52.11.24   <none>        5432/TCP   32m
service/gke-pg-cluster-ro   ClusterIP   10.52.9.233   <none>        5432/TCP   32m
service/gke-pg-cluster-rw   ClusterIP   10.52.1.135   <none>        5432/TCP   32m

NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/gke-pg-cluster-1   Bound    pvc-bbdd1cdd-bdd9-4e7c-8f8c-1a14a87e5329   2Gi        RWO            standard       32m
persistentvolumeclaim/gke-pg-cluster-2   Bound    pvc-e7a8b4df-6a3e-43ce-beb0-b54ec1d24011   2Gi        RWO            standard       31m
persistentvolumeclaim/gke-pg-cluster-3   Bound    pvc-dac7f931-6ac5-425f-ac61-0cfc55aae72f   2Gi        RWO            standard       30m

NAME                                                MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
poddisruptionbudget.policy/gke-pg-cluster           1               N/A               1                     32m
poddisruptionbudget.policy/gke-pg-cluster-primary   1               N/A               0                     32m

NAME                                TYPE                       DATA   AGE
secret/gke-pg-cluster-app           kubernetes.io/basic-auth   3      32m
secret/gke-pg-cluster-ca            Opaque                     2      32m
secret/gke-pg-cluster-replication   kubernetes.io/tls          2      32m
secret/gke-pg-cluster-server        kubernetes.io/tls          2      32m
secret/gke-pg-cluster-superuser     kubernetes.io/basic-auth   3      32m

NAME                                DATA   AGE
configmap/cnpg-default-monitoring   1      32m
configmap/kube-root-ca.crt          1      135m

運算子會建立下列資源:

  • 代表 PostgreSQL 叢集的叢集自訂資源,由運算子控管
  • 具有對應永久磁碟區的 PersistentVolumeClaim 資源
  • 密鑰,內含存取資料庫和 Postgres 節點間複寫作業的使用者憑證。
  • 三個資料庫端點服務:<name>-rw<name>-ro<name>-r,可連線至叢集。詳情請參閱 PostgreSQL 架構

向 Postgres 進行驗證

您可以連線至 PostgreSQL 資料庫,並透過運算子建立的不同服務端點檢查存取權。為此,您會使用額外的 Pod,其中包含 PostgreSQL 用戶端,以及以環境變數形式掛接的同步應用程式使用者憑證。

  1. 執行用戶端 Pod,與 Postgres 叢集互動:

    kubectl apply -n pg-ns -f manifests/02-auth/pg-client.yaml
    
  2. pg-client Pod 上執行 exec 指令,並登入 gke-pg-cluster-rw 服務:

    kubectl wait --for=condition=Ready -n pg-ns pod/pg-client --timeout=300s
    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  3. 使用 gke-pg-cluster-rw 服務登入資料庫,建立具有讀寫權限的連線:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-rw.pg-ns/app
    

    終端機開頭會顯示資料庫名稱:

    app=>
    
  4. 建立資料表:

    CREATE TABLE travel_agency_clients (
    client VARCHAR ( 50 ) UNIQUE NOT NULL,
    address VARCHAR ( 50 ) UNIQUE NOT NULL,
    phone VARCHAR ( 50 ) UNIQUE NOT NULL);
    
  5. 將資料插入資料表:

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('Tom', 'Warsaw', '+55555')
    RETURNING *;
    
  6. 查看您建立的資料:

    SELECT * FROM travel_agency_clients ;
    

    輸出結果會與下列內容相似:

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  7. 登出目前的資料庫工作階段:

    exit
    
  8. 使用 gke-pg-cluster-ro 服務登入資料庫,確認唯讀存取權。這項服務允許查詢資料,但限制任何寫入作業:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-ro.pg-ns/app
    
  9. 嘗試插入新資料:

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('John', 'Paris', '+55555')
    RETURNING *;
    

    輸出結果會與下列內容相似:

    ERROR:  cannot execute INSERT in a read-only transaction
    
  10. 嘗試讀取資料:

    SELECT * FROM travel_agency_clients ;
    

    輸出結果會與下列內容相似:

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  11. 登出目前的資料庫工作階段:

    exit
    
  12. 退出 Pod shell:

    exit
    

瞭解 Prometheus 如何收集 Postgres 叢集的指標

下圖顯示 Prometheus 指標的收集方式:

在圖表中,GKE 私人叢集包含:

  • Postgres Pod,會收集路徑 / 和通訊埠 9187 的指標
  • 以 Prometheus 為基礎的收集器,可處理來自 Postgres Pod 的指標
  • 將指標傳送至 Cloud Monitoring 的 PodMonitoring 資源

如要啟用從 Pod 收集指標的功能,請按照下列步驟操作:

  1. 建立 PodMonitoring 資源:

    kubectl apply -f manifests/03-observability/pod-monitoring.yaml -n pg-ns
    
  2. 前往 Google Cloud 控制台的「指標探索器」頁面:

    前往 Metrics Explorer

    資訊主頁顯示的指標擷取率不是零。

  3. 在「Select a metric」(選取指標) 中,輸入「Prometheus Target」(Prometheus 目標)

  4. 在「Active Metric Categories」(使用中的指標類別) 專區中,選取「Cnpg」

建立指標資訊主頁

如要將匯出的指標視覺化,請建立指標資訊主頁。

  1. 部署資訊主頁:

    gcloud --project "${PROJECT_ID}" monitoring dashboards create --config-from-file manifests/03-observability/gcp-pg.json
    
  2. 前往 Google Cloud 控制台的「資訊主頁」頁面。

    前往資訊主頁

  3. 選取「PostgresQL Prometheus Overview」(PostgresQL Prometheus 總覽) 資訊主頁。

    如要查看資訊主頁監控函式的方式,您可以重複使用「資料庫驗證」部分中的動作,並對資料庫套用讀取和寫入要求,然後在資訊主頁中查看收集到的指標視覺化資料。

  4. 連線至用戶端 Pod:

    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  5. 插入隨機資料:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-rw.pg-ns/app -c "CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR ( 50 ) NOT NULL);INSERT INTO test (randomdata) VALUES (generate_series(1, 1000));"
    
  6. 重新整理資訊主頁。圖表會更新為實際指標。

  7. 退出 Pod shell:

    exit
    

清除所用資源

刪除專案

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

刪除個別資源

  1. 設定環境變數。

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
  2. 執行 terraform destroy 指令:

    export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
    terraform  -chdir=terraform/FOLDER destroy \
      -var project_id=${PROJECT_ID} \
      -var region=${REGION} \
      -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
    

    FOLDER 替換為 gke-autopilotgke-standard

    系統顯示提示訊息時,請輸入 yes

  3. 找出所有未連接的磁碟:

    export disk_list=$(gcloud compute disks list --filter="-users:* AND labels.name=${KUBERNETES_CLUSTER_PREFIX}-cluster" --format "value[separator=|](name,zone)")
    
  4. 刪除磁碟:

    for i in $disk_list; do
      disk_name=$(echo $i| cut -d'|' -f1)
      disk_zone=$(echo $i| cut -d'|' -f2|sed 's|.*/||')
      echo "Deleting $disk_name"
      gcloud compute disks delete $disk_name --zone $disk_zone --quiet
    done
    

後續步驟

  • 探索 Google Cloud 的參考架構、圖表和最佳做法。 歡迎瀏覽我們的雲端架構中心