在 AKS 上测试和验证 PostgreSQL 数据库

在本文中,你将对新部署的 PostgreSQL 数据库执行各种测试和验证步骤。

重要

AKS 文档和示例中都提到了开源软件。 AKS 服务级别协议、有限保修和 Azure 支持不涵盖你部署的软件。 将开源技术与 AKS 一起使用时,请查阅相应社区和项目维护者提供的支持选项来制定计划。

例如,Ray GitHub 存储库描述了多个在响应时间、用途和支持级别方面各不相同的平台。

Microsoft 将负责生成我们在 AKS 上部署的开源包。 该责任包括对生成、扫描、签名、验证和修补过程拥有完整的所有权,以及对容器映像中的二进制文件的控制。 如需了解详细信息,请参阅 AKS 漏洞管理AKS 支持范围

检查已部署的 PostgreSQL 群集

使用 kubectl get 命令检索 AKS 节点详细信息,验证 PostgreSQL 是否跨多个可用性区域分布。

kubectl get nodes \
    --context $AKS_PRIMARY_CLUSTER_NAME \
    --namespace $PG_NAMESPACE \
    --output json | jq '.items[] | {node: .metadata.name, zone: .metadata.labels."failure-domain.beta.kubernetes.io/zone"}'

输出应与下面的示例输出类似,其中显示了每个节点的可用性区域:

{
    "node": "aks-postgres-15810965-vmss000000",
    "zone": "chinanorth3-1"
}
{
    "node": "aks-postgres-15810965-vmss000001",
    "zone": "chinanorth3-2"
}
{
    "node": "aks-postgres-15810965-vmss000002",
    "zone": "chinanorth3-3"
}
{
    "node": "aks-systempool-26112968-vmss000000",
    "zone": "chinanorth3-1"
}
{
    "node": "aks-systempool-26112968-vmss000001",
    "zone": "chinanorth3-2"
}

连接到 PostgreSQL 并创建示例数据集

在本部分,你将创建一个表,并在前面部署的 CNPG 群集 CRD 中创建的应用数据库中插入一些数据。 使用此数据来验证 PostgreSQL 群集的备份和还原操作。

  • 使用以下命令创建表并在应用数据库中插入数据:

    kubectl cnpg psql $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE
    
    # Run the following PSQL commands to create a small dataset
    # postgres=#
    
    CREATE TABLE datasample (id INTEGER,name VARCHAR(255));
    INSERT INTO datasample (id, name) VALUES (1, 'John');
    INSERT INTO datasample (id, name) VALUES (2, 'Jane');
    INSERT INTO datasample (id, name) VALUES (3, 'Alice');
    SELECT COUNT(*) FROM datasample;
    
    # Type \q to exit psql
    

    输出应与下面的示例输出类似:

    CREATE TABLE
    INSERT 0 1
    INSERT 0 1
    INSERT 0 1
    count
    -------
        3
    (1 row)
    

连接到 PostgreSQL 只读副本

  • 使用以下命令连接到 PostgreSQL 只读副本并验证示例数据集:

    kubectl cnpg psql --replica $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE
    
    #postgres=#
    SELECT pg_is_in_recovery();
    

    示例输出

    # pg_is_in_recovery
    #-------------------
    # t
    #(1 row)
    
    #postgres=#
    SELECT COUNT(*) FROM datasample;
    

    示例输出

    # count
    #-------
    #     3
    #(1 row)
    
    # Type \q to exit psql
    

使用 Barman 设置按需和计划的 PostgreSQL 备份

  1. 使用以下命令验证 PostgreSQL 群集是否可访问 CNPG 群集 CRD 中指定的 Azure 存储帐户,以及 Working WAL archiving 是否报告为 OK

    kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME 1 \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    

    示例输出

    Continuous Backup status
    First Point of Recoverability:  Not Available
    Working WAL archiving:          OK
    WALs waiting to be archived:    0
    Last Archived WAL:              00000001000000000000000A   @   2024-07-09T17:18:13.982859Z
    Last Failed WAL:                -
    
  2. 使用 YAML 文件和 kubectl apply 命令将按需备份部署到 Azure 存储,该备份使用 AKS 工作负载标识集成。

    export BACKUP_ONDEMAND_NAME="on-demand-backup-1"
    
    cat <<EOF | kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE -v 9 -f -
    apiVersion: postgresql.cnpg.io/v1
    kind: Backup
    metadata:
      name: $BACKUP_ONDEMAND_NAME
    spec:
      method: barmanObjectStore
      cluster:
        name: $PG_PRIMARY_CLUSTER_NAME
    EOF
    
  3. 使用 kubectl describe 命令验证按需备份的状态。

    kubectl describe backup $BACKUP_ONDEMAND_NAME \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    

    示例输出

    Type    Reason     Age   From                   Message
     ----    ------     ----  ----                   -------
    Normal  Starting   6s    cloudnative-pg-backup  Starting backup for cluster pg-primary-cnpg-r8c7unrw
    Normal  Starting   5s    instance-manager       Backup started
    Normal  Completed  1s    instance-manager       Backup completed
    
  4. 使用以下命令验证群集是否具有第一个可恢复性点:

    kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME 1 \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    

    示例输出

    Continuous Backup status
    First Point of Recoverability:  2024-06-05T13:47:18Z
    Working WAL archiving:          OK
    
  5. 使用 YAML 文件与 命令配置每小时在整点过 15 分kubectl apply执行的计划备份。

    export BACKUP_SCHEDULED_NAME="scheduled-backup-1"
    
    cat <<EOF | kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE -v 9 -f -
    apiVersion: postgresql.cnpg.io/v1
    kind: ScheduledBackup
    metadata:
      name: $BACKUP_SCHEDULED_NAME
    spec:
      # Backup once per hour
      schedule: "0 15 * ? * *"
      backupOwnerReference: self
      cluster:
        name: $PG_PRIMARY_CLUSTER_NAME
    EOF
    
  6. 使用 kubectl describe 命令验证计划备份的状态。

    kubectl describe scheduledbackup $BACKUP_SCHEDULED_NAME \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    
  7. 使用 az storage blob list 命令查看存储在主群集的 Azure Blob 存储上的备份文件。

    az storage blob list \
        --account-name $PG_PRIMARY_STORAGE_ACCOUNT_NAME \
        --container-name backups \
        --query "[*].name" \
        --only-show-errors
    

    输出应与下面的示例输出类似,验证备份是否成功:

    [
      "pg-primary-cnpg-r8c7unrw/base/20240605T134715/backup.info",
      "pg-primary-cnpg-r8c7unrw/base/20240605T134715/data.tar",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000001",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000002",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000003",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000003.00000028.backup",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000004",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000005",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000005.00000028.backup",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000006",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000007",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000008",
      "pg-primary-cnpg-r8c7unrw/wals/0000000100000000/000000010000000000000009"
    ]
    

将按需备份还原到新的 PostgreSQL 群集

在本部分中,将使用 CNPG 运算符创建的按需备份还原到使用启动群集 CRD 的新实例中。 为了简单起见,使用单个实例群集。 请记住,AKS 工作负载标识(通过 CNPG inheritFromAzureAD)访问备份文件,并且恢复群集名称用于生成特定于恢复群集的新 Kubernetes 服务帐户。

你还可以创建另一个联合凭据,将新的恢复群集服务帐户映射到拥有 Blob 存储上的备份文件的“存储 Blob 数据参与者”访问权限的现有 UAMI。

  1. 使用 az identity federated-credential create 命令另外创建一个联合标识凭据。

    export PG_PRIMARY_CLUSTER_NAME_RECOVERED="$PG_PRIMARY_CLUSTER_NAME-recovered-db"
    
    az identity federated-credential create \
        --name $PG_PRIMARY_CLUSTER_NAME_RECOVERED \
        --identity-name $AKS_UAMI_CLUSTER_IDENTITY_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --issuer "${AKS_PRIMARY_CLUSTER_OIDC_ISSUER}" \
        --subject system:serviceaccount:"${PG_NAMESPACE}":"${PG_PRIMARY_CLUSTER_NAME_RECOVERED}" \
        --audience api://AzureADTokenExchange
    
  2. 使用群集 CRD 和 kubectl apply 命令还原按需备份。

    cat <<EOF | kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE -v 9 -f -
    apiVersion: postgresql.cnpg.io/v1
    kind: Cluster
    metadata:
      name: $PG_PRIMARY_CLUSTER_NAME_RECOVERED
    spec:
    
      inheritedMetadata:
        annotations:
          service.beta.kubernetes.io/azure-dns-label-name: $AKS_PRIMARY_CLUSTER_PG_DNSPREFIX
        labels:
          azure.workload.identity/use: "true"
    
      instances: 1
    
      affinity:
        nodeSelector:
          workload: postgres
    
      # Point to cluster backup created earlier and stored on Azure Blob Storage
      bootstrap:
        recovery:
          source: clusterBackup
    
      storage:
        size: 2Gi
        pvcTemplate:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 2Gi
          storageClassName: managed-csi-premium
          volumeMode: Filesystem
    
      walStorage:
        size: 2Gi
        pvcTemplate:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 2Gi
          storageClassName: managed-csi-premium
          volumeMode: Filesystem
    
      serviceAccountTemplate:
        metadata:
          annotations:
            azure.workload.identity/client-id: "$AKS_UAMI_WORKLOAD_CLIENTID"
          labels:
            azure.workload.identity/use: "true"
    
      externalClusters:
        - name: clusterBackup
          barmanObjectStore:
            destinationPath: https://${PG_PRIMARY_STORAGE_ACCOUNT_NAME}.blob.core.chinacloudapi.cn/backups
            serverName: $PG_PRIMARY_CLUSTER_NAME
            azureCredentials:
              inheritFromAzureAD: true
            wal:
              maxParallel: 8
    EOF
    
  3. 连接到恢复的实例,然后使用以下命令验证在进行完整备份的原始群集上创建的数据集是否存在:

    kubectl cnpg psql $PG_PRIMARY_CLUSTER_NAME_RECOVERED --namespace $PG_NAMESPACE
    
    postgres=# SELECT COUNT(*) FROM datasample;
    

    示例输出

    # count
    #-------
    #     3
    #(1 row)
    
    # Type \q to exit psql
    
  4. 使用以下命令删除恢复的群集:

    kubectl cnpg destroy $PG_PRIMARY_CLUSTER_NAME_RECOVERED 1 \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    
  5. 使用 az identity federated-credential delete 命令删除联合标识凭据。

    az identity federated-credential delete \
        --name $PG_PRIMARY_CLUSTER_NAME_RECOVERED \
        --identity-name $AKS_UAMI_CLUSTER_IDENTITY_NAME \
        --resource-group $RESOURCE_GROUP_NAME \
        --yes
    

使用公共负载均衡器公开 PostgreSQL 群集

在本部分,你将配置必要的基础结构,以向客户端工作站的公共 IP 地址公开具有 IP 源限制的 PostgreSQL 读写和只读终结点。

你还从 Cluster IP 服务中检索到以下终结点:

  • 一个主要的读写终结点,以*-rw结尾。
  • 零到 N(具体取决于副本数)个以 *-ro 结尾的只读终结点。
  • 一个以 结尾的复制终结点。*-r
  1. 使用 kubectl get 命令获取群集 IP 服务详细信息。

    kubectl get services \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE \
        -l cnpg.io/cluster=$PG_PRIMARY_CLUSTER_NAME
    

    示例输出

    NAME                          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
    pg-primary-cnpg-sryti1qf-r    ClusterIP   10.0.193.27    <none>        5432/TCP   3h57m
    pg-primary-cnpg-sryti1qf-ro   ClusterIP   10.0.237.19    <none>        5432/TCP   3h57m
    pg-primary-cnpg-sryti1qf-rw   ClusterIP   10.0.244.125   <none>        5432/TCP   3h57m
    

    注释

    有三个服务:namespace/cluster-name-ro(映射到端口 5433)、namespace/cluster-name-rwnamespace/cluster-name-r(映射到端口 5433)。 请务必避免使用与 PostgreSQL 数据库群集的读/写节点相同的端口。 如果希望应用程序仅访问 PostgreSQL 数据库群集的只读副本,请将其定向到端口 5433。 最终服务通常用于数据备份,但也可用作只读节点。

  2. 使用 kubectl get 命令获取服务详细信息。

    export PG_PRIMARY_CLUSTER_RW_SERVICE=$(kubectl get services \
        --namespace $PG_NAMESPACE \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        -l "cnpg.io/cluster" \
        --output json | jq -r '.items[] | select(.metadata.name | endswith("-rw")) | .metadata.name')
    
    echo $PG_PRIMARY_CLUSTER_RW_SERVICE
    
    export PG_PRIMARY_CLUSTER_RO_SERVICE=$(kubectl get services \
        --namespace $PG_NAMESPACE \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        -l "cnpg.io/cluster" \
        --output json | jq -r '.items[] | select(.metadata.name | endswith("-ro")) | .metadata.name')
    
    echo $PG_PRIMARY_CLUSTER_RO_SERVICE
    
  3. 使用 kubectl apply 命令和以下 YAML 文件配置负载均衡器服务。

    cat <<EOF | kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME -f -
    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/azure-load-balancer-resource-group: $AKS_PRIMARY_CLUSTER_NODERG_NAME
        service.beta.kubernetes.io/azure-pip-name: $AKS_PRIMARY_CLUSTER_PUBLICIP_NAME
        service.beta.kubernetes.io/azure-dns-label-name: $AKS_PRIMARY_CLUSTER_PG_DNSPREFIX
      name: cnpg-cluster-load-balancer-rw
      namespace: "${PG_NAMESPACE}"
    spec:
      type: LoadBalancer
      ports:
      - protocol: TCP
        port: 5432
        targetPort: 5432
      selector:
        cnpg.io/instanceRole: primary
        cnpg.io/podRole: instance
      loadBalancerSourceRanges:
      - "$MY_PUBLIC_CLIENT_IP/32"
    EOF
    
    cat <<EOF | kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME -f -
    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/azure-load-balancer-resource-group: $AKS_PRIMARY_CLUSTER_NODERG_NAME
        service.beta.kubernetes.io/azure-pip-name: $AKS_PRIMARY_CLUSTER_PUBLICIP_NAME
        service.beta.kubernetes.io/azure-dns-label-name: $AKS_PRIMARY_CLUSTER_PG_DNSPREFIX
      name: cnpg-cluster-load-balancer-ro
      namespace: "${PG_NAMESPACE}"
    spec:
      type: LoadBalancer
      ports:
      - protocol: TCP
        port: 5433
        targetPort: 5432
      selector:
        cnpg.io/instanceRole: replica
        cnpg.io/podRole: instance
      loadBalancerSourceRanges:
      - "$MY_PUBLIC_CLIENT_IP/32"
    EOF
    
  4. 使用 kubectl describe 命令获取服务详细信息。

    kubectl describe service cnpg-cluster-load-balancer-rw \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    
    kubectl describe service cnpg-cluster-load-balancer-ro \
        --context $AKS_PRIMARY_CLUSTER_NAME \
        --namespace $PG_NAMESPACE
    
    export AKS_PRIMARY_CLUSTER_ALB_DNSNAME="$(az network public-ip show \
        --resource-group $AKS_PRIMARY_CLUSTER_NODERG_NAME \
        --name $AKS_PRIMARY_CLUSTER_PUBLICIP_NAME \
        --query "dnsSettings.fqdn" --output tsv)"
    
    echo $AKS_PRIMARY_CLUSTER_ALB_DNSNAME
    

验证公共 PostgreSQL 端点

在本部分中,你需要验证 Azure 负载均衡器是否已正确设置,方法是使用先前创建的静态 IP 将连接路由到主要的读写副本和只读副本。然后,你将使用 psql CLI 连接到这两种副本。

请记住,主要读写终结点映射到 TCP 端口 5432,只读副本终结点映射到端口 5433,以允许读取器和编写器使用相同的 PostgreSQL DNS 名称。

注释

需要之前生成的 PostgreSQL 基本身份验证的应用用户密码的值,并将其存储在 $PG_DATABASE_APPUSER_SECRET 环境变量中。

  • 使用以下 psql 命令验证公共 PostgreSQL 终结点:

    echo "Public endpoint for PostgreSQL cluster: $AKS_PRIMARY_CLUSTER_ALB_DNSNAME"
    
    # Query the primary, pg_is_in_recovery = false
    
    psql -h $AKS_PRIMARY_CLUSTER_ALB_DNSNAME \
        -p 5432 -U app -d appdb -W -c "SELECT pg_is_in_recovery();"
    

    示例输出

    pg_is_in_recovery
    -------------------
     f
    (1 row)
    
    echo "Query a replica, pg_is_in_recovery = true"
    
    psql -h $AKS_PRIMARY_CLUSTER_ALB_DNSNAME \
        -p 5433 -U app -d appdb -W -c "SELECT pg_is_in_recovery();"
    

    示例输出

    # Example output
    
    pg_is_in_recovery
    -------------------
    t
    (1 row)
    

    成功连接到主要读写终结点时,PostgreSQL 函数会返回 f 表示 false,指出当前连接是可写的。

    连接到副本时,该函数返回 t true,这表示数据库正在恢复且为只读。

模拟计划外故障转移

在本部分,通过删除运行主终结点的 Pod 来触发突然失败,这模拟了与托管 PostgreSQL 主终结点的节点的网络连接突然崩溃或断开的情况。

  1. 使用以下命令检查正在运行的 Pod 实例的状态:

    kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE
    

    示例输出

    Name                        Current LSN Rep role        Status  Node
    --------------------------- ----------- --------        ------- -----------
    pg-primary-cnpg-sryti1qf-1  0/9000060   Primary         OK      aks-postgres-32388626-vmss000000
    pg-primary-cnpg-sryti1qf-2  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000001
    pg-primary-cnpg-sryti1qf-3  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000002
    
  2. 使用 kubectl delete 命令删除主容器组。

    PRIMARY_POD=$(kubectl get pod \
        --namespace $PG_NAMESPACE \
        --no-headers \
        -o custom-columns=":metadata.name" \
        -l role=primary)
    
    kubectl delete pod $PRIMARY_POD --grace-period=1 --namespace $PG_NAMESPACE
    
  3. 使用以下命令验证 pg-primary-cnpg-sryti1qf-2 Pod 实例现在是主实例:

    kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE
    

    示例输出

    pg-primary-cnpg-sryti1qf-2  0/9000060   Primary         OK      aks-postgres-32388626-vmss000001
    pg-primary-cnpg-sryti1qf-1  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000000
    pg-primary-cnpg-sryti1qf-3  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000002
    
  4. 使用以下命令将 pg-primary-cnpg-sryti1qf-1 Pod 实例重置为主实例:

    kubectl cnpg promote $PG_PRIMARY_CLUSTER_NAME 1 --namespace $PG_NAMESPACE
    
  5. 使用以下命令验证 Pod 实例在计划外故障转移测试之前是否已返回到其原始状态:

    kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE
    

    示例输出

    Name                        Current LSN Rep role        Status  Node
    --------------------------- ----------- --------        ------- -----------
    pg-primary-cnpg-sryti1qf-1  0/9000060   Primary         OK      aks-postgres-32388626-vmss000000
    pg-primary-cnpg-sryti1qf-2  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000001
    pg-primary-cnpg-sryti1qf-3  0/9000060   Standby (sync)  OK      aks-postgres-32388626-vmss000002
    

清理资源

  • 完成部署评审后,使用 az group delete 命令删除本指南中创建的所有资源。

    az group delete --resource-group $RESOURCE_GROUP_NAME --no-wait --yes
    

后续步骤

通过本操作指南,我们已学会了:

  • 使用 Azure CLI 创建多区域 AKS 群集。
  • 使用 CNPG operator 部署高可用性 PostgreSQL 群集和数据库。
  • 使用 Prometheus 和 Grafana 设置 PostgreSQL 的监控。
  • 将示例数据集部署到 PostgreSQL 数据库。
  • 模拟群集中断和 PostgreSQL 副本故障转移。
  • 执行 PostgreSQL 数据库的备份和还原。

若要详细了解如何对工作负载使用 AKS,请参阅什么是 Azure Kubernetes 服务 (AKS)?

供稿人

Microsoft维护本文。 以下贡献者最初撰写了这篇文章:

  • Ken Kilty | 首席技术项目经理 (TPM)
  • Russell de Pina | 主要 TPM
  • Adrian Joian | 高级客户工程师
  • Jenny Hayes | 高级内容开发人员
  • Carol Smith | 高级内容开发人员
  • Erin Schaffer | 内容开发人员 2
  • Adam Sharif | 客户工程师 2