使用 CloudNativePG 将 PostgreSQL 部署到 GKE


本指南介绍了如何使用 CloudNativePG 操作器在 Google Kubernetes Engine (GKE) 上部署 PostgreSQL 集群。

PostgreSQL 是一种开源对象关系型数据库,经过了数十年的积极开发,可确保稳定的客户端性能。它提供一系列功能,包括复制、时间点恢复、安全功能和扩展性。PostgreSQL 与主要操作系统兼容,并完全符合 ACID(原子性、一致性、隔离性、耐用性)标准。

本指南适用于有意在 GKE 上部署 Postgres 集群的平台管理员、云架构师和运营专家。在 GKE 中运行 Postgres(而不是使用 Cloud SQL)可以为经验丰富的数据库管理员提供更多灵活性和配置控制。

优势

CloudNativePG 是由 EBD 根据 Apache 2 许可开发的开源操作器。它为 PostgreSQL 部署引入了以下功能:

  • 用于管理和配置 PostgreSQL 集群的声明式 Kubernetes 原生方法
  • 使用卷快照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的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。

新 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. 如果您使用的是外部身份提供方 (IdP),则必须先使用联合身份登录 gcloud CLI

  4. 如需初始化 gcloud CLI,请运行以下命令:

    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. Verify 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. 如果您使用的是外部身份提供方 (IdP),则必须先使用联合身份登录 gcloud CLI

  10. 如需初始化 gcloud CLI,请运行以下命令:

    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. Verify 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 the following:

    • PROJECT_ID: your project ID.
    • USER_IDENTIFIER: the identifier for your user account—for example, myemail@example.com.
    • ROLE: the IAM role that you grant to your user account.
  15. 设置环境

    如需设置您的环境,请按以下步骤操作:

    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 集群。

    您可以使用 Standard 或 Autopilot 集群安装操作器。

    Standard

    下图显示了部署在三个不同可用区中的专用区域级 Standard 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 节点的 VPC 网络和专用子网
    • 用于通过 NAT 访问互联网的路由器
    • 专用 GKE 集群(在 us-central1 区域中)
    • 启用了自动扩缩的节点池(每个可用区一个到两个节点,每个可用区最少一个节点)

    输出类似于以下内容:

    ...
    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 节点的 VPC 网络和专用子网
    • 用于通过 NAT 访问互联网的路由器
    • 专用 GKE 集群(在 us-central1 区域中)
    • 具有日志记录和监控权限的 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 资源
    • 包含用户凭证的 Secret,用于访问数据库以及在 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 Service:

      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 Service 登录数据库以建立具有读写权限的连接:

      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 Service 登录数据库以验证只读权限。此 Service 允许查询数据,但限制任何写入操作:

      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 页面:

      转到 Metrics Explorer

      信息中心显示非零指标提取率。

    3. 选择一个指标中,输入 Prometheus Target(Prometheus 目标)。

    4. 活跃指标类别部分中,选择 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 的参考架构、图表和最佳做法。查看我们的 Cloud 架构中心