创建和使用 Spot 虚拟机


本页面介绍如何创建和管理 Spot 虚拟机,包括以下主题:

  • 如何创建、启动和识别 Spot 虚拟机
  • 如何检测、处理和测试 Spot 虚拟机的抢占
  • Spot 虚拟机的最佳做法

Spot 虚拟机是具有 Spot 配置模型的虚拟机 (VM) 实例。与标准虚拟机的价格相比,您可以按 60-91% 的折扣获得 Spot 虚拟机。但是,Compute Engine 可能会随时抢占 Spot 虚拟机来收回资源。建议只将 Spot 虚拟机用于可承受虚拟机抢占的容错应用。在您决定创建 Spot 虚拟机之前,请确保您的应用可以处理抢占

准备工作

  • 阅读 Spot 虚拟机文档:
    • 查看 Spot 虚拟机的限制价格
    • 为了防止 Spot 虚拟机消耗标准虚拟机的 CPU、GPU 和磁盘的配额,请考虑为 Spot 虚拟机申请抢占式配额
  • 如果您尚未设置身份验证,请进行设置。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以按如下方式向 Compute Engine 进行身份验证。

    选择标签页以了解您打算如何使用本页面上的示例:

    控制台

    当您使用 Google Cloud 控制台访问 Google Cloud 服务和 API 时,无需设置身份验证。

    gcloud

    1. 安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init
    2. 设置默认区域和可用区

    Terraform

    如需从本地开发环境使用本页面上的 Terraform 示例,请安装并初始化 gcloud CLI,然后使用用户凭据设置应用默认凭据。

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

      gcloud init
    3. 为您的 Google 账号创建本地身份验证凭据:

      gcloud auth application-default login

    如需了解详情,请参阅 为本地开发环境设置身份验证

    REST

    如需在本地开发环境中使用本页面上的 REST API 示例,请使用您提供给 gcloud CLI 的凭据。

      安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init

创建 Spot 虚拟机

使用 Google Cloud 控制台、gcloud CLI 或 Compute Engine API 创建 Spot 虚拟机。Spot 虚拟机是指配置为使用 Spot 预配模型的任何虚拟机:

  • 在 Google Cloud 控制台中将虚拟机预配模型设置为 Spot
  • 在 gcloud CLI 中使用 --provisioning-model=SPOT
  • 在 Compute Engine API 中使用 "provisioningModel": "SPOT"

控制台

  1. 在 Google Cloud 控制台中,转到创建实例页面。

    转到“创建实例”

  2. 之后,执行以下操作:

    1. 可用性政策部分中,从虚拟机预配模型列表中选择 Spot。此设置会对虚拟机停用自动重启和主机维护选项,并启用终止操作选项。
    2. 可选:在虚拟机终止时列表中,选择要在 Compute Engine 抢占虚拟机时执行的操作:
      • 如需在抢占期间停止虚拟机,请选择停止(默认)。
      • 如需在抢占期间删除虚拟机,请选择删除
  3. 可选:指定其他虚拟机选项。如需了解详情,请参阅创建和启动虚拟机实例

  4. 要创建并启动该虚拟机,请点击创建

gcloud

如需通过 gcloud CLI 创建虚拟机,请使用 gcloud compute instances create 命令。如需创建 Spot 虚拟机,您必须添加 --provisioning-model=SPOT 标志。(可选)您也可以通过同时包括 --instance-termination-action 标志来为 Spot 虚拟机指定终止操作。

gcloud compute instances create VM_NAME \
    --provisioning-model=SPOT \
    --instance-termination-action=TERMINATION_ACTION

替换以下内容:

  • VM_NAME:新虚拟机的名称
  • TERMINATION_ACTION(可选):指定在 Compute Engine 抢占虚拟机时要执行的操作(STOP(默认行为)或 DELETE)。

如需详细了解您在创建虚拟机时可以指定的选项,请参阅创建和启动虚拟机实例。 例如,如需创建具有指定机器类型和映像的 Spot 虚拟机,请使用以下命令:

gcloud compute instances create VM_NAME \
    --provisioning-model=SPOT \
    [--image=IMAGE | --image-family=IMAGE_FAMILY] \
    --image-project=IMAGE_PROJECT \
    --machine-type=MACHINE_TYPE \
    --instance-termination-action=TERMINATION_ACTION

替换以下内容:

  • VM_NAME:新虚拟机的名称
  • IMAGE:指定以下其中一项:
    • IMAGE:公共映像或映像系列的特定版本。例如,特定映像为 --image=debian-10-buster-v20200309
    • 映像系列。此项表示通过最新的未弃用的操作系统映像创建虚拟机。例如,如果您指定 --image-family=debian-10,则 Compute Engine 会通过 Debian 10 映像系列中最新版本的操作系统映像创建虚拟机。
  • IMAGE_PROJECT:包含映像的项目。 例如,如果您将 debian-10 指定为映像系列,请将 debian-cloud 指定为映像项目。
  • MACHINE_TYPE:新虚拟机的预定义自定义机器类型。
  • TERMINATION_ACTION(可选):指定在 Compute Engine 抢占虚拟机时要执行的操作(STOP(默认行为)或 DELETE)。

    如需获取可用区中可用的机器类型列表,请使用带有 --zones 标志的 gcloud compute machine-types list 命令

Terraform

您可以通过 Terraform 资源使用调度块创建 Spot 实例


resource "google_compute_instance" "spot_vm_instance" {
  name         = "spot-instance-name"
  machine_type = "f1-micro"
  zone         = "us-central1-c"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
    }
  }

  scheduling {
    preemptible                 = true
    automatic_restart           = false
    provisioning_model          = "SPOT"
    instance_termination_action = "STOP"
  }

  network_interface {
    # A default network is created for all GCP projects
    network = "default"
    access_config {
    }
  }
}

REST

如需通过 Compute Engine API 创建虚拟机,请使用 instances.insert 方法。您必须为虚拟机指定机器类型和名称。(可选)您也可以指定启动磁盘的映像。

如需创建 Spot 虚拟机,您必须添加 "provisioningModel": spot 字段。(可选)您也可以通过添加 "instanceTerminationAction" 字段来为 Spot 虚拟机指定终止操作。

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances
{
 "machineType": "zones/ZONE/machineTypes/MACHINE_TYPE",
 "name": "VM_NAME",
 "disks": [
   {
     "initializeParams": {
       "sourceImage": "projects/IMAGE_PROJECT/global/images/IMAGE"
     },
     "boot": true
   }
 ]
 "scheduling":
 {
     "provisioningModel": "SPOT",
     "instanceTerminationAction": "TERMINATION_ACTION"
 },
 ...
}

替换以下内容:

  • PROJECT_ID:要在其中创建虚拟机的项目的 ID
  • ZONE:要在其中创建虚拟机的可用区。该可用区还必须支持要用于新虚拟机的机器类型。
  • MACHINE_TYPE:新虚拟机的预定义自定义机器类型。
  • VM_NAME:新虚拟机的名称
  • IMAGE_PROJECT:包含映像的项目。 例如,如果您将 debian-10 指定为映像系列,请将 debian-cloud 指定为映像项目。
  • IMAGE:指定以下其中一项:
    • 公共映像的特定版本。例如,特定映像为 "sourceImage": "projects/debian-cloud/global/images/debian-10-buster-v20200309",其中 debian-cloudIMAGE_PROJECT
    • 映像系列。此项表示通过最新的未弃用的操作系统映像创建虚拟机。例如,如果您指定 "sourceImage": "projects/debian-cloud/global/images/family/debian-10",其中 debian-cloudIMAGE_PROJECT,则 Compute Engine 会通过 Debian 10 映像系列中最新版本的操作系统映像创建虚拟机。
  • TERMINATION_ACTION(可选):指定在 Compute Engine 抢占虚拟机时要执行的操作(STOP(默认行为)或 DELETE)。

如需详细了解您在创建虚拟机时可以指定的选项,请参阅创建和启动虚拟机实例

如需创建多个具有相同属性的 Spot 虚拟机,您可以执行以下操作:创建实例模板,然后使用该模板创建代管式实例组 (MIG)。如需了解详情,请参阅最佳做法

启动 Spot 虚拟机

与其他虚拟机一样,Spot 虚拟机会在创建后启动。同样,如果停止 Spot 虚拟机,您可以重启虚拟机以恢复 RUNNING 状态。只要有容量,您就可以根据需要停止和重启抢占式 Spot 虚拟机。如需了解详情,请参阅虚拟机实例生命周期

如果 Compute Engine 停止自动扩缩代管实例组 (MIG) 或 Google Kubernetes Engine (GKE) 集群中的一个或多个 Spot 虚拟机,则实例组会在资源再次可用时重启虚拟机。

确定虚拟机的预配模型和终止操作

确定虚拟机的预配模型,以查看它是标准虚拟机、Spot 虚拟机还是抢占式虚拟机。对于 Spot 虚拟机,您还可以识别终止操作。您可以使用 Google Cloud 控制台、gcloud CLI 或 Compute Engine API 识别虚拟机的预配模型和终止操作。

控制台

  1. 转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 点击要识别的虚拟机的名称虚拟机实例详情页面随即打开。

  3. 转到页面底部的管理部分。在可用性政策子部分中,检查以下选项:

    • 如果虚拟机预配模型设置为 Spot,则虚拟机为 Spot 虚拟机。
      • 虚拟机终止时表示在 Compute Engine 抢占虚拟机时要执行的操作(停止虚拟机或删除虚拟机)。
    • 否则,如果虚拟机预配模型设置为标准-
      • 如果可抢占性选项设置为开启,则虚拟机为抢占式虚拟机。
      • 否则,该虚拟机为标准虚拟机。

gcloud

如需通过 gcloud CLI 描述虚拟机,请使用 gcloud compute instances describe 命令

gcloud compute instances describe VM_NAME

其中 VM_NAME 是您要检查的虚拟机的名称

在输出中,检查 scheduling 字段以识别虚拟机:

  • 如果输出包含设置为 SPOTprovisioningModel 字段(类似于以下内容),则该虚拟机是 Spot 虚拟机。

    ...
    scheduling:
    ...
    provisioningModel: SPOT
    instanceTerminationAction: TERMINATION_ACTION
    ...
    

    其中,TERMINATION_ACTION 表示在 Compute Engine 抢占虚拟机时要执行的操作(停止 (STOP) 虚拟机或删除 (DELETE) 虚拟机)。如果缺少 instanceTerminationAction 字段,则默认值为 STOP

  • 否则,如果输出包含设置为 standardprovisioningModel 字段或者如果输出省略 provisioningModel 字段:

    • 如果输出包含设置为 truepreemptible 字段,则虚拟机为抢占式虚拟机。
    • 否则,该虚拟机为标准虚拟机。

REST

如需通过 Compute Engine API 描述虚拟机,请使用 instances.get 方法

GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME

替换以下内容:

  • PROJECT_ID:虚拟机所属项目的 ID
  • ZONE:虚拟机所在的可用区
  • VM_NAME:您要检查的虚拟机的名称

在输出中,检查 scheduling 字段以识别虚拟机:

  • 如果输出包含设置为 SPOTprovisioningModel 字段(类似于以下内容),则该虚拟机是 Spot 虚拟机。

    {
      ...
      "scheduling":
      {
         ...
         "provisioningModel": "SPOT",
         "instanceTerminationAction": "TERMINATION_ACTION"
         ...
      },
      ...
    }
    

    其中,TERMINATION_ACTION 表示在 Compute Engine 抢占虚拟机时要执行的操作(停止 (STOP) 虚拟机或删除 (DELETE) 虚拟机)。如果缺少 instanceTerminationAction 字段,则默认值为 STOP

  • 否则,如果输出包含设置为 standardprovisioningModel 字段或者如果输出省略 provisioningModel 字段:

    • 如果输出包含设置为 truepreemptible 字段,则虚拟机为抢占式虚拟机。
    • 否则,该虚拟机为标准虚拟机。

使用关停脚本处理抢占

如果 Spot 虚拟机被 Compute Engine 抢占,您可以使用关停脚本在每个虚拟机被抢占前执行清理操作。例如,您可以正常停止正在运行的进程,并将检查点文件复制到 Cloud Storage

下面是一个关停脚本示例,您可以将其添加到正在运行的 Spot 虚拟机中,或者在创建新的 Spot 虚拟机时添加。该脚本会在虚拟机开始关停时运行,然后操作系统的常规 kill 命令会停止所有剩余进程。在正常停止所需程序后,该脚本会将检查点文件并行上传到 Cloud Storage 存储桶。

#!/bin/bash

MY_PROGRAM="PROGRAM_NAME" # For example, "apache2" or "nginx"
MY_USER="LOCAL_USER"
CHECKPOINT="/home/$MY_USER/checkpoint.out"
GSUTIL_OPTS="-m -o GSUtil:parallel_composite_upload_threshold=32M"
BUCKET_NAME="BUCKET_NAME" # For example, "my-checkpoint-files" (without gs://)

echo "Shutting down!  Seeing if ${MY_PROGRAM} is running."

# Find the newest copy of $MY_PROGRAM
PID="$(pgrep -n "$MY_PROGRAM")"

if [[ "$?" -ne 0 ]]; then
  echo "${MY_PROGRAM} not running, shutting down immediately."
  exit 0
fi

echo "Sending SIGINT to $PID"
kill -2 "$PID"

# Portable waitpid equivalent
while kill -0 "$PID"; do
   sleep 1
done

echo "$PID is done, copying ${CHECKPOINT} to gs://${BUCKET_NAME} as ${MY_USER}"

su "${MY_USER}" -c "gsutil $GSUTIL_OPTS cp $CHECKPOINT gs://${BUCKET_NAME}/"

echo "Done uploading, shutting down."

此脚本假定您满足以下条件:

  • 您已创建至少具备 Cloud Storage 读写权限的虚拟机。如需了解如何创建具有适当范围的虚拟机,请参阅身份验证文档

  • 您已有一个 Cloud Storage 存储桶,且拥有其写入权限。

如需将此脚本添加到虚拟机中,请将此脚本配置为与虚拟机上的应用搭配使用,并将其添加到虚拟机的元数据中。

  1. 复制或下载关停脚本:

    • 在替换以下内容后,复制上述关停脚本:

      • PROGRAM_NAME 是您要关停的进程或程序的名称,例如 apache2nginx
      • LOCAL_USER 是您用于登录虚拟机的用户名。
      • BUCKET_NAME 是您要用于保存程序检查点文件的 Cloud Storage 存储桶的名称。请注意,本例中的存储桶名称不是以 gs:// 开头。
    • 下载关停脚本到本地工作站,然后替换文件中的以下变量:

      • [PROGRAM_NAME] 是您要关停的进程或程序的名称,例如 apache2nginx
      • [LOCAL_USER] 是您用于登录虚拟机的用户名。
      • [BUCKET_NAME] 是您要用于保存程序检查点文件的 Cloud Storage 存储桶的名称。请注意,本例中的存储桶名称不是以 gs:// 开头。
  2. 将关停脚本添加到新虚拟机现有虚拟机

检测 Spot 虚拟机的抢占

使用 Google Cloud 控制台gcloud CLICompute Engine API 确定 Spot 虚拟机是否已被 Compute Engine 抢占。

控制台

您可以通过检查系统活动日志来检查虚拟机是否已被抢占。

  1. 在 Google Cloud 控制台中,转到日志页面。

    转到“日志”

  2. 选择您的项目并点击继续

  3. compute.instances.preempted 添加到按标签过滤或搜索文字字段。

  4. (可选)如果您要查看特定虚拟机的抢占操作,还可以输入虚拟机名称。

  5. 按 Enter 键以应用指定的过滤条件。Google Cloud 控制台会更新日志列表以仅显示虚拟机被抢占的操作。

  6. 在列表中选择一项操作,查看被抢占虚拟机的相关详细信息。

gcloud

gcloud compute operations list 命令过滤条件参数结合使用可以获取您的项目中的抢占事件列表。

gcloud compute operations list \
    --filter="operationType=compute.instances.preempted"

(可选)您可以使用其他过滤条件参数来进一步限定结果的范围。例如,如需仅查看代管式实例组中实例的抢占事件,请使用以下命令:

gcloud compute operations list \
    --filter="operationType=compute.instances.preempted AND targetLink:instances/BASE_INSTANCE_NAME"

其中 BASE_INSTANCE_NAME 是指定为代管式实例组中所有虚拟机的名称的前缀的基本名称。

输出内容类似如下:

NAME                  TYPE                         TARGET                                        HTTP_STATUS STATUS TIMESTAMP
systemevent-xxxxxxxx  compute.instances.preempted  us-central1-f/instances/example-instance-xxx  200         DONE   2015-04-02T12:12:10.881-07:00

操作类型 compute.instances.preempted 表示虚拟机实例已被抢占。您可以使用 gcloud compute operations describe 命令来获取特定抢占操作的相关详细信息。

gcloud compute operations describe SYSTEM_EVENT \
    --zone=ZONE

请替换以下内容:

  • SYSTEM_EVENTgcloud compute operations list 命令输出中的系统事件,例如 systemevent-xxxxxxxx
  • ZONE:系统事件的可用区,例如 us-central1-f

输出类似于以下内容:

...
operationType: compute.instances.preempted
progress: 100
selfLink: https://compute.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f/operations/systemevent-xxxxxxxx
startTime: '2015-04-02T12:12:10.881-07:00'
status: DONE
statusMessage: Instance was preempted.
...

REST

如需获取特定项目和可用区最近的系统操作列表,请使用 zoneOperations.get 方法

GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/operations

替换以下内容:

(可选)如需将响应范围限定为仅显示抢占操作,您可以在 API 请求中添加过滤条件:

operationType="compute.instances.preempted"

或者,如需查看特定虚拟机的抢占操作,请为过滤条件添加 targetLink 参数:

operationType="compute.instances.preempted" AND
targetLink="https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME

替换以下内容:+ PROJECT_ID项目 ID。+ ZONE可用区。+ VM_NAME:此可用区和项目中特定虚拟机的名称。

该响应包含最近的操作列表。例如,抢占类似于以下内容:

{
  "kind": "compute#operation",
  "id": "15041793718812375371",
  "name": "systemevent-xxxxxxxx",
  "zone": "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f",
  "operationType": "compute.instances.preempted",
  "targetLink": "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f/instances/example-instance",
  "targetId": "12820389800990687210",
  "status": "DONE",
  "statusMessage": "Instance was preempted.",
  ...
}

或者,您也可以通过虚拟机本身来确定虚拟机是否已被抢占。如果您要处理因 Compute Engine 抢占而导致的关停事件(与处理因关停脚本而导致的正常关停事件的方式不同),那么这种做法会非常实用。为此,只需在元数据服务器中检查虚拟机的默认元数据中的 preempted 值即可。

例如,在虚拟机中使用 curl 获取 preempted 的值:

curl "http://metadata.google.internal/computeMetadata/v1/instance/preempted" -H "Metadata-Flavor: Google"
TRUE

如果此值为 TRUE,则表示虚拟机已被 Compute Engine 抢占;否则此值为 FALSE

如果您要在关停脚本外部使用此命令,则可以将 ?wait_for_change=true 附加到该网址。这将执行挂起的 HTTP GET 请求,该请求仅在元数据更改并且虚拟机已被抢占时才会返回。

curl "http://metadata.google.internal/computeMetadata/v1/instance/preempted?wait_for_change=true" -H "Metadata-Flavor: Google"
TRUE

如何测试抢占设置

您可以在虚拟机上运行模拟维护事件来强制进行抢占。使用此功能可以测试应用如何处理 Spot 虚拟机。请参阅模拟主机维护事件,了解如何在实例上测试维护事件。

您还可以通过停止虚拟机实例来模拟虚拟机抢占,这样做不但可以省去模拟维护事件的操作,还可避免配额限制。

最佳做法

以下是一些可帮助您充分利用 Spot 虚拟机的最佳做法。

  • 使用实例模板。您可以使用实例模板创建具有相同属性的多个 Spot 虚拟机,而不是一次创建一个 Spot 虚拟机。使用 MIG 需要实例模板。或者,您还可以使用批量实例 API 创建多个 Spot 虚拟机。

  • 使用 MIG 按区域分布并自动重新创建 Spot 虚拟机。使用 MIG 可使 Spot 虚拟机上的工作负载更具灵活性和弹性。例如,您可以使用区域级 MIG 在多个可用区中分布虚拟机,这有助于减少资源可用性错误。此外,请使用自动修复在 Spot 虚拟机被抢占后自动重新创建这些虚拟机。

  • 选择较小的机器类型。Spot 虚拟机的资源来自于额外及备用的 Google Cloud 容量。对于较小的机器类型,Spot 虚拟机的容量通常更容易获取,因为这些机器类型所需的 vCPU 和内存等资源也较少。您可能会发现,通过选择较小的自定义机器类型可以增加 Spot 虚拟机的容量;但对于较小的预定义机器类型,容量可能会更大。例如,与 n2-standard-32 预定义机器类型的容量相比,n2-custom-24-96 自定义机器类型的容量可能更大,但 n2-standard-16 预定义机器类型的容量可能会比前者还要大。

  • 在非高峰时段运行大型 Spot 虚拟机集群。Google Cloud 数据中心的负载因地点和时段而异,但通常夜晚和周末的负载最低。因此,夜晚和周末是运行大型 Spot 虚拟机集群的最佳时间。

  • 将您的应用设计成容错且容抢占型应用。请务必应对以下情况:抢占模式会随着时间点的不同而发生变化。例如,如果某个可用区受到部分中断影响,则大量 Spot 虚拟机可能会被抢占,以便为需要在恢复过程中迁移的标准虚拟机腾出空间。在这一小段时间内,抢占率会看起来与其他任何一天完全不同。如果您的应用假设抢占始终以小组形式完成,您可能无法应对此类事件。

  • 重新尝试创建已被抢占的 Spot 虚拟机。如果您的 Spot 虚拟机已被抢占,建议先尝试创建新的 Spot 虚拟机一到两次,然后再恢复为标准虚拟机。建议您根据具体要求在集群中结合使用标准虚拟机和 Spot 虚拟机,以确保工作能够按照适当的速度继续执行。

  • 使用关停脚本。使用可保存作业进度的关停脚本来管理关停和抢占通知,以便作业可以接续上次进度执行,而不用从头开始执行。

后续步骤