使用永久性磁盘快照部署可冷恢复的 Web 服务器

Last reviewed 2021-08-04 UTC

本文档介绍了如何使用托管式实例组永久性磁盘快照为 Web 服务器部署冷故障切换拓扑。本文档适用于架构师以及在运营和管理团队工作的人员。

您需要创建托管式实例组,以运行具有存储数据的永久性磁盘的单个虚拟机。永久性磁盘的预定快照可最大限度减少故障切换场景中的数据丢失。外部应用负载均衡器会将用户定向到在托管式实例组中运行的虚拟机,如下图所示:

外部应用负载均衡器将用户定向到在托管式实例组中运行的单个虚拟机,而资源政策为挂接到该虚拟机的永久性磁盘定期创建快照。

如果存在实例故障,则代管式实例组会尝试在同一可用区重新创建虚拟机。如果故障发生在可用区级别,则 Cloud Monitoring 或类似服务可通知您存在问题,并且您将在其他可用区或区域中另外手动创建一个代管式实例组。在任一故障切换场景中,平台都会使用最新的永久性磁盘快照来创建替换磁盘,并将其挂接到实例组中的新虚拟机。

在本文档中,您可以使用虚拟机或负载均衡器的外部 IP 地址查看网络服务器上的基本页面。如果您没有注册域名且没有任何 DNS 更改,则可通过此方法测试冷故障切换模式。在生产环境中,请创建并配置 Cloud DNS 区域和记录,以解析为分配给负载均衡器的外部 IP 地址。

此模式可平衡为维护一定程度的数据保护而运行多个虚拟机或区域永久性磁盘的费用。当您运行一个虚拟机和永久性磁盘时,费用会低一些,但永久性磁盘快照仅在设定的时间间隔进行截取,因此可能导致数据丢失。如需减少可能的数据丢失,请考虑改为部署使用区域级永久性磁盘的可冷恢复的 Web 服务器

下表简要介绍了使用区域永久性磁盘或永久性磁盘快照的可恢复冷方法在数据保护方案之间的一些差异。如需了解详情,请参阅使用永久性磁盘的高可用性选项

区域永久性磁盘 永久性磁盘快照
数据丢失 - 恢复点目标 (RPO) 单个故障(例如在可用区或网络中断中连续中断)为零。 自上次截取快照以来的所有数据(通常为一小时或更长时间)。

数据丢失的可能性取决于您控制快照截取频率的快照时间表。
目标恢复时间 (RTO) 新虚拟机的部署时间,以及重新挂接区域永久性磁盘的秒数。 新虚拟机的部署时间,以及根据最新快照创建新永久性磁盘的时间。

磁盘的创建时间取决于快照的大小,并且可能需要几分钟或几小时。
费用 由于区域永久性磁盘不断复制到另一个可用区,因此存储费用会翻倍。 您只需为使用的快照空间付费。
如需了解详情,请参阅磁盘和映像价格

目标

  • 创建代管式实例组以运行具有永久性磁盘的虚拟机。
  • 配置快照时间表以截取永久性磁盘的常规快照。
  • 创建实例模板和启动脚本。
  • 创建并配置外部应用负载均衡器。
  • 使用替换代管式实例组测试网络服务器冷故障切换。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 启用 Compute Engine API。

    启用 API

  5. 安装 Google Cloud CLI。
  6. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  7. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  8. 确保您的 Google Cloud 项目已启用结算功能

  9. 启用 Compute Engine API。

    启用 API

  10. 安装 Google Cloud CLI。
  11. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  12. 您可以在 Google Cloud 控制台中运行 Google Cloud CLI,而无需安装 Google Cloud CLI。如需在 Google Cloud 控制台中运行 gcloud CLI,请使用 Cloud Shell。

准备环境

在本部分中,您将为资源名称和位置定义一些变量。在您部署资源时,Google Cloud CLI 命令会使用这些变量。

在本文档中,除非另有说明,否则您在 Cloud Shell 或本地开发环境中输入所有命令。

  1. 请将 PROJECT_ID 替换为您自己的项目 ID。如果需要,请为资源提供自己的名称后缀,例如 app

    指定区域(例如 us-central1),指定区域内的两个可用区(例如 us-central1-aus-central1-f)。这些可用区定义初始永久性磁盘和代管式实例组组的部署位置,以及您可根据需要手动故障切换到的位置。

    PROJECT_ID=PROJECT_ID
    NAME_SUFFIX=app
    REGION=us-central1
    ZONE1=us-central1-a
    ZONE2=us-central1-f
    

创建 VPC 和子网

如需为虚拟机提供网络访问权限,请创建 Virtual Private Cloud (VPC) 和子网。由于代管式实例组可在单个区域内的多个可用区工作,因此只创建一个子网。如需详细了解自定义子网模式的优势来管理环境中使用的 IP 地址范围,请参阅使用自定义模式 VPC 网络

  1. 使用自定义子网模式创建 VPC:

    gcloud compute networks create network-$NAME_SUFFIX \
        --subnet-mode=custom
    

    如果您看到 Cloud Shell 提示符,请授权第一个请求以进行 API 调用。

  2. 在新 VPC 中创建一个子网。定义您自己的地址范围使其适合您的网络范围,例如 10.1.0.0/20

    gcloud compute networks subnets create subnet-$NAME_SUFFIX-$REGION \
        --network=network-$NAME_SUFFIX \
        --range=10.1.0.0/20 \
        --region=$REGION
    

创建防火墙规则

  1. 创建防火墙规则,以允许针对负载均衡器和代管式实例组的网络流量和健康检查:

    gcloud compute firewall-rules create allow-http-$NAME_SUFFIX \
        --network=network-$NAME_SUFFIX \
        --direction=INGRESS \
        --priority=1000 \
        --action=ALLOW \
        --rules=tcp:80 \
        --source-ranges=0.0.0.0/0 \
        --target-tags=http-server
    
    gcloud compute firewall-rules create allow-health-check-$NAME_SUFFIX \
        --network=network-$NAME_SUFFIX \
        --action=allow \
        --direction=ingress \
        --source-ranges=130.211.0.0/22,35.191.0.0/16 \
        --target-tags=allow-health-check \
        --rules=tcp:80
    

    HTTP 规则允许发送到任何应用 http-server 标记的虚拟机的流量,并允许来自任何使用 0.0.0.0/0 范围的来源的流量。对于健康检查规则,Google Cloud 的默认范围需设置为允许平台正确检查资源的运行状况。

  2. 如需允许基本虚拟机映像对 SSH 流量进行初始配置,请使用 --source-range 参数将防火墙规则范围限定到您的环境。您可能需要与网络团队合作,以确定您的组织使用的来源范围。

    IP_ADDRESS_SCOPE 替换为您自己的 IP 地址范围:

    gcloud compute firewall-rules create allow-ssh-$NAME_SUFFIX \
        --network=network-$NAME_SUFFIX \
        --direction=INGRESS \
        --priority=1000 \
        --action=ALLOW \
        --rules=tcp:22 \
        --source-ranges=IP_ADDRESS_SCOPE
    
  3. 创建防火墙规则后,请验证是否添加了这三条规则:

    gcloud compute firewall-rules list \
        --project=$PROJECT_ID \
        --filter="NETWORK=network-$NAME_SUFFIX"
    

    以下示例输出显示了已正确创建三个规则:

    NAME                    NETWORK      DIRECTION  PRIORITY  ALLOW
    allow-health-check-app  network-app  INGRESS    1000      tcp:80
    allow-http-app          network-app  INGRESS    1000      tcp:80
    allow-ssh-app           network-app  INGRESS    1000      tcp:22
    

创建和配置基本虚拟机映像

如需创建无需额外配置即可部署的相同虚拟机,请使用自定义虚拟机映像。此映像会捕获操作系统和 Apache 配置,并用于在后续步骤中创建代管式实例组中的每个虚拟机。

使用永久性磁盘来存储应用数据。在本文档中,您将使用基本 Apache 网站来提供应用。在本文档的后面部分,您将创建挂接到此永久性磁盘的快照时间表,以创建自动磁盘快照。

在虚拟机上,在永久性磁盘上创建基本的 index.html 文件并将其装载到 /var/www/example.com。位于 /etc/apache2/sites-available/example.com.conf 的 Apache 配置文件从已装载的永久性磁盘位置提供网页内容。

下图展示了由永久性磁盘上存储的 Apache 提供的基本 HTML 页面。

该虚拟机在永久性磁盘上存储基本的 HTML 页面,以及从装载的磁盘位置加载的 Apache 配置文件

您可以按照以下步骤构建此环境。

  1. 创建一个 10 GiB 的 SSD。了解您的存储空间需求以及预配空间的相关费用,而不是消耗空间的相关费用。如需了解详情,请参阅永久性磁盘价格

    gcloud compute disks create disk-$NAME_SUFFIX \
        --zone $ZONE1 \
        --size=10 \
        --type=pd-ssd
    
  2. 创建挂接了永久性磁盘的基本虚拟机:

    gcloud compute instances create vm-base-$NAME_SUFFIX \
        --zone=$ZONE1 \
        --machine-type=n1-standard-1 \
        --subnet=subnet-$NAME_SUFFIX-$REGION \
        --tags=http-server \
        --image=debian-10-buster-v20210721 \
        --image-project=debian-cloud \
        --boot-disk-size=10GB \
        --boot-disk-type=pd-balanced \
        --boot-disk-device-name=vm-base-$NAME_SUFFIX \
        --disk=mode=rw,name=disk-$NAME_SUFFIX,device-name=disk-$NAME_SUFFIX
    

    您可以使用本文档开头定义的参数来命名虚拟机并连接到正确的子网。名称还会从启动磁盘的数据参数和数据磁盘的参数进行分配。

  3. 如需安装和配置简单网站,请首先使用 SSH 连接到基本虚拟机:

    gcloud compute ssh vm-base-$NAME_SUFFIX --zone=$ZONE1
    
  4. 在与虚拟机的 SSH 会话中,创建一个脚本以在您选择的编辑器中配置虚拟机。以下示例使用 Nano 作为编辑器:

    nano configure-vm.sh
    

    将以下配置脚本粘贴到此文件中。更新 NAME_SUFFIX 变量以匹配本文档开头设置的值,例如 app

    #!/bin/bash
    
    NAME_SUFFIX=app
    
    # Create directory for the basic website files
    sudo mkdir -p /var/www/example.com
    sudo chmod a+w /var/www/example.com
    sudo chown -R www-data: /var/www/example.com
    
    # Find the disk name, then format and mount it
    DISK_NAME="google-disk-$NAME_SUFFIX"
    DISK_PATH="$(find /dev/disk/by-id -name "${DISK_NAME}" | xargs -I '{}' readlink -f '{}')"
    
    sudo mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $DISK_PATH
    sudo mount -o discard,defaults $DISK_PATH /var/www/example.com
    
    # Install Apache, additional utilities, and cloud-init
    sudo apt-get update && sudo apt-get -y install apache2 moreutils cloud-init
    
    # Write out a basic HTML file to the mounted persistent disk
    sudo tee -a /var/www/example.com/index.html >/dev/null <<'EOF'
    <!doctype html>
    <html lang=en>
    <head>
    <meta charset=utf-8>
        <title>HA / DR example</title>
    </head>
    <body>
        <p>Welcome to a test web server with persistent disk snapshots!</p>
    </body>
    </html>
    EOF
    
    # Write out an Apache configuration file
    sudo tee -a /etc/apache2/sites-available/example.com.conf >/dev/null <<'EOF'
    <VirtualHost *:80>
            ServerName www.example.com
    
            ServerAdmin webmaster@localhost
            DocumentRoot /var/www/example.com
    
            ErrorLog ${APACHE_LOG_DIR}/error.log
            CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
    EOF
    
    # Enable the Apache configuration file and reload service
    sudo a2dissite 000-default
    sudo a2ensite example.com.conf
    sudo systemctl reload apache2
    
  5. 写出该文件并退出编辑器。例如,在 Nano 中,您可以使用 Ctrl-O 写出该文件,然后使用 Ctrl-X 退出。

  6. 将配置脚本设为可执行,然后运行它:

    chmod +x configure-vm.sh
    ./configure-vm.sh
    
  7. 如果存在实例故障,并且代管式实例组需要根据此基本虚拟机创建替换实例,则应用数据必须可用。以下步骤应在每个新的虚拟机上自动运行:

    • 从元数据服务器获取一些信息。
    • 获取永久性磁盘的最新快照。
    • 通过此最新快照创建磁盘。
    • 将新磁盘挂接到虚拟机。
    • 在虚拟机中装载磁盘。

    创建一个名为 app-startup.sh 的启动脚本,用于执行虚拟机所需的这些步骤。此启动脚本会应用于以下步骤中的实例模板。

    sudo mkdir /opt/cloud-init-scripts
    
    sudo tee -a /opt/cloud-init-scripts/app-startup.sh >/dev/null <<'EOF'
    #!/bin/bash
    
    # Install jq and get an access token for API requests
    apt-get install -y jq
    OAUTH_TOKEN=$(curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
        -H "Metadata-Flavor: Google" --silent | jq -r '.access_token')
    
    # Make a request against the metadata server to determine the project name,
    # instance name, and what zone it's running in
    ZONE_INFO=$(curl http://metadata.google.internal/computeMetadata/v1/instance/zone \
        -H "Metadata-Flavor: Google" --silent)
    PROJECT_NAME=$(curl http://metadata.google.internal/computeMetadata/v1/instance/zone \
        -H "Metadata-Flavor: Google" --silent | awk -v FS="/" '{print $2}')
    ZONE_NAME=$(curl http://metadata.google.internal/computeMetadata/v1/instance/zone \
        -H "Metadata-Flavor: Google" --silent | sed 's:.*/::')
    INSTANCE_NAME=$(curl http://metadata.google.internal/computeMetadata/v1/instance/name \
        -H "Metadata-Flavor: Google" --silent)
    
    # Get the latest snapshot of the app disk
    LATEST_SNAPSHOT=$(curl -X GET -H "Authorization: Bearer $OAUTH_TOKEN" \
        https://compute.googleapis.com/compute/v1/projects/$PROJECT_NAME/global/snapshots \
        --silent | jq -s '.[].items[] | select(.name | contains("disk-$NAME")) | .name' \
        | sort -r | head -n 1 | tr -d '"')
    
    # Create a persistent disk using the latest persistent disk snapshot
    curl -X POST -H "Authorization: Bearer $OAUTH_TOKEN" -H "Content-Type: application/json; charset=utf-8" \
        https://compute.googleapis.com/compute/v1/$ZONE_INFO/disks \
        --data '{"name":"'$LATEST_SNAPSHOT'-restored","sizeGb":"10","type":"zones/'$ZONE_NAME'/diskTypes/pd-ssd","sourceSnapshot":"https://www.googleapis.com/compute/v1/projects/'$PROJECT_NAME'/global/snapshots/'$LATEST_SNAPSHOT'"}'
    
    # Wait for the persistent disk to be created from the disk snapshot
    DISK_STATUS=$(curl -X GET -H "Authorization: Bearer $OAUTH_TOKEN" \
        https://compute.googleapis.com/compute/v1/projects/$PROJECT_NAME/zones/$ZONE_NAME/disks/$LATEST_SNAPSHOT-restored \
        --silent | jq -r .status)
    
    while [ $DISK_STATUS != "READY" ]
    do
        sleep 2
        DISK_STATUS=$(curl -X GET -H "Authorization: Bearer $OAUTH_TOKEN" \
            https://compute.googleapis.com/compute/v1/projects/$PROJECT_NAME/zones/$ZONE_NAME/disks/$LATEST_SNAPSHOT-restored \
            --silent | jq -r .status)
    done
    
    # Attach the new persistent disk created from the snapshot to the VM
    curl -X POST -H "Authorization: Bearer $OAUTH_TOKEN" -H "Content-Type: application/json; charset=utf-8" \
        https://compute.googleapis.com/compute/v1/$ZONE_INFO/instances/$INSTANCE_NAME/attachDisk \
        --data '{ "source": "/compute/v1/'$ZONE_INFO'/disks/'$LATEST_SNAPSHOT'-restored"}'
    
    # Wait for the persistent disk to be attached before mounting
    ATTACH_STATUS=$(curl -X GET -H "Authorization: Bearer $OAUTH_TOKEN" \
        https://compute.googleapis.com/compute/v1/projects/$PROJECT_NAME/zones/$ZONE_NAME/instances/$INSTANCE_NAME \
        --silent | jq '.disks[] | select(.source | contains("disk-"))')
    
    while [ -z "$ATTACH_STATUS" ]
    do
        sleep 2
        ATTACH_STATUS=$(curl -X GET -H "Authorization: Bearer $OAUTH_TOKEN" GET \
            https://compute.googleapis.com/compute/v1/projects/$PROJECT_NAME/zones/$ZONE_NAME/instances/$INSTANCE_NAME \
            --silent | jq '.disks[] | select(.source | contains("disk-"))')
    done
    
    # With the disk attached, mount the disk and restart Apache
    echo UUID=`blkid -s UUID -o value /dev/sdb` /var/www/example.com ext4 discard,defaults,nofail 0 2 \
        | tee -a /etc/fstab
    mount -a
    systemctl reload apache2
    
    # Remove jq so it's not left on the VM
    apt-get remove -y jq
    EOF
    
  8. 如需将您在文档开头定义的 NAME_SUFFIX 变量应用于启动脚本(例如 app),请使用 envsubst 命令:

    export NAME=app
    envsubst '$NAME' < "/opt/cloud-init-scripts/app-startup.sh" \
        | sudo sponge "/opt/cloud-init-scripts/app-startup.sh"
    
  9. 退出与虚拟机的 SSH 会话:

    exit
    
  10. 获取虚拟机的 IP 地址,并使用 curl 查看基本网页:

    curl $(gcloud compute instances describe vm-base-$NAME_SUFFIX \
        --zone $ZONE1 \
        --format="value(networkInterfaces.accessConfigs.[0].natIP)")
    

    系统会返回基本网站,如以下示例输出所示:

    <!doctype html>
    
    <html lang=en>
    <head>
    <meta charset=utf-8>
        <title>HA / DR example</title>
    </head>
    <body>
        <p>Welcome to a test web server with persistent disk snapshots!</p>
    </body>
    </html>
    

    此步骤确认 Apache 已正确配置,并且已从挂接的永久性磁盘加载页面。在下面的部分中,您将使用此基本虚拟机创建映像,并使用启动脚本配置实例模板。

创建永久性磁盘快照时间表

如需确保在代管式实例组中创建的虚拟机始终具有永久性磁盘的最新数据,您需要创建快照时间表。此时间表会按定义的时间自动截取永久性磁盘的快照并控制快照的保留时间。下图展示了此快照过程的工作原理:

系统会创建一项资源政策来定义快照时间表,并将其附加到永久性磁盘以定期截取快照。

请考虑您的应用需求和业务目标,以决定您应截取快照的频率;例如,与将数据写入磁盘的活跃应用相比,静态网站的快照频率要低。

如需详细了解如何确定您自己应用的最佳方法及所用恢复方法,请参阅灾难恢复规划指南

  1. 在此情况下,您使用快照时间表来创建常规的永久性磁盘快照。您可以在资源政策中定义此快照时间表。通过资源政策,您可以定义要运行的操作,并将其附加到环境中的资源。

    在这项资源政策中,您可以定义时间表以根据以下设置来创建快照:

    • 每 4 小时截取一个快照,从世界协调时间 (UTC) 22:00 点开始
    • 将快照保留 1 天

    根据环境的需要配置此时间表,例如开始时间和截取快照的频率:

    gcloud compute resource-policies create snapshot-schedule snapshot-schedule-$NAME_SUFFIX \
        --description "Snapshot persistent disk every 4 hours" \
        --max-retention-days 1 \
        --start-time 22:00 \
        --hourly-schedule 4 \
        --region $REGION
    

    如需了解详情,请参阅如何为永久性磁盘使用计划快照

  2. 如需使用快照时间表,请将资源政策附加到永久性磁盘。指定永久性磁盘的名称和在上一步中创建的资源政策:

    gcloud compute disks add-resource-policies disk-$NAME_SUFFIX \
        --resource-policies snapshot-schedule-$NAME_SUFFIX \
        --zone $ZONE1
    
  3. 在创建第一个磁盘快照之前,您无法完成本文档的其余部分并查看实际应用中的代管式实例组。现在手动创建磁盘快照,让资源政策快照时间表根据定义创建其他快照:

    gcloud compute disks snapshot disk-$NAME_SUFFIX \
        --zone=$ZONE1 \
        --snapshot-names=disk-$NAME_SUFFIX-$(date "+%Y%m%d%H%M%S")
    

创建服务账号

在后续步骤中创建的代管式实例组中的每个虚拟机都需要运行启动脚本。此启动脚本通过快照创建永久性磁盘,然后将其挂接到虚拟机。最佳做法:创建新的服务账号,使其仅包含执行这些磁盘操作所需的权限。然后将此服务账号分配给虚拟机。

  1. 创建一个服务账号以便与代管式实例组中的虚拟机搭配使用:

    gcloud iam service-accounts create instance-sa-$NAME_SUFFIX \
        --description="Service account for HA/DR example" \
        --display-name="HA/DR for VM instances"
    
  2. 创建一个自定义角色,并仅为其分配执行磁盘管理任务所需的权限。必须拥有以下权限:

    • compute.snapshots.list
    • compute.snapshots.useReadOnly
    • compute.disks.get
    • compute.disks.create
    • compute.instances.get
    • compute.instances.attachDisk
    • compute.disks.use
    gcloud iam roles create instance_snapshot_management_$NAME_SUFFIX \
        --project=$PROJECT_ID \
        --title="Snapshot management for VM instances" \
        --description="Custom role to allow an instance to create a persistent disk from a snapshot and attach to VM." \
        --permissions=compute.snapshots.list,compute.snapshots.useReadOnly,compute.disks.get,compute.disks.create,compute.instances.get,compute.instances.attachDisk,compute.disks.use \
        --stage=GA
    
  3. 为新的服务账号添加所需的角色绑定:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:instance-sa-$NAME_SUFFIX@$PROJECT_ID.iam.gserviceaccount.com" \
        --role="projects/$PROJECT_ID/roles/instance_snapshot_management_$NAME_SUFFIX"
    
    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member="serviceAccount:instance-sa-$NAME_SUFFIX@$PROJECT_ID.iam.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
    

创建虚拟机映像和实例模板

若要创建无需额外配置即可自动部署的相同虚拟机,请使用自定义虚拟机映像。此映像会捕获操作系统和 Apache 配置。在后续步骤中,在代管式实例组中创建的每个虚拟机都会使用此映像。

  1. 您必须先停止虚拟机,然后才能创建映像:

    gcloud compute instances stop vm-base-$NAME_SUFFIX --zone=$ZONE1
    
  2. 创建上一部分中配置的基本虚拟机的映像:

    gcloud compute images create image-$NAME_SUFFIX \
        --source-disk=vm-base-$NAME_SUFFIX \
        --source-disk-zone=$ZONE1 \
        --storage-location=$REGION
    
  3. 您可以使用 cloud-init 在代管式实例组中的虚拟机首次启动时运行之前的启动脚本。每当虚拟机启动时(例如,虚拟机在更新后重新启动时),应用于虚拟机的常规启动脚本都会运行。

    创建 cloud-init 配置文件来与实例模板配合使用:

    tee -a cloud-init.yaml >/dev/null <<'EOF'
    #cloud-config
    
    runcmd:
     - [ bash, /opt/cloud-init-scripts/app-startup.sh ]
    EOF
    
  4. 创建一个实例模板,用于应用 cloud-init 配置来运行启动脚本,从而通过快照创建磁盘,然后将该磁盘挂接并装载到虚拟机:

    gcloud compute instance-templates create template-$NAME_SUFFIX \
        --machine-type=n1-standard-1 \
        --subnet=projects/$PROJECT_ID/regions/$REGION/subnetworks/subnet-$NAME_SUFFIX-$REGION \
        --tags=http-server \
        --image=image-$NAME_SUFFIX \
        --scopes cloud-platform \
        --service-account="instance-sa-$NAME_SUFFIX@$PROJECT_ID.iam.gserviceaccount.com" \
        --metadata-from-file user-data=cloud-init.yaml
    

创建代管式实例组

代管式实例组会运行虚拟机。代管式实例组在定义的可用区中运行,并监控虚拟机的运行状况。如果发生故障,并且虚拟机停止运行,则代管式实例组会尝试在同一可用区中重新创建另一个虚拟机,并根据最新快照创建一个永久性磁盘。如果故障发生在可用区级别,则您必须手动执行冷故障切换,并在其他可用区中另外创建一个代管式实例组。相同的自定义映像和实例模板会自动以相同的方式配置虚拟机。

  1. 创建一个健康检查以监控代管式实例组中的虚拟机。此健康检查可确保虚拟机在端口 80 上响应。对于您自己的应用,监控相应端口以检查虚拟机运行状况。

    gcloud compute health-checks create http http-basic-check-$NAME_SUFFIX --port 80
    
  2. 创建仅包含一个虚拟机的代管式实例组。这个虚拟机会启动并使用最新快照创建永久性磁盘,然后装载该磁盘并开始处理网络流量。

    gcloud compute instance-groups managed create instance-group-$NAME_SUFFIX-$ZONE1 \
        --base-instance-name=instance-vm-$NAME_SUFFIX \
        --template=template-$NAME_SUFFIX \
        --size=1 \
        --zone=$ZONE1 \
        --health-check=http-basic-check-$NAME_SUFFIX
    

创建和配置负载均衡器

为了让用户能够访问您的网站,您需要允许流量流向代管式实例组中运行的虚拟机。如果代管式实例组中有可用区故障,您还需要将流量自动重定向到新虚拟机。

在下面的部分中,您将创建外部负载均衡器和后端服务,以获取端口 80 上的 HTTP 流量,使用在前面步骤中创建的健康检查,并将外部 IP 地址映射到后端服务。

如需了解详情,请参阅如何设置简单的外部 HTTP 负载均衡器

  1. 为您的应用创建和配置负载均衡器:

    # Configure port rules for HTTP port 80
    gcloud compute instance-groups set-named-ports \
        instance-group-$NAME_SUFFIX-$ZONE1 \
        --named-ports http:80 \
        --zone $ZONE1
    
    # Create a backend service and add the managed instance group to it
    gcloud compute backend-services create \
        web-backend-service-$NAME_SUFFIX \
        --protocol=HTTP \
        --port-name=http \
        --health-checks=http-basic-check-$NAME_SUFFIX \
        --global
    
    gcloud compute backend-services add-backend \
        web-backend-service-$NAME_SUFFIX \
        --instance-group=instance-group-$NAME_SUFFIX-$ZONE1 \
        --instance-group-zone=$ZONE1 \
        --global
    
    # Create a URL map for the backend service
    gcloud compute url-maps create web-map-http-$NAME_SUFFIX \
        --default-service web-backend-service-$NAME_SUFFIX
    
    # Configure forwarding for the HTTP traffic
    gcloud compute target-http-proxies create \
        http-lb-proxy-$NAME_SUFFIX \
        --url-map web-map-http-$NAME_SUFFIX
    
    gcloud compute forwarding-rules create \
        http-content-rule-$NAME_SUFFIX \
        --global \
        --target-http-proxy=http-lb-proxy-$NAME_SUFFIX \
        --ports=80
    
  2. 获取 Web 流量的转发规则的 IP 地址:

    IP_ADDRESS=$(gcloud compute forwarding-rules describe http-content-rule-$NAME_SUFFIX \
        --global \
        --format="value(IPAddress)")
    
  3. 使用 curl 或打开网络浏览器,以使用上一步中的负载均衡器的 IP 地址查看网站:

    curl $IP_ADDRESS
    

    负载均衡器完成部署并将流量正确定向到您的后端需要几分钟时间。如果负载均衡器仍在部署,则会返回 HTTP 404 或 502 错误。如需要,请等待几分钟,再尝试访问网站。

    系统会返回基本网站,如以下示例输出所示:

    <!doctype html>
    
    <html lang=en>
    <head>
    <meta charset=utf-8>
        <title>HA / DR example</title>
    </head>
    <body>
        <p>Welcome to a Compute Engine website with warm failover to Cloud Storage!</p>
    </body>
    </html>
    

模拟可用区故障和恢复

在可用区级别模拟故障之前,请先查看资源部署。所有资源均已创建用于以下环境:

外部应用负载均衡器将用户定向到在托管式实例组中运行的单个虚拟机,而资源政策为挂接到该虚拟机的永久性磁盘定期创建快照。

  • 一个虚拟机在挂接了一个永久性磁盘的代管式实例组中运行,其中该磁盘用于存储基本网站。
  • 系统会定期使用资源政策快照时间表来截取永久性磁盘的快照。
  • 启动脚本应用于实例模板,因此在代管式实例组中创建的任何虚拟机都将根据最后一个磁盘快照创建永久性磁盘并挂接该磁盘。
  • 健康检查会监控代管式实例组内虚拟机的状态。
  • 外部应用负载均衡器会将用户定向到在代管式实例组中运行的虚拟机。
  • 如果虚拟机发生故障,则代管式实例组会尝试在同一可用区重新创建虚拟机。如果故障发生在可用区级别,您必须在另一个无故障的可用区手动创建替换代管实例组。

在生产环境中,您可能会在出现问题时使用 Cloud Monitoring 或其他监控解决方案收到提醒。此提醒会提示您人工了解故障的范围,然后再在另一个有效可用区中手动创建替换托管实例组。另一种方法是使用监控解决方案自动响应代管式实例组发生的服务中断。

当您或监控解决方案确定最合适的操作是故障切换时,请创建一个替换代管式实例组。在本文档中,您将手动创建此替换资源。

  1. 如需模拟可用区级别的故障,请删除负载均衡器后端和代管式实例组:

    gcloud compute backend-services remove-backend \
        web-backend-service-$NAME_SUFFIX \
        --instance-group=instance-group-$NAME_SUFFIX-$ZONE1 \
        --instance-group-zone=$ZONE1 \
        --global
    
    gcloud compute instance-groups managed delete instance-group-$NAME_SUFFIX-$ZONE1 \
          --zone=$ZONE1
    

    出现提示时,确认删除代管式实例组的请求。

    在生产环境中,您的监控系统会生成提醒,立即提示冷故障切换操作。

  2. 使用 curl 或网络浏览器再次访问负载均衡器的 IP 地址:

    curl $IP_ADDRESS --max-time 5
    

    curl 请求失败,因为负载均衡器没有运行状况良好的目标。

  3. 如需模拟冷故障切换,请在其他可用区创建一个代管式实例组:

    gcloud compute instance-groups managed create instance-group-$NAME_SUFFIX-$ZONE2 \
        --template=template-$NAME_SUFFIX \
        --size=1 \
        --zone=$ZONE2 \
        --health-check=http-basic-check-$NAME_SUFFIX
    

    虚拟机映像、实例模板和永久性磁盘会保留应用实例的所有配置。

  4. 更新负载均衡器以添加新的代管式实例组和虚拟机:

    gcloud compute instance-groups set-named-ports \
        instance-group-$NAME_SUFFIX-$ZONE2 \
        --named-ports http:80 \
        --zone $ZONE2
    
    gcloud compute backend-services add-backend \
        web-backend-service-$NAME_SUFFIX \
        --instance-group=instance-group-$NAME_SUFFIX-$ZONE2 \
        --instance-group-zone=$ZONE2 \
        --global
    
  5. 再次使用 curl 或网络浏览器访问负载均衡器的 IP 地址,该负载均衡器会将流量定向到代管式实例组中运行的虚拟机:

    curl $IP_ADDRESS
    

    虚拟机需要几分钟才能从最新的永久性磁盘快照完成数据的部署和恢复。如果虚拟机仍在部署,则返回 HTTP 404 或 502 错误;如果仍然恢复数据,则显示默认 Apache。如需要,请等待几分钟,再尝试访问网站。

    以下示例响应显示了在虚拟机上正常运行的网页:

    <!doctype html>
    <html lang=en>
    <head>
    <meta charset=utf-8>
        <title>HA / DR example</title>
    </head>
    <body>
        <p>Welcome to a test web server with persistent disk snapshots!</p>
    </body>
    </html>
    
  6. 检查代管式实例组的运行状况:

    gcloud compute instance-groups managed list-instances instance-group-$NAME_SUFFIX-$ZONE2 \
        --zone $ZONE2
    

    以下示例输出显示虚拟机的状态为 RUNNINGHEALTHY

    NAME             ZONE           STATUS   HEALTH_STATE  ACTION
    instance-vm-app  us-central1-f  RUNNING  HEALTHY       NONE
    
  7. 如需验证是否已使用快照创建挂接的永久性磁盘,请查看来源。指定上一个 list-instances 命令中显示的实例 NAME

    gcloud compute instances describe NAME \
        --zone=$ZONE2 \
        --format="value(disks.[1].source)"
    

    以下示例输出显示名为 disk-app-us-central1-a-20210630165529-umopkt17-restored 的永久性磁盘。启动脚本将 -restored 后缀添加到最新磁盘快照的名称后面:

    https://www.googleapis.com/compute/v1/projects/project/zones/us-central1-f/disks/disk-app-us-central1-a-20210630165529-umopkt17-restored
    

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

要删除本文档中创建的各资源,请完成以下步骤。

  1. 删除负载平衡器配置:

    gcloud compute forwarding-rules delete \
        http-content-rule-$NAME_SUFFIX --global --quiet
    
    gcloud compute target-http-proxies delete \
        http-lb-proxy-$NAME_SUFFIX --quiet
    
    gcloud compute url-maps delete web-map-http-$NAME_SUFFIX --quiet
    
    gcloud compute backend-services delete \
        web-backend-service-$NAME_SUFFIX --global --quiet
    
  2. 删除代管式实例组和健康检查:

    gcloud compute instance-groups managed delete instance-group-$NAME_SUFFIX-$ZONE2 \
        --zone=$ZONE2 --quiet
    
    gcloud compute health-checks delete http-basic-check-$NAME_SUFFIX --quiet
    
  3. 删除实例模板、cloud-init 配置、映像、基本虚拟机、永久性磁盘和快照时间表。

    gcloud compute instance-templates delete template-$NAME_SUFFIX --quiet
    
    rm cloud-init.yaml
    
    gcloud compute images delete image-$NAME_SUFFIX --quiet
    
    gcloud compute instances delete vm-base-$NAME_SUFFIX --zone=$ZONE1 --quiet
    
    gcloud compute disks delete disk-$NAME_SUFFIX --zone=$ZONE1 --quiet
    
    gcloud compute resource-policies delete \
        snapshot-schedule-$NAME_SUFFIX --region $REGION --quiet
    
  4. 列出实例创建的快照和磁盘,然后将它们删除:

    gcloud compute disks list --filter="name:disk-$NAME_SUFFIX" \
        --uri | xargs gcloud compute disks delete
    
    gcloud compute snapshots list --filter="name:disk-$NAME_SUFFIX" \
        --uri | xargs gcloud compute snapshots delete
    
  5. 删除自定义角色和服务账号:

    gcloud iam roles delete instance_snapshot_management_$NAME_SUFFIX \
      --project=$PROJECT_ID --quiet
    
    gcloud iam service-accounts delete \
        instance-sa-$NAME_SUFFIX@$PROJECT_ID.iam.gserviceaccount.com --quiet
    
  6. 删除防火墙规则:

    gcloud compute firewall-rules delete allow-health-check-$NAME_SUFFIX --quiet
    
    gcloud compute firewall-rules delete allow-ssh-$NAME_SUFFIX --quiet
    
    gcloud compute firewall-rules delete allow-http-$NAME_SUFFIX --quiet
    
  7. 删除子网和 VPC:

    gcloud compute networks subnets delete \
        subnet-$NAME_SUFFIX-$REGION --region=$REGION --quiet
    
    gcloud compute networks delete network-$NAME_SUFFIX --quiet
    

后续步骤