在本文中,你将对新部署的 PostgreSQL 数据库执行各种测试和验证步骤。
- 如果尚未部署 PostgreSQL,请按照使用 Azure CLI 在 AKS 上部署高度可用的 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 备份
使用以下命令验证 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: -
使用 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
使用
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
使用以下命令验证群集是否具有第一个可恢复性点:
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
使用 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
使用
kubectl describe
命令验证计划备份的状态。kubectl describe scheduledbackup $BACKUP_SCHEDULED_NAME \ --context $AKS_PRIMARY_CLUSTER_NAME \ --namespace $PG_NAMESPACE
使用
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。
使用
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
使用群集 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
连接到恢复的实例,然后使用以下命令验证在进行完整备份的原始群集上创建的数据集是否存在:
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
使用以下命令删除恢复的群集:
kubectl cnpg destroy $PG_PRIMARY_CLUSTER_NAME_RECOVERED 1 \ --context $AKS_PRIMARY_CLUSTER_NAME \ --namespace $PG_NAMESPACE
使用
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
使用
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-rw
和namespace/cluster-name-r
(映射到端口 5433)。 请务必避免使用与 PostgreSQL 数据库群集的读/写节点相同的端口。 如果希望应用程序仅访问 PostgreSQL 数据库群集的只读副本,请将其定向到端口 5433。 最终服务通常用于数据备份,但也可用作只读节点。使用
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
使用
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
使用
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 主终结点的节点的网络连接突然崩溃或断开的情况。
使用以下命令检查正在运行的 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
使用
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
使用以下命令验证
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
使用以下命令将
pg-primary-cnpg-sryti1qf-1
Pod 实例重置为主实例:kubectl cnpg promote $PG_PRIMARY_CLUSTER_NAME 1 --namespace $PG_NAMESPACE
使用以下命令验证 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