1. 简介
在此 Codelab 中,您将使用 RPM 编排器将高可用性 (HA) 集群备份到与 S3 兼容的 AlloyDB Omni 对象存储区。
此 Codelab 使用了 Codelab 使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群中的集群,以在 Compute Engine 虚拟机 (VM) 上创建 HA 集群。在此 Codelab 中,您将在其中一个虚拟机上添加与 S3 兼容的对象存储区服务器。您可以使用 HA 集群的对象存储服务器将数据备份到该服务器,并从该服务器恢复集群。

前提条件
- 启用了结算功能的 Google Cloud 项目。
- HA Codelab 列出的 IAM 角色。
- 具备 Linux 命令和 SQL 方面的基础知识。
学习内容
- 如何安装和运行与 S3 兼容的对象存储区服务器以进行备份。
- 如何创建将备份写入与 S3 兼容的存储空间的备份方案。
- 如何创建按需备份。
- 如何使用备份将集群恢复到某个时间点。
所需条件
- AlloyDB Omni 软件代码库和编排器 Ansible 集成的网址。如需接收这些网址,请填写 AlloyDB Omni 注册表单。如需了解详情,请参阅使用 RPM 的 AlloyDB Omni 概览。您将在 HA Codelab 中使用这些网址。
- 来自使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群 Codelab 的 AlloyDB Omni 高可用性集群。
- 可访问 Google Cloud 的终端和 Cloud Shell。
2. 设置和要求
- 在此步骤中,您将使用 Codelab 使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群创建高可用性集群。完成以下步骤。如果您已拥有 Codelab 中的集群,请继续执行下一步。
- 注意:在此阶段,请勿执行清理资源步骤。您将在本 Codelab 结束时执行该步骤。
- 返回到此 Codelab
3. 连接到集群并添加示例数据
在此步骤中,您将在控制虚拟机上加载集群设置。然后,您连接到集群并添加包含示例数据的表。
- 如果您未连接到控制虚拟机,请从 Cloud Shell 连接到该虚拟机:
gcloud compute ssh "$CLUSTER-control" --zone="$ZONE" --project="$PROJECT"CLUSTER、ZONE和PROJECT变量来自 使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群 Codelab 中的为部署准备虚拟机这一步。如果 Cloud Shell 没有这些变量,请重新设置。 - 在控制虚拟机上,切换到您的主目录并加载环境变量。然后,将集群名称和 SSH 用户添加到环境文件中:
验证输出是否显示cd ~ source /tmp/cluster.conf cat >> ~/.codelab.env <<EOF export CLUSTER="$cluster" export SSH_USER="$service_account" EOF source ~/.codelab.env env | grep -E '^(CLUSTER|SSH_USER|VIRTUAL_IP)='CLUSTER、SSH_USER和VIRTUAL_IP。注意:如果您稍后再次连接到控制虚拟机,请先运行cd ~和source ~/.codelab.env,然后再继续。 - 将连接设置添加到环境文件中。当
read命令要求输入密码时,输入postgres用户的密码。您可以在使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群 Codelab 的引导 AlloyDB Omni 集群步骤中设置此密码cat >> ~/.codelab.env <<'EOF' export PATH="/usr/pgsql-18/bin:$PATH" export PGHOST="$VIRTUAL_IP" export PGUSER="postgres" EOF read -rsp "Password of the postgres user: " PGPASSWORD; echo printf 'export PGPASSWORD=%q\n' "$PGPASSWORD" >> ~/.codelab.env source ~/.codelab.env env | grep -E '^(PGHOST|PGUSER|PGPASSWORD)=' - 创建表并添加 1,000 行:
psql <<'SQL' CREATE TABLE public.orders ( id integer PRIMARY KEY, item text NOT NULL, created_at timestamptz NOT NULL DEFAULT pg_catalog.now() ); INSERT INTO public.orders (id, item) SELECT g, 'item-' || g FROM pg_catalog.generate_series(1, 1000) AS g; SQL - 计算行数:
验证输出是否返回psql -c "SELECT pg_catalog.count(*) FROM public.orders;"1000。
4. 安装并运行与 S3 兼容的对象存储区
在此步骤中,您将编写一个 Ansible playbook,用于在控制虚拟机上安装 MinIO 服务器。该 playbook 还会创建用于备份的存储桶和用户,并确保数据库虚拟机可以连接到 MinIO。在此步骤中,请在控制虚拟机上运行所有命令。
- 在控制虚拟机上,将存储设置添加到环境文件中。
openssl命令可生成随机密码。 请确保输出显示控制虚拟机的名称和内部 IP 地址,例如MD=http://metadata.google.internal/computeMetadata/v1/instance S3_IP=$(curl -s -H "Metadata-Flavor: Google" "$MD/network-interfaces/0/ip") cat >> ~/.codelab.env <<EOF export S3_IP="$S3_IP" export S3_HOST="$CLUSTER-control" export S3_BUCKET="alloydb-backups" export S3_REGION="us-east-1" export MINIO_ROOT_USER="s3admin" export MINIO_ROOT_PASSWORD="$(openssl rand -hex 16)" export S3_ACCESS_KEY_ID="alloydb-backup" export S3_SECRET_ACCESS_KEY="$(openssl rand -hex 16)" export MINIO_URL="https://github.com/minio/minio/releases/download/RELEASE.2024-05-10T01-41-38Z/minio.linux-amd64.RELEASE.2024-05-10T01-41-38Z" export MC_URL="https://github.com/minio/mc/releases/download/RELEASE.2024-05-09T17-04-24Z/mc.linux-amd64.RELEASE.2024-05-09T17-04-24Z" EOF source ~/.codelab.env echo "$S3_HOST $S3_IP"my-cluster-control 10.1.0.2。数据库虚拟机使用此主机名和 IP 地址连接到 MinIO。虚拟机的元数据服务器会提供 IP 地址。如需了解详情,请参阅虚拟机元数据文档。MINIO_URL和MC_URL值分别是 MinIO 服务器和 MinIO 客户端 (mc) 的下载网址。 - 在控制虚拟机上安装所需的
community.cryptoAnsible 集合:ansible-galaxy collection install community.crypto - 创建
s3_server.yamlplaybook: 借助cat > s3_server.yaml <<EOF - name: Install and run MinIO on the control VM hosts: localhost connection: local become: true gather_facts: false tasks: - name: Create MinIO directories ansible.builtin.file: path: "{{ item }}" state: directory mode: "0755" loop: - /var/lib/minio/data - /var/lib/minio/certs - name: Download MinIO server and client ansible.builtin.get_url: url: "{{ item.url }}" dest: "{{ item.dest }}" mode: "0755" loop: - url: "$MINIO_URL" dest: /usr/local/bin/minio - url: "$MC_URL" dest: /usr/local/bin/mc - name: Generate MinIO TLS private key community.crypto.openssl_privatekey: path: /var/lib/minio/certs/private.key size: 2048 mode: "0600" - name: Generate MinIO certificate signing request community.crypto.openssl_csr_pipe: privatekey_path: /var/lib/minio/certs/private.key common_name: "$S3_HOST" subject_alt_name: - "DNS:$S3_HOST" - "DNS:$S3_BUCKET.$S3_HOST" - "DNS:localhost" - "IP:127.0.0.1" - "IP:$S3_IP" register: minio_csr - name: Generate self-signed MinIO TLS certificate community.crypto.x509_certificate: path: /var/lib/minio/certs/public.crt csr_content: "{{ minio_csr.csr }}" privatekey_path: /var/lib/minio/certs/private.key provider: selfsigned selfsigned_not_after: "+365d" mode: "0644" - name: Create MinIO systemd service unit ansible.builtin.copy: dest: /etc/systemd/system/minio.service mode: "0600" content: | [Unit] Description=MinIO Object Storage After=network.target [Service] Environment="MINIO_ROOT_USER=$MINIO_ROOT_USER" Environment="MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD" Environment="MINIO_DOMAIN=$S3_HOST" ExecStart=/usr/local/bin/minio server /var/lib/minio/data --certs-dir /var/lib/minio/certs Restart=on-failure [Install] WantedBy=multi-user.target - name: Start and enable MinIO service ansible.builtin.systemd_service: name: minio daemon_reload: true state: started enabled: true - name: Add storage host names to /etc/hosts on the control VM ansible.builtin.lineinfile: path: /etc/hosts line: "$S3_IP $S3_HOST $S3_BUCKET.$S3_HOST" state: present - name: Wait for MinIO health endpoint ansible.builtin.uri: url: "https://$S3_HOST:9000/minio/health/live" ca_path: /var/lib/minio/certs/public.crt status_code: 200 register: minio_health until: minio_health.status == 200 retries: 6 delay: 5 - name: Create backup bucket and user in MinIO ansible.builtin.command: argv: "{{ item }}" loop: - - /usr/local/bin/mc - --insecure - alias - set - s3repo - "https://$S3_HOST:9000" - "$MINIO_ROOT_USER" - "$MINIO_ROOT_PASSWORD" - - /usr/local/bin/mc - --insecure - mb - --ignore-existing - "s3repo/$S3_BUCKET" - - /usr/local/bin/mc - --insecure - admin - user - add - s3repo - "$S3_ACCESS_KEY_ID" - "$S3_SECRET_ACCESS_KEY" - - /usr/local/bin/mc - --insecure - admin - policy - attach - s3repo - readwrite - --user - "$S3_ACCESS_KEY_ID" become: false no_log: true - name: Configure database VMs for MinIO hosts: primary_instance_nodes become: true gather_facts: false vars: ansible_user: $SSH_USER ansible_ssh_private_key_file: $HOME/ssh-key-cluster-sa tasks: - name: Set SELinux to permissive mode ansible.builtin.command: cmd: setenforce 0 - name: Add storage host names to /etc/hosts ansible.builtin.lineinfile: path: /etc/hosts line: "$S3_IP $S3_HOST $S3_BUCKET.$S3_HOST" state: present - name: Verify storage host resolution ansible.builtin.getent: database: hosts key: "$S3_BUCKET.$S3_HOST" - name: Verify connection to MinIO ansible.builtin.uri: url: "https://$S3_HOST:9000/minio/health/live" validate_certs: false status_code: 200 EOFMINIO_DOMAIN变量,MinIO 可以接受虚拟主机样式的请求。RPM 编排器使用alloydb-backup用户写入备份。readwrite政策是 MinIO 内置政策。 - 运行 playbook:
验证ansible-playbook s3_server.yaml -i deployment_spec.yamlPLAY RECAP输出是否显示localhost和每个数据库虚拟机的failed=0。 - 验证存储桶是否存在:
验证输出是否显示mc --insecure ls s3repoalloydb-backups/存储桶。
5. 创建备份方案
备份方案包含集群备份的存储位置、时间表和保留期限。在此步骤中,您将为 MinIO 存储桶创建备份方案。
- 在控制虚拟机上,创建备份 playbook 和状态 playbook。您使用
Secret、BackupPlan和Backup资源的备份剧本。您可以使用状态 playbook 来显示资源的状态。cat > backup.yaml <<EOF - name: Manage AlloyDB Omni backups hosts: localhost roles: - role: google.alloydbomni_orchestrator.backup EOF cat > status.yaml <<EOF - name: Get resource status hosts: localhost roles: - role: google.alloydbomni_orchestrator.status EOF - 为 S3 凭据创建
Secret资源。然后应用资源:S3_KEY_ID_B64=$(printf '%s' "$S3_ACCESS_KEY_ID" | base64 -w 0) S3_KEY_B64=$(printf '%s' "$S3_SECRET_ACCESS_KEY" | base64 -w 0) cat > s3_secret.yaml <<EOF Secret: metadata: name: s3-backup-secret spec: type: Opaque data: access-key-id: "$S3_KEY_ID_B64" access-key: "$S3_KEY_B64" EOF ansible-playbook backup.yaml -i deployment_spec.yaml \ -e resource_spec=s3_secret.yamldata部分中的键名称必须是access-key-id和access-key。您必须先应用Secret资源,然后才能应用BackupPlan资源。 - 创建
BackupPlan资源,然后应用该资源:cat > backup_plan.yaml <<EOF BackupPlan: metadata: name: s3-backup-plan spec: dbclusterRef: $CLUSTER backupLocation: type: S3 s3Options: bucket: $S3_BUCKET key: /backups endpoint: https://$S3_HOST:9000 region: $S3_REGION secretRef: name: s3-backup-secret caBundle: - | $(sed 's/^/ /' /var/lib/minio/certs/public.crt) EOF ansible-playbook backup.yaml -i deployment_spec.yaml \ -e resource_spec=backup_plan.yamlkey值是相应存储桶中备份的路径。此路径必须以斜杠 (/) 开头。caBundle字段包含用于验证 S3 存储服务器证书的 PEM 编码的 CA 证书。 - 验证备份方案是否已准备就绪:
验证ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=BackupPlan -e resource_name=s3-backup-planphase字段是否显示Ready。如果阶段不是Ready,请等待一分钟,然后再次运行该命令。 - 创建备份方案后,RPM Orchestrator 会自动启动初始完整备份。在创建按需备份之前,请验证此初始备份是否已完成:
验证初始备份阶段是否成功。ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=Backup
6. 创建按需备份
在此步骤中,您将创建集群的按需备份。
- 在控制虚拟机上,创建
Backup资源。然后应用资源:cat > create_backup.yaml <<EOF Backup: metadata: name: s3-backup-1 spec: backupPlanRef: s3-backup-plan dbclusterRef: $CLUSTER EOF ansible-playbook backup.yaml -i deployment_spec.yaml \ -e resource_spec=create_backup.yamlbackupPlanRef字段是备份方案的名称。dbclusterRef字段是集群的名称。 - 验证备份是否已完成:
验证ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=Backup -e resource_name=s3-backup-1phase字段是否显示Succeeded。如果阶段不是Succeeded,请等待一分钟,然后再次运行该命令。 - 验证备份文件是否位于相应存储桶中:
验证输出是否显示包含mc --insecure tree "s3repo/$S3_BUCKET"db文件夹的backup文件夹。
7. 从备份恢复集群
RPM 编排程序会就地恢复集群。在此步骤中,您将删除一个表,然后从备份中恢复该表。然后,您可以选择将集群恢复到某个时间点。
注意:恢复操作会覆盖数据库集群中的所有数据。
使用备份进行恢复
- 在控制虚拟机上,删除
orders表。然后计算行数: 验证第二个命令是否因错误而失败。出错提示显示关系不存在。例如,“ERROR: table "orders" does not exist”(错误:表“orders”不存在)psql -c "DROP TABLE public.orders;" psql -c "SELECT pg_catalog.count(*) FROM public.orders;" - 创建恢复 playbook:
cat > restore.yaml <<EOF - name: Restore AlloyDB Omni cluster hosts: localhost roles: - role: google.alloydbomni_orchestrator.restore EOF - 为
s3-backup-1备份创建Restore资源。然后应用资源:cat > restore_backup.yaml <<EOF Restore: metadata: name: s3-restore-1 spec: sourceDBCluster: $CLUSTER backup: s3-backup-1 EOF ansible-playbook restore.yaml -i deployment_spec.yaml \ -e resource_spec=restore_backup.yaml - 验证恢复操作是否已完成:
验证ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=Restore -e resource_name=s3-restore-1phase字段是否显示ProvisionSucceeded。如果阶段不是ProvisionSucceeded,请等待一分钟,然后再次运行该命令。 - 统计已恢复表中的行数:
验证输出是否显示psql -c "SELECT pg_catalog.count(*) FROM public.orders;"1000。
(可选)恢复到某个时间点
时间点恢复使用备份和归档的预写式日志 (WAL)。您可以将集群恢复到备份方案的恢复时段内的任何时间点。
目标时间规则
您必须遵循以下所有规则来选择目标时间的值和格式:
- 使用 RFC 3339 格式(采用世界协调时间 [UTC]):
YYYY-MM-DDThh:mm:ssZ。例如,2026-06-19T14:30:00Z是 2026 年 6 月 19 日 14:30:00 UTC。字母T用于分隔日期和时间。字母Z表示世界协调时间 (UTC)。 - 使用过去的时间。
- 使用
BackupPlanCRD 的recoveryWindow字段的begin值之后的时间。
如需以 RFC 3339 格式显示当前时间,请运行以下命令:
date -u +%Y-%m-%dT%H:%M:%SZ
如需将本地时间更改为这种格式,请将本地时间的 UTC 偏移量添加到 date 命令中。例如,以下命令会更改 UTC+02:00 处 19 June 2026 上的 16:30:00:
date -u -d "2026-06-19 16:30:00 +0200" +%Y-%m-%dT%H:%M:%SZ
该命令返回 2026-06-19T14:30:00Z。
恢复到录制时间
在此过程中,您将向表格添加行并记录时间。您记录时间时,会为数据库完全完好无损(正好有 1,500 行)的确切时刻添加书签。然后,您删除行并将集群恢复到记录的时间。
- 向表中添加 500 行:
psql <<'SQL' INSERT INTO public.orders (id, item) SELECT g, 'item-' || g FROM pg_catalog.generate_series(1001, 1500) AS g; SQL - 记录时间。此时,表格有 1,500 行。
验证输出是否显示sleep 5 PITR_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) echo "export PITR_TIME=$PITR_TIME" >> ~/.codelab.env echo "$PITR_TIME" sleep 5YYYY-MM-DDThh:mm:ssZ格式的时间,例如2026-10-05T11:16:02Z。 - 从表中删除行,以模拟意外删除数据的情况。然后启动新的 WAL 文件:
验证psql -c "DELETE FROM public.orders WHERE id > 1000;" psql -c "SELECT pg_catalog.count(*) FROM public.orders;" psql -c "SELECT pg_catalog.pg_switch_wal();"count查询是否显示1000。pg_switch_wal函数可使服务器开始使用新的 WAL 文件。然后,服务器可以归档包含您所做更改的 WAL 文件。 - 验证备份方案的恢复时段是否包含记录的时间:
验证test -n "$PITR_TIME" || { echo "PITR_TIME is not set. Run step 2 again."; exit 1; } echo "$PITR_TIME" ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=BackupPlan -e resource_name=s3-backup-planPITR_TIME值是否晚于recoveryWindow字段的begin值。 - 为记录的时间创建
Restore资源。然后应用资源:cat > restore_pitr.yaml <<EOF Restore: metadata: name: s3-restore-pitr spec: sourceDBCluster: $CLUSTER pointInTime: "$PITR_TIME" EOF ansible-playbook restore.yaml -i deployment_spec.yaml \ -e resource_spec=restore_pitr.yaml - 验证恢复操作是否已完成:
验证ansible-playbook status.yaml -i deployment_spec.yaml \ -e resource_type=Restore -e resource_name=s3-restore-pitrphase字段是否显示ProvisionSucceeded。如果阶段不是ProvisionSucceeded,请等待一分钟,然后再次运行该命令。 - 计算行数:
验证输出是否显示psql -c "SELECT pg_catalog.count(*) FROM public.orders;"1500。
8. 清理
为避免您的 Google Cloud 账号产生费用,请删除本 Codelab 和 使用 RPM Orchestrator 在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群 Codelab 的资源。首先,删除备份资源,然后删除集群和计算环境。
- 在控制虚拟机上,创建删除 playbook:
cat > delete.yaml <<EOF - name: Delete AlloyDB Omni resources hosts: localhost roles: - role: google.alloydbomni_orchestrator.delete EOF - 列出备份:
验证输出是否显示ansible-playbook status.yaml -i deployment_spec.yaml -e resource_type=Backups3-backup-1备份。 - 删除
s3-backup-1备份:ansible-playbook delete.yaml -i deployment_spec.yaml \ -e resource_type=Backup -e resource_name=s3-backup-1 - 删除备份方案:
ansible-playbook delete.yaml -i deployment_spec.yaml \ -e resource_type=BackupPlan -e resource_name=s3-backup-plan - 按照 使用 RPM 编排器在 GCE 虚拟机上部署 AlloyDB Omni 高可用性集群 Codelab 中的清理资源步骤,删除集群和计算环境。MinIO 服务器及其中的备份位于控制虚拟机上。删除计算环境时,服务器和备份都会被删除。
9. 恭喜
恭喜!您使用 RPM 编排器将高可用性 (HA) 集群备份到与 AlloyDB Omni 兼容的 S3 对象存储区。然后,您从备份中恢复了集群。
所学内容
- 如何安装和运行 MinIO 作为与 S3 兼容的对象存储区。
- 如何创建备份方案和按需备份。
- 如何使用备份将集群恢复到某个时间点。
如需了解详情,请参阅以下页面: