暂停和恢复虚拟机


本文档介绍如何暂停和恢复虚拟机实例。如需了解如何停止和启动实例,请参阅停止和启动实例。如需了解实例生命周期,请参阅实例生命周期文档。

如果希望保留虚拟机实例,但不想在未使用时付费,则可以暂停虚拟机。如果暂停虚拟机,Google 会保留您项目中的虚拟机,并将虚拟机内存的内容迁移到存储空间。在虚拟机暂停期间,Google 只会针对用于保留虚拟机内存的存储空间收费。静态 IP 地址等特性仍会保留,因此当您恢复虚拟机时,网络功能会按预期工作。恢复后,Google 会将虚拟机的内存从存储空间移回实例,并开始对正在运行的虚拟机实例收费。

暂停实例适用于:

  • 在关停期间(例如晚上或周末)未充分利用的开发和测试环境,可节省费用或者实现比创建新虚拟机实例更快的初始化速度。
  • 在实例启动完成后、准备处理第一个请求之前需要进行长时间初始化的应用,例如虚拟开发者工作站或复杂的 Java 应用。

暂停的工作原理

如果暂停实例,则系统会向实例的操作系统发送 ACPI S3 暂停信号。暂停实例类似于关闭笔记本电脑的盖子,让实例处于 SUSPENDED 状态。

暂停实例在以下方面与停止实例不同:

挂接到实例的所有资源仍继续挂接到该实例,并且会产生费用,包括永久性磁盘和静态或保留的外部 IP 地址。即使已暂停实例,我们也会根据价格表对所有这些资源收费。

您无法使用客机环境中内置的标准过程暂停实例。也无法使用命令(例如 Ubuntu 16.04 及更高版本中的 systemctl suspend 命令)。您只能使用 Google Cloud CLI 或 REST 暂停实例。

如果您在以后恢复实例时并不在意恢复实例的内存和设备状态,则可以改为停止实例,这样不会产生额外的存储费用。

准备工作

  • 设置身份验证(如果尚未设置)。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以按如下方式向 Compute Engine 进行身份验证。

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

    控制台

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

    gcloud

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

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

    Go

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

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

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

      gcloud auth application-default login

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

    Java

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

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

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

      gcloud auth application-default login

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

    Node.js

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

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

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

      gcloud auth application-default login

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

    PHP

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

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

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

      gcloud auth application-default login

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

    Python

    如需从本地开发环境使用本页面上的 Python 示例,请安装并初始化 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

限制

此功能存在以下限制:

  • 您无法暂停使用 GPU 的实例。
  • 您无法使用客机环境中内置的标准过程暂停实例。也无法使用命令(例如 Ubuntu 16.04 及更高版本中的 systemctl suspend 命令)。客机内信号会被忽略。
  • 在虚拟机自动停止之前,您最多只能将实例暂停 60 天。
  • 您无法暂停内存超过 208 GB 的实例。
  • 您可以暂停抢占式实例,但抢占式实例可能会在其成功暂停之前终止。
  • 您无法暂停机密虚拟机。
  • 您无法暂停挂接了 CSEK 保护的磁盘的虚拟机。

本地 SSD

通常,暂停使用本地 SSD 的虚拟机实例会舍弃本地 SSD 驱动器上的所有数据,其行为与停止实例相同。

如需了解详情,请参阅本地 SSD 文档

抢占式虚拟机

您可以暂停抢占式虚拟机,但如果抢占操作(不是抢占操作之前的预先警告)在暂停操作完成之前执行,则暂停操作会退出并且实例会被抢占。

操作系统兼容性

Compute Engine 上提供的大多数操作系统 (OS) 都支持暂停和恢复功能,但也有少数操作系统不支持。如需查看完整列表,请参阅操作系统详细信息页面。

价格

暂停实例时,您需要为以下各项支付费用:

  • 实例内存(请参阅已暂停的虚拟机实例的价格)。
  • 挂接到实例的启动磁盘和任何额外磁盘的任何永久性磁盘使用量(请参阅永久性磁盘价格)。
  • 挂接到实例的任何静态 IP 地址
  • 暂停虚拟机可能会节省软件许可费。例如,如果您暂停 Windows 虚拟机,则不会产生 Windows 许可费。其他映像的许可费可能受不同条款及条件的约束,即使已暂停,也可能会产生费用。

暂停实例

如需暂停实例,请使用Google Cloud 控制台gcloud CLIAPICloud 客户端库

您无法使用客机环境中内置的标准过程暂停实例。您可以使用 Google Cloud 控制台、Google Cloud CLI 或 API 来暂停实例。

如果在实例启动后过早触发暂停,则暂停操作可能会失败。必须完全启动实例(包括访客代理等进程),暂停操作才能成功。

控制台

  1. 在 Google Cloud 控制台中,转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 选择一个或多个要暂停的实例。

  3. 点击暂停

  4. 出现提示时,如果您要舍弃本地 SSD 数据,请选择舍弃 SSD 内容。实例恢复后,该实例上的任何本地 SSD 数据都将已舍弃。

gcloud

如需在 Google Cloud CLI 中暂停实例,请运行以下命令:

 gcloud compute instances suspend VM_NAME

在发出暂停实例的请求后,Compute Engine 可能需要一些时间来保留实例所需的所有数据。在此期间,只要实例仍在运行,您都将继续为其付费。

已暂停的实例标记为 SUSPENDED 状态。请通过发出 describe 请求来检查实例的状态:

gcloud compute instances describe VM_NAME

如需暂停具有本地 SSD 数据的实例,则必须提供 --discard-local-ssd 标志

gcloud compute instances suspend VM_NAME --discard-local-ssd

使用 --discard-local-ssd--discard-local-ssd=True 会舍弃本地 SSD 的内容。Compute Engine --discard-local-ssd=False 目前处于公开预览版阶段。使用此标志会在暂停期间保存最多 16 个本地 SSD 磁盘的内容。如需了解详情,请参阅本地 SSD 文档

Go

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "cloud.google.com/go/compute/apiv1/computepb"
)

// suspendInstance suspends a running Google Compute Engine instance.
func suspendInstance(w io.Writer, projectID, zone, instanceName string) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	req := &computepb.SuspendInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
	}

	op, err := instancesClient.Suspend(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to suspend instance: %w", err)
	}

	if err = op.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Instance suspended\n")

	return nil
}

Java


import com.google.cloud.compute.v1.Instance.Status;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class SuspendInstance {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // project: project ID or project number of the Cloud project your instance belongs to.
    // zone: name of the zone your instance belongs to.
    // instanceName: name of the instance your want to suspend.

    String project = "your-project-id";
    String zone = "zone-name";
    String instanceName = "instance-name";

    suspendInstance(project, zone, instanceName);
  }

  // Suspend a running Google Compute Engine instance.
  // For limitations and compatibility on which instances can be suspended,
  // see: https://cloud.google.com/compute/docs/instances/suspend-resume-instance#limitations
  public static void suspendInstance(String project, String zone, String instanceName)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Instantiates a client.
    try (InstancesClient instancesClient = InstancesClient.create()) {

      Operation operation = instancesClient.suspendAsync(project, zone, instanceName)
          .get(300, TimeUnit.SECONDS);

      if (operation.hasError() || !instancesClient.get(project, zone, instanceName).getStatus()
          .equalsIgnoreCase(Status.SUSPENDED.toString())) {
        System.out.println("Cannot suspend instance. Try again!");
        return;
      }

      System.out.printf("Instance suspended successfully ! %s", instanceName);
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const instanceName = 'YOUR_INSTANCE_NAME';

const compute = require('@google-cloud/compute');

// Suspends a running Google Compute Engine instance.
async function suspendInstance() {
  const instancesClient = new compute.InstancesClient();

  const [response] = await instancesClient.suspend({
    project: projectId,
    zone,
    instance: instanceName,
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.ZoneOperationsClient();

  // Wait for the create operation to complete.
  while (operation.status !== 'DONE') {
    [operation] = await operationsClient.wait({
      operation: operation.name,
      project: projectId,
      zone: operation.zone.split('/').pop(),
    });
  }

  console.log('Instance suspended.');
}

suspendInstance();

PHP

use Google\Cloud\Compute\V1\Client\InstancesClient;
use Google\Cloud\Compute\V1\SuspendInstanceRequest;

/**
 * Suspend a running Google Compute Engine instance.
 *
 * @param string $projectId Project ID or project number of the Cloud project your instance belongs to.
 * @param string $zone Name of the zone your instance belongs to.
 * @param string $instanceName Name of the instance you want to suspend.
  *
 * @throws \Google\ApiCore\ApiException if the remote call fails.
 * @throws \Google\ApiCore\ValidationException if local error occurs before remote call.
 */
function suspend_instance(
    string $projectId,
    string $zone,
    string $instanceName
) {
    // Suspend the running Compute Engine instance using InstancesClient.
    $instancesClient = new InstancesClient();
    $request = (new SuspendInstanceRequest())
        ->setInstance($instanceName)
        ->setProject($projectId)
        ->setZone($zone);
    $operation = $instancesClient->suspend($request);

    // Wait for the operation to complete.
    $operation->pollUntilComplete();
    if ($operation->operationSucceeded()) {
        printf('Instance %s suspended successfully' . PHP_EOL, $instanceName);
    } else {
        $error = $operation->getError();
        printf('Failed to suspend instance: %s' . PHP_EOL, $error?->getMessage());
    }
}

Python

from __future__ import annotations

import sys
from typing import Any

from google.api_core.extended_operation import ExtendedOperation
from google.cloud import compute_v1


def wait_for_extended_operation(
    operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
) -> Any:
    """
    Waits for the extended (long-running) operation to complete.

    If the operation is successful, it will return its result.
    If the operation ends with an error, an exception will be raised.
    If there were any warnings during the execution of the operation
    they will be printed to sys.stderr.

    Args:
        operation: a long-running operation you want to wait on.
        verbose_name: (optional) a more verbose name of the operation,
            used only during error and warning reporting.
        timeout: how long (in seconds) to wait for operation to finish.
            If None, wait indefinitely.

    Returns:
        Whatever the operation.result() returns.

    Raises:
        This method will raise the exception received from `operation.exception()`
        or RuntimeError if there is no exception set, but there is an `error_code`
        set for the `operation`.

        In case of an operation taking longer than `timeout` seconds to complete,
        a `concurrent.futures.TimeoutError` will be raised.
    """
    result = operation.result(timeout=timeout)

    if operation.error_code:
        print(
            f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
            file=sys.stderr,
            flush=True,
        )
        print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
        raise operation.exception() or RuntimeError(operation.error_message)

    if operation.warnings:
        print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
        for warning in operation.warnings:
            print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)

    return result


def suspend_instance(project_id: str, zone: str, instance_name: str) -> None:
    """
    Suspend a running Google Compute Engine instance.
    Args:
        project_id: project ID or project number of the Cloud project your instance belongs to.
        zone: name of the zone your instance belongs to.
        instance_name: name of the instance you want to suspend.
    """
    instance_client = compute_v1.InstancesClient()

    operation = instance_client.suspend(
        project=project_id, zone=zone, instance=instance_name
    )

    wait_for_extended_operation(operation, "suspend instance")

REST

在 API 中,使用 instances.suspend 方法发出请求:

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

替换以下内容:

  • PROJECT_ID:项目 ID
  • ZONE:虚拟机所在的区域
  • VM_NAME:您要暂停的实例

在发出暂停实例的请求后,Compute Engine 可能需要一些时间来保留实例所需的所有数据。在此期间,只要实例仍在运行,您都将为其付费。

Compute Engine 将已暂停的实例标记为 SUSPENDED 状态。请通过发出 GET 请求来检查实例的状态:

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

在状态字段中可以看到实例的状态。例如:

...
"zone": "https://content.googleapis.com/compute/v1/projects/example-project/zones/us-central1-a",
"status": "SUSPENDED",
"name": "example-vm",
...

如需暂停具有超过 {maximum_local_ssd_disks_for_suspend}} 本地 SSD 磁盘的实例,您必须通过提供 discardLocalSsd 查询参数来舍弃本地 SSD 数据:

https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/suspend?discardLocalSsd=true

恢复已暂停的实例

只有在虚拟机所在的可用区中有足够的容量时,您才能恢复实例。在大多数情况下,这不是问题,但如果您遇到容量问题,请稍后重试恢复请求。

如需恢复已暂停的实例,请使用 Google Cloud 控制台gcloud CLIAPICloud 客户端库

控制台

  1. 在 Google Cloud 控制台中,转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 选择一个或多个要恢复的实例。

  3. 点击启动/恢复

gcloud

如需在 Google Cloud CLI 中恢复实例,请运行以下命令:

 gcloud compute instances resume VM_NAME

在发出恢复实例的请求之后,Compute Engine 可能需要一些时间来恢复实例所需的所有数据。 在此期间,只要实例正在恢复,您都将为其付费。

当实例标记为 RUNNING 时,则该实例已恢复。请通过发出描述请求来检查实例的状态:

gcloud compute instances describe VM_NAME

Go

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "cloud.google.com/go/compute/apiv1/computepb"
)

// resumeInstance resumes a suspended Google Compute Engine instance
// (with unencrypted disks).
func resumeInstance(w io.Writer, projectID, zone, instanceName string) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	getInstanceReq := &computepb.GetInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
	}

	instance, err := instancesClient.Get(ctx, getInstanceReq)
	if err != nil {
		return fmt.Errorf("unable to get instance: %w", err)
	}

	if instance.GetStatus() != "SUSPENDED" {
		return fmt.Errorf(
			"only suspended instances can be resumed, instance %s is in %s state",
			instanceName,
			instance.GetStatus(),
		)
	}

	resumeInstanceReq := &computepb.ResumeInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
	}

	op, err := instancesClient.Resume(ctx, resumeInstanceReq)
	if err != nil {
		return fmt.Errorf("unable to resume instance: %w", err)
	}

	if err = op.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Instance resumed\n")

	return nil
}

Java


import com.google.cloud.compute.v1.Instance.Status;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ResumeInstance {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // project: project ID or project number of the Cloud project your instance belongs to.
    // zone: name of the zone your instance belongs to.
    // instanceName: name of the instance your want to resume.

    String project = "your-project-id";
    String zone = "zone-name";
    String instanceName = "instance-name";

    resumeInstance(project, zone, instanceName);
  }

  // Resume a suspended Google Compute Engine instance (with unencrypted disks).
  // Instance state changes to RUNNING, if successfully resumed.
  public static void resumeInstance(String project, String zone, String instanceName)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Instantiates a client.
    try (InstancesClient instancesClient = InstancesClient.create()) {

      String currentInstanceState = instancesClient.get(project, zone, instanceName).getStatus();

      // Check if the instance is currently suspended.
      if (!currentInstanceState.equalsIgnoreCase(Status.SUSPENDED.toString())) {
        throw new RuntimeException(
            String.format("Only suspended instances can be resumed. Instance %s is in %s state.",
                instanceName, currentInstanceState));
      }

      Operation operation = instancesClient.resumeAsync(project, zone, instanceName)
          .get(300, TimeUnit.SECONDS);

      if (operation.hasError() || !instancesClient.get(project, zone, instanceName).getStatus()
          .equalsIgnoreCase(
              Status.RUNNING.toString())) {
        System.out.println("Cannot resume instance. Try again!");
        return;
      }

      System.out.printf("Instance resumed successfully ! %s", instanceName);
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const instanceName = 'YOUR_INSTANCE_NAME';

const compute = require('@google-cloud/compute');

// Resumes a suspended Google Compute Engine instance (with unencrypted disks).
async function resumeInstance() {
  const instancesClient = new compute.InstancesClient();

  const [instance] = await instancesClient.get({
    project: projectId,
    zone,
    instance: instanceName,
  });

  if (instance.status !== 'SUSPENDED') {
    throw new Error(
      'Only suspended instances can be resumed.' +
        `Instance ${instanceName} is in ${instance.status} state.`
    );
  }

  const [response] = await instancesClient.resume({
    project: projectId,
    zone,
    instance: instanceName,
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.ZoneOperationsClient();

  // Wait for the create operation to complete.
  while (operation.status !== 'DONE') {
    [operation] = await operationsClient.wait({
      operation: operation.name,
      project: projectId,
      zone: operation.zone.split('/').pop(),
    });
  }

  console.log('Instance resumed.');
}

resumeInstance();

PHP

use Google\Cloud\Compute\V1\Client\InstancesClient;
use Google\Cloud\Compute\V1\ResumeInstanceRequest;

/**
 * Resume a suspended Google Compute Engine instance (with unencrypted disks).
 *
 * @param string $projectId Project ID or project number of the Cloud project your instance belongs to.
 * @param string $zone Name of the zone your instance belongs to.
 * @param string $instanceName Name of the instance you want to resume.
  *
 * @throws \Google\ApiCore\ApiException if the remote call fails.
 * @throws \Google\ApiCore\ValidationException if local error occurs before remote call.
 */
function resume_instance(
    string $projectId,
    string $zone,
    string $instanceName
) {
    // Resume the suspended Compute Engine instance using InstancesClient.
    $instancesClient = new InstancesClient();
    $request = (new ResumeInstanceRequest())
        ->setInstance($instanceName)
        ->setProject($projectId)
        ->setZone($zone);
    $operation = $instancesClient->resume($request);

    // Wait for the operation to complete.
    $operation->pollUntilComplete();
    if ($operation->operationSucceeded()) {
        printf('Instance %s resumed successfully' . PHP_EOL, $instanceName);
    } else {
        $error = $operation->getError();
        printf('Failed to resume instance: %s' . PHP_EOL, $error?->getMessage());
    }
}

Python

from __future__ import annotations

import sys
from typing import Any

from google.api_core.extended_operation import ExtendedOperation
from google.cloud import compute_v1


def wait_for_extended_operation(
    operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
) -> Any:
    """
    Waits for the extended (long-running) operation to complete.

    If the operation is successful, it will return its result.
    If the operation ends with an error, an exception will be raised.
    If there were any warnings during the execution of the operation
    they will be printed to sys.stderr.

    Args:
        operation: a long-running operation you want to wait on.
        verbose_name: (optional) a more verbose name of the operation,
            used only during error and warning reporting.
        timeout: how long (in seconds) to wait for operation to finish.
            If None, wait indefinitely.

    Returns:
        Whatever the operation.result() returns.

    Raises:
        This method will raise the exception received from `operation.exception()`
        or RuntimeError if there is no exception set, but there is an `error_code`
        set for the `operation`.

        In case of an operation taking longer than `timeout` seconds to complete,
        a `concurrent.futures.TimeoutError` will be raised.
    """
    result = operation.result(timeout=timeout)

    if operation.error_code:
        print(
            f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
            file=sys.stderr,
            flush=True,
        )
        print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
        raise operation.exception() or RuntimeError(operation.error_message)

    if operation.warnings:
        print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
        for warning in operation.warnings:
            print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)

    return result


def resume_instance(project_id: str, zone: str, instance_name: str) -> None:
    """
    Resume a suspended Google Compute Engine instance (with unencrypted disks).
    Args:
        project_id: project ID or project number of the Cloud project your instance belongs to.
        zone: name of the zone your instance belongs to.
        instance_name: name of the instance you want to resume.
    """
    instance_client = compute_v1.InstancesClient()

    instance = instance_client.get(
        project=project_id, zone=zone, instance=instance_name
    )
    if instance.status != compute_v1.Instance.Status.SUSPENDED.name:
        raise RuntimeError(
            f"Only suspended instances can be resumed. "
            f"Instance {instance_name} is in {instance.status} state."
        )

    operation = instance_client.resume(
        project=project_id, zone=zone, instance=instance_name
    )

    wait_for_extended_operation(operation, "instance resumption")

REST

instances.resume 方法发出请求:

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

替换以下内容:

  • PROJECT_ID:此请求的项目 ID
  • ZONE:虚拟机所在的区域
  • VM_NAME:要恢复的实例

在发出恢复实例的请求之后,Compute Engine 可能需要一些时间来恢复实例所需的所有数据。在此期间,只要实例正在恢复,您都将为其付费

恢复完成后,Compute Engine 会将实例标记为 RUNNING 状态。请通过发出 GET 请求来检查实例的状态:

GET https://compute.googleapis.com/compute/v1/projects/example-project/zones/us-central1-a/instances/example-instance

在状态字段中可以看到实例的状态。例如:

...
"zone": "https://content.googleapis.com/compute/v1/projects/example-project/zones/us-central1-a",
"status": "RUNNING",
"name": "example-instance",
...

暂停过程

发出暂停请求时,您可以向虚拟机实例发送 ACPI 暂停信号。如果虚拟机在几分钟内未响应 ACPI S3 暂停信号,则 Compute Engine 将取消暂停尝试并将虚拟机恢复到 RUNNING 状态。

下表描述了暂停虚拟机实例对其关联资源的影响:

资源 支持
内存 您只能暂停内存小于或等于 208 GB 的虚拟机
本地 SSD 本地 SSD 数据会被舍弃
永久性磁盘 永久性 HDD 和 SSD 磁盘会保留
IP 地址 在暂停期间,临时 IP 地址会被释放,但静态 IP 地址仍然会挂接到虚拟机实例。如果您想保留临时 ID,请提升它。
虚拟机配置(例如机器类型、元数据、标签等等) 除了临时 IP 地址外,所有虚拟机配置都会保留并在实例恢复时恢复。

配置 Debian 虚拟机以支持暂停和恢复

运行 Debian 8 和 9 的虚拟机可以暂停和恢复,但必须事先配置。如需配置您的 Debian 实例,请按照以下一组说明(方案 A 或方案 B)操作。如果可以的话,我们建议您配置 ACPID(方案 A)。

方案 A

此方案会将 ACPID 配置为处理休眠按钮事件,并添加用于处理休眠事件的 Shell 脚本。

  1. 使用 ssh 连接到您的虚拟机实例:

    gcloud compute ssh VM_NAME
    
  2. 在虚拟机实例的 acpi 文件夹下,创建一个目录:

    sudo mkdir -p /etc/acpi/events/
    
  3. 将 ACPID 配置为处理休眠按钮事件:

    cat <<EOF | sudo tee /etc/acpi/events/sleepbtn-acpi-support
    event=button[ /]sleep
    action=/etc/acpi/sleepbtn-acpi-support.sh
    EOF
    
  4. 创建休眠事件处理脚本:

    cat <<EOF | sudo tee /etc/acpi/sleepbtn-acpi-support.sh
    #!/bin/sh
    echo mem > /sys/power/state
    EOF
    
  5. 设置脚本的权限:

    sudo chmod 755 /etc/acpi/sleepbtn-acpi-support.sh
    
  6. 重启 ACPID:

    sudo systemctl restart acpid.service
    

方案 B

  1. 使用 ssh 连接到您的虚拟机实例:

    gcloud compute ssh VM_NAME
    
  2. 在虚拟机实例上,安装 dbus

    sudo apt-get install dbus
    
  3. 重启 logind

    sudo systemctl restart systemd-logind.service
    

后续步骤