将本地 SSD 添加到虚拟机


本地 SSD 专为临时存储使用场景(例如用作缓存或临时处理空间)而设计。由于本地 SSD 位于虚拟机运行所在的物理机器上,因此您只能在创建虚拟机期间创建这些 SSD。本地 SSD 不能用作启动设备。

对于第三代机器系列,当您创建虚拟机时,系统会向虚拟机添加一组本地 SSD 磁盘。向这些虚拟机添加本地 SSD 存储的唯一方法是:

  • 对于 C3 和 C3D,本地 SSD 存储空间仅适用于某些机器类型,例如 c3-standard-88-lssd
  • 对于 Z3、A3 和 A2 Ultra 机器系列,每种机器类型都附带了本地 SSD 存储空间。

对于 M3 以及第一代和第二代机器类型,您必须在创建虚拟机时指定本地 SSD 磁盘。

创建本地 SSD 磁盘后,必须先格式化并装载设备,然后才能使用它。

如需了解各种机器类型可用的本地 SSD 存储空间数量以及可以挂接到虚拟机的本地 SSD 磁盘数量,请参阅选择有效的本地 SSD 数量

准备工作

  • 在使用本地 SSD 之前,请先查看本地 SSD 限制
  • 查看本地 SSD 磁盘的数据持久化场景。
  • 如果您要将本地 SSD 添加到挂接了 GPU 的虚拟机 (VM) 实例,请参阅 GPU 区域和可用区的本地 SSD 可用性
  • 如果您尚未设置身份验证,请进行设置。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以选择以下任一选项向 Compute Engine 进行身份验证:

    Select the tab for how you plan to use the samples on this page:

    Console

    When you use the Google Cloud console to access Google Cloud services and APIs, you don't need to set up authentication.

    gcloud

    1. Install the Google Cloud CLI, then initialize it by running the following command:

      gcloud init
    2. Set a default region and zone.
    3. Terraform

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

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      如需了解详情,请参阅 Set up authentication for a local development environment

      Go

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

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      如需了解详情,请参阅 Set up authentication for a local development environment

      Java

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

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      如需了解详情,请参阅 Set up authentication for a local development environment

      Python

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

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      如需了解详情,请参阅 Set up authentication for a local development environment

      REST

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

        Install the Google Cloud CLI, then initialize it by running the following command:

        gcloud init

      如需了解详情,请参阅 Google Cloud 身份验证文档中的使用 REST 时进行身份验证

创建具有本地 SSD 的虚拟机

您可以使用 Google Cloud 控制台gcloud CLICompute Engine API 创建具有本地 SSD 磁盘存储空间的虚拟机。

控制台

  1. 转到创建实例页面。

    转到“创建实例”

  2. 为该虚拟机指定名称、区域和可用区。(可选)添加标记或标签。

  3. 机器配置部分中,选择包含目标机器类型的机器家族。

  4. 系列列表中选择一个系列,然后选择机器类型。

    • 对于第三代机器系列 C3 和 C3D,请选择以 -lssd 结尾的机器类型。
    • 对于 Z3、A3 和 A2 Ultra,每种机器类型都附带本地 SSD 存储空间。
    • 对于 M3 或第一代和第二代机器系列,在选择机器类型后,请执行以下操作:
      1. 展开高级选项部分。
      2. 展开磁盘,点击添加本地 SSD,然后执行以下操作:
        1. 配置本地 SSD 页面上,选择磁盘接口类型。
        2. 磁盘容量列表中选择所需的磁盘数量。
        3. 点击保存
  5. 继续虚拟机创建过程。

  6. 创建具有本地 SSD 磁盘的虚拟机后,必须先格式化并装载每个设备,然后才能使用磁盘。

gcloud

  • 对于 Z3、A3 和 A2 Ultra 机器系列,如需创建挂接了本地 SSD 磁盘的虚拟机,请按照创建实例说明创建使用该系列的任何可用机器类型的虚拟机。

  • 对于 C3 或 C3D 机器系列,如需创建挂接了本地 SSD 磁盘的虚拟机,请按照相关说明创建实例,但要指定包含本地 SSD 磁盘的实例类型 (-lssd)。

    例如,您可以创建一个具有两个本地 SSD 分区的 C3 虚拟机,这些分区使用 NVMe 磁盘接口,如下所示:

    gcloud compute instances create example-c3-instance \
       --zone ZONE \
       --machine-type c3-standard-8-lssd \
       --image-project IMAGE_PROJECT \
       --image-family IMAGE_FAMILY
    
  • 对于 M3 以及第一代和第二代机器系列,如需创建挂接了本地 SSD 磁盘的虚拟机,请按照相关说明创建实例,但要使用 --local-ssd 标志来创建并挂接本地 SSD 磁盘。如需创建多个本地 SSD 磁盘,请添加多个 --local-ssd 标志。或者,您也可以为每个 --local-ssd 标志设置接口值和设备名称。

    例如,您可以创建一个具有四个本地 SSD 磁盘的 M3 虚拟机,并指定磁盘接口类型,如下所示:

    gcloud compute instances create VM_NAME \
       --machine-type m3-ultramem-64 \
       --zone ZONE \
       --local-ssd interface=INTERFACE_TYPE,device-name=DEVICE-NAME \
       --local-ssd interface=INTERFACE_TYPE,device-name=DEVICE-NAME \
       --local-ssd interface=INTERFACE_TYPE,device-name=DEVICE-NAME \
       --local-ssd interface=INTERFACE_TYPE \
       --image-project IMAGE_PROJECT \
       --image-family IMAGE_FAMILY
    

替换以下内容:

  • VM_NAME:新虚拟机的名称
  • ZONE:要在其中创建虚拟机的可用区。如果您已配置 gcloud CLI compute/zone 属性或环境变量 CLOUDSDK_COMPUTE_ZONE,则此标志为可选标志。
  • INTERFACE_TYPE:要用于本地 SSD 设备的磁盘接口类型。如果是创建 M3 虚拟机或者您的启动磁盘映像具有优化的 NVMe 驱动程序,请指定 nvme。请为其他映像指定 scsi
  • DEVICE-NAME(可选):该名称指示要在客机操作系统符号链接中使用的磁盘名称。
  • IMAGE_FAMILY:您要在启动磁盘上安装的可用映像系列之一
  • IMAGE_PROJECT:映像系列所属的映像项目

如有必要,您可以使用 nvmescsi 的组合将本地 SSD 挂接到第一代或第二代虚拟机,以用于不同分区。nvme 设备的性能取决于实例的启动磁盘映像。第三代虚拟机仅支持 NVMe 磁盘接口。

创建具有本地 SSD 的虚拟机后,必须先格式化并装载每个设备,然后才能使用它。

Terraform

如需创建挂接了本地 SSD 磁盘的虚拟机,您可以使用 google_compute_instance 资源


# Create a VM with a local SSD for temporary storage use cases

resource "google_compute_instance" "default" {
  name         = "my-vm-instance-with-scratch"
  machine_type = "n2-standard-8"
  zone         = "us-central1-a"

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

  # Local SSD interface type; NVME for image with optimized NVMe drivers or SCSI
  # Local SSD are 375 GiB in size
  scratch_disk {
    interface = "SCSI"
  }

  network_interface {
    network = "default"
    access_config {}
  }
}

如需了解如何应用或移除 Terraform 配置,请参阅基本 Terraform 命令

如需生成 Terraform 代码,您可以使用 Google Cloud 控制台中的等效代码组件。
  1. 在 Google Cloud 控制台中,转到虚拟机实例页面。

    转到“虚拟机实例”

  2. 点击创建实例
  3. 指定所需的参数。
  4. 在页面顶部或底部,点击等效代码,然后点击 Terraform 标签页以查看 Terraform 代码。

Go

Go

试用此示例之前,请按照《Compute Engine 快速入门:使用客户端库》中的 Go 设置说明进行操作。 如需了解详情,请参阅 Compute Engine Go API 参考文档

如需向 Compute Engine 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "cloud.google.com/go/compute/apiv1/computepb"
	"google.golang.org/protobuf/proto"
)

// createWithLocalSSD creates a new VM instance with Debian 10 operating system and a local SSD attached.
func createWithLocalSSD(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()

	imagesClient, err := compute.NewImagesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewImagesRESTClient: %w", err)
	}
	defer imagesClient.Close()

	// List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details.
	newestDebianReq := &computepb.GetFromFamilyImageRequest{
		Project: "debian-cloud",
		Family:  "debian-12",
	}
	newestDebian, err := imagesClient.GetFromFamily(ctx, newestDebianReq)
	if err != nil {
		return fmt.Errorf("unable to get image from family: %w", err)
	}

	req := &computepb.InsertInstanceRequest{
		Project: projectID,
		Zone:    zone,
		InstanceResource: &computepb.Instance{
			Name: proto.String(instanceName),
			Disks: []*computepb.AttachedDisk{
				{
					InitializeParams: &computepb.AttachedDiskInitializeParams{
						DiskSizeGb:  proto.Int64(10),
						SourceImage: newestDebian.SelfLink,
						DiskType:    proto.String(fmt.Sprintf("zones/%s/diskTypes/pd-standard", zone)),
					},
					AutoDelete: proto.Bool(true),
					Boot:       proto.Bool(true),
					Type:       proto.String(computepb.AttachedDisk_PERSISTENT.String()),
				},
				{
					InitializeParams: &computepb.AttachedDiskInitializeParams{
						DiskType: proto.String(fmt.Sprintf("zones/%s/diskTypes/local-ssd", zone)),
					},
					AutoDelete: proto.Bool(true),
					Type:       proto.String(computepb.AttachedDisk_SCRATCH.String()),
				},
			},
			MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/n1-standard-1", zone)),
			NetworkInterfaces: []*computepb.NetworkInterface{
				{
					Name: proto.String("global/networks/default"),
				},
			},
		},
	}

	op, err := instancesClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create 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 created\n")

	return nil
}

Java

Java

试用此示例之前,请按照《Compute Engine 快速入门:使用客户端库》中的 Java 设置说明进行操作。 如需了解详情,请参阅 Compute Engine Java API 参考文档

如需向 Compute Engine 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证


import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.Image;
import com.google.cloud.compute.v1.ImagesClient;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateWithLocalSsd {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // projectId: project ID or project number of the Cloud project you want to use.
    String projectId = "your-project-id";
    // zone: name of the zone to create the instance in. For example: "us-west3-b"
    String zone = "zone-name";
    // instanceName: name of the new virtual machine (VM) instance.
    String instanceName = "instance-name";

    createWithLocalSsd(projectId, zone, instanceName);
  }

  // Create a new VM instance with Debian 11 operating system and SSD local disk.
  public static void createWithLocalSsd(String projectId, String zone, String instanceName)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {

    int diskSizeGb = 10;
    boolean boot = true;
    boolean autoDelete = true;
    String diskType = String.format("zones/%s/diskTypes/pd-standard", zone);
    // Get the latest debian image.
    Image newestDebian = getImageFromFamily("debian-cloud", "debian-11");
    List<AttachedDisk> disks = new ArrayList<>();

    // Create the disks to be included in the instance.
    disks.add(
        createDiskFromImage(diskType, diskSizeGb, boot, newestDebian.getSelfLink(), autoDelete));
    disks.add(createLocalSsdDisk(zone));

    // Create the instance.
    Instance instance = createInstance(projectId, zone, instanceName, disks);

    if (instance != null) {
      System.out.printf("Instance created with local SSD: %s", instance.getName());
    }

  }

  // Retrieve the newest image that is part of a given family in a project.
  // Args:
  //    projectId: project ID or project number of the Cloud project you want to get image from.
  //    family: name of the image family you want to get image from.
  private static Image getImageFromFamily(String projectId, String family) throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the `imagesClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (ImagesClient imagesClient = ImagesClient.create()) {
      // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
      return imagesClient.getFromFamily(projectId, family);
    }
  }

  // Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
  // source for the new disk.
  //
  // Args:
  //    diskType: the type of disk you want to create. This value uses the following format:
  //        "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
  //        For example: "zones/us-west3-b/diskTypes/pd-ssd"
  //
  //    diskSizeGb: size of the new disk in gigabytes.
  //
  //    boot: boolean flag indicating whether this disk should be used as a
  //    boot disk of an instance.
  //
  //    sourceImage: source image to use when creating this disk.
  //    You must have read access to this disk. This can be one of the publicly available images
  //    or an image from one of your projects.
  //    This value uses the following format: "projects/{project_name}/global/images/{image_name}"
  //
  //    autoDelete: boolean flag indicating whether this disk should be deleted
  //    with the VM that uses it.
  private static AttachedDisk createDiskFromImage(String diskType, int diskSizeGb, boolean boot,
      String sourceImage, boolean autoDelete) {

    AttachedDiskInitializeParams attachedDiskInitializeParams =
        AttachedDiskInitializeParams.newBuilder()
            .setSourceImage(sourceImage)
            .setDiskSizeGb(diskSizeGb)
            .setDiskType(diskType)
            .build();

    AttachedDisk bootDisk = AttachedDisk.newBuilder()
        .setInitializeParams(attachedDiskInitializeParams)
        // Remember to set auto_delete to True if you want the disk to be deleted when you delete
        // your VM instance.
        .setAutoDelete(autoDelete)
        .setBoot(boot)
        .build();

    return bootDisk;
  }

  // Create an AttachedDisk object to be used in VM instance creation. The created disk contains
  // no data and requires formatting before it can be used.
  // Args:
  //    zone: The zone in which the local SSD drive will be attached.
  private static AttachedDisk createLocalSsdDisk(String zone) {

    AttachedDiskInitializeParams attachedDiskInitializeParams =
        AttachedDiskInitializeParams.newBuilder()
            .setDiskType(String.format("zones/%s/diskTypes/local-ssd", zone))
            .build();

    AttachedDisk disk = AttachedDisk.newBuilder()
        .setType(AttachedDisk.Type.SCRATCH.name())
        .setInitializeParams(attachedDiskInitializeParams)
        .setAutoDelete(true)
        .build();

    return disk;
  }

  // Send an instance creation request to the Compute Engine API and wait for it to complete.
  // Args:
  //    projectId: project ID or project number of the Cloud project you want to use.
  //    zone: name of the zone to create the instance in. For example: "us-west3-b"
  //    instanceName: name of the new virtual machine (VM) instance.
  //    disks: a list of compute.v1.AttachedDisk objects describing the disks
  //           you want to attach to your new instance.
  private static Instance createInstance(String projectId, String zone, String instanceName,
      List<AttachedDisk> disks)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the `instancesClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (InstancesClient instancesClient = InstancesClient.create()) {

      // machineType: machine type of the VM being created. This value uses the
      // following format: "zones/{zone}/machineTypes/{type_name}".
      // For example: "zones/europe-west3-c/machineTypes/f1-micro"
      String typeName = "n1-standard-1";
      String machineType = String.format("zones/%s/machineTypes/%s", zone, typeName);

      // networkLink: name of the network you want the new instance to use.
      // For example: "global/networks/default" represents the network
      // named "default", which is created automatically for each project.
      String networkLink = "global/networks/default";

      // Collect information into the Instance object.
      Instance instance = Instance.newBuilder()
          .setName(instanceName)
          .setMachineType(machineType)
          .addNetworkInterfaces(NetworkInterface.newBuilder().setName(networkLink).build())
          .addAllDisks(disks)
          .build();

      Operation response = instancesClient.insertAsync(projectId, zone, instance)
          .get(3, TimeUnit.MINUTES);

      if (response.hasError()) {
        throw new Error("Instance creation failed ! ! " + response);
      }
      System.out.println("Operation Status: " + response.getStatus());
      return instancesClient.get(projectId, zone, instanceName);
    }

  }

}

Python

Python

试用此示例之前,请按照《Compute Engine 快速入门:使用客户端库》中的 Python 设置说明进行操作。 如需了解详情,请参阅 Compute Engine Python API 参考文档

如需向 Compute Engine 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

from __future__ import annotations

import re
import sys
from typing import Any
import warnings

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


def get_image_from_family(project: str, family: str) -> compute_v1.Image:
    """
    Retrieve the newest image that is part of a given family in a project.

    Args:
        project: project ID or project number of the Cloud project you want to get image from.
        family: name of the image family you want to get image from.

    Returns:
        An Image object.
    """
    image_client = compute_v1.ImagesClient()
    # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
    newest_image = image_client.get_from_family(project=project, family=family)
    return newest_image


def disk_from_image(
    disk_type: str,
    disk_size_gb: int,
    boot: bool,
    source_image: str,
    auto_delete: bool = True,
) -> compute_v1.AttachedDisk:
    """
    Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
    source for the new disk.

    Args:
         disk_type: the type of disk you want to create. This value uses the following format:
            "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
            For example: "zones/us-west3-b/diskTypes/pd-ssd"
        disk_size_gb: size of the new disk in gigabytes
        boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
        source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
            of the publicly available images or an image from one of your projects.
            This value uses the following format: "projects/{project_name}/global/images/{image_name}"
        auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it

    Returns:
        AttachedDisk object configured to be created using the specified image.
    """
    boot_disk = compute_v1.AttachedDisk()
    initialize_params = compute_v1.AttachedDiskInitializeParams()
    initialize_params.source_image = source_image
    initialize_params.disk_size_gb = disk_size_gb
    initialize_params.disk_type = disk_type
    boot_disk.initialize_params = initialize_params
    # Remember to set auto_delete to True if you want the disk to be deleted when you delete
    # your VM instance.
    boot_disk.auto_delete = auto_delete
    boot_disk.boot = boot
    return boot_disk


def local_ssd_disk(zone: str) -> compute_v1.AttachedDisk():
    """
    Create an AttachedDisk object to be used in VM instance creation. The created disk contains
    no data and requires formatting before it can be used.

    Args:
        zone: The zone in which the local SSD drive will be attached.

    Returns:
        AttachedDisk object configured as a local SSD disk.
    """
    disk = compute_v1.AttachedDisk()
    disk.type_ = compute_v1.AttachedDisk.Type.SCRATCH.name
    initialize_params = compute_v1.AttachedDiskInitializeParams()
    initialize_params.disk_type = f"zones/{zone}/diskTypes/local-ssd"
    disk.initialize_params = initialize_params
    disk.auto_delete = True
    return disk


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 create_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    disks: list[compute_v1.AttachedDisk],
    machine_type: str = "n1-standard-1",
    network_link: str = "global/networks/default",
    subnetwork_link: str = None,
    internal_ip: str = None,
    external_access: bool = False,
    external_ipv4: str = None,
    accelerators: list[compute_v1.AcceleratorConfig] = None,
    preemptible: bool = False,
    spot: bool = False,
    instance_termination_action: str = "STOP",
    custom_hostname: str = None,
    delete_protection: bool = False,
) -> compute_v1.Instance:
    """
    Send an instance creation request to the Compute Engine API and wait for it to complete.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        disks: a list of compute_v1.AttachedDisk objects describing the disks
            you want to attach to your new instance.
        machine_type: machine type of the VM being created. This value uses the
            following format: "zones/{zone}/machineTypes/{type_name}".
            For example: "zones/europe-west3-c/machineTypes/f1-micro"
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
            "regions/{region}/subnetworks/{subnetwork_name}"
        internal_ip: internal IP address you want to assign to the new instance.
            By default, a free address from the pool of available internal IP addresses of
            used subnet will be used.
        external_access: boolean flag indicating if the instance should have an external IPv4
            address assigned.
        external_ipv4: external IPv4 address to be assigned to this instance. If you specify
            an external IP address, it must live in the same region as the zone of the instance.
            This setting requires `external_access` to be set to True to work.
        accelerators: a list of AcceleratorConfig objects describing the accelerators that will
            be attached to the new instance.
        preemptible: boolean value indicating if the new instance should be preemptible
            or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
        spot: boolean value indicating if the new instance should be a Spot VM or not.
        instance_termination_action: What action should be taken once a Spot VM is terminated.
            Possible values: "STOP", "DELETE"
        custom_hostname: Custom hostname of the new VM instance.
            Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
        delete_protection: boolean value indicating if the new virtual machine should be
            protected against deletion or not.
    Returns:
        Instance object.
    """
    instance_client = compute_v1.InstancesClient()

    # Use the network interface provided in the network_link argument.
    network_interface = compute_v1.NetworkInterface()
    network_interface.network = network_link
    if subnetwork_link:
        network_interface.subnetwork = subnetwork_link

    if internal_ip:
        network_interface.network_i_p = internal_ip

    if external_access:
        access = compute_v1.AccessConfig()
        access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
        access.name = "External NAT"
        access.network_tier = access.NetworkTier.PREMIUM.name
        if external_ipv4:
            access.nat_i_p = external_ipv4
        network_interface.access_configs = [access]

    # Collect information into the Instance object.
    instance = compute_v1.Instance()
    instance.network_interfaces = [network_interface]
    instance.name = instance_name
    instance.disks = disks
    if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
        instance.machine_type = machine_type
    else:
        instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"

    instance.scheduling = compute_v1.Scheduling()
    if accelerators:
        instance.guest_accelerators = accelerators
        instance.scheduling.on_host_maintenance = (
            compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
        )

    if preemptible:
        # Set the preemptible setting
        warnings.warn(
            "Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
        )
        instance.scheduling = compute_v1.Scheduling()
        instance.scheduling.preemptible = True

    if spot:
        # Set the Spot VM setting
        instance.scheduling.provisioning_model = (
            compute_v1.Scheduling.ProvisioningModel.SPOT.name
        )
        instance.scheduling.instance_termination_action = instance_termination_action

    if custom_hostname is not None:
        # Set the custom hostname for the instance
        instance.hostname = custom_hostname

    if delete_protection:
        # Set the delete protection bit
        instance.deletion_protection = True

    # Prepare the request to insert an instance.
    request = compute_v1.InsertInstanceRequest()
    request.zone = zone
    request.project = project_id
    request.instance_resource = instance

    # Wait for the create operation to complete.
    print(f"Creating the {instance_name} instance in {zone}...")

    operation = instance_client.insert(request=request)

    wait_for_extended_operation(operation, "instance creation")

    print(f"Instance {instance_name} created.")
    return instance_client.get(project=project_id, zone=zone, instance=instance_name)


def create_with_ssd(
    project_id: str, zone: str, instance_name: str
) -> compute_v1.Instance:
    """
    Create a new VM instance with Debian 10 operating system and SSD local disk.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.

    Returns:
        Instance object.
    """
    newest_debian = get_image_from_family(project="debian-cloud", family="debian-12")
    disk_type = f"zones/{zone}/diskTypes/pd-standard"
    disks = [
        disk_from_image(disk_type, 10, True, newest_debian.self_link, True),
        local_ssd_disk(zone),
    ]
    instance = create_instance(project_id, zone, instance_name, disks)
    return instance

REST

使用 instances.insert method 根据映像系列或操作系统映像的特定版本创建虚拟机。

  • 对于 Z3、A3 和 A2 Ultra 机器系列,如需创建挂接了本地 SSD 磁盘的虚拟机,请创建一个使用该系列任何可用机器类型的虚拟机。
  • 对于 C3 或 C3D 机器系列,如需创建挂接本地 SSD 磁盘的虚拟机,请指定包含本地 SSD 磁盘 (-lssd) 的实例类型。

    以下示例请求载荷会创建具有 Ubuntu 启动磁盘和两个本地 SSD 磁盘的 C3 虚拟机:

    {
     "machineType":"zones/us-central1-c/machineTypes/c3-standard-8-lssd",
     "name":"c3-with-local-ssd",
     "disks":[
        {
           "type":"PERSISTENT",
           "initializeParams":{
              "sourceImage":"projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts"
           },
           "boot":true
        }
     ],
     "networkInterfaces":[
        {
           "network":"global/networks/default"
    }
     ]
    }
    
  • 对于 M3 以及第一代和第二代机器系列,如需创建挂接了本地 SSD 磁盘的虚拟机,您可以在创建虚拟机期间使用 initializeParams 属性添加本地 SSD 设备。您还必须提供以下属性:

    • diskType:设置为本地 SSD
    • autoDelete:设置为 true
    • type:设置为 SCRATCH

    以下属性不能用于本地 SSD 设备:

    • diskName
    • sourceImage 属性
    • diskSizeGb

    以下示例请求载荷会创建一个具有启动磁盘和四个本地 SSD 磁盘的 M3 虚拟机:

    {
     "machineType":"zones/us-central1-f/machineTypes/m3-ultramem-64",
     "name":"local-ssd-instance",
     "disks":[
        {
         "type":"PERSISTENT",
         "initializeParams":{
            "sourceImage":"projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts"
         },
         "boot":true
        },
        {
           "type":"SCRATCH",
           "initializeParams":{
              "diskType":"zones/us-central1-f/diskTypes/local-ssd"
           },
           "autoDelete":true,
           "interface": "NVME"
        },
        {
           "type":"SCRATCH",
           "initializeParams":{
              "diskType":"zones/us-central1-f/diskTypes/local-ssd"
           },
           "autoDelete":true,
           "interface": "NVME"
        },
        {
           "type":"SCRATCH",
           "initializeParams":{
              "diskType":"zones/us-central1-f/diskTypes/local-ssd"
           },
           "autoDelete":true,
           "interface": "NVME"
        },
        {
           "type":"SCRATCH",
           "initializeParams":{
              "diskType":"zones/us-central1-f/diskTypes/local-ssd"
           },
           "autoDelete":true,
           "interface": "NVME"
        },
     ],
     "networkInterfaces":[
        {
           "network":"global/networks/default"
        }
     ]
    }
    

创建本地 SSD 磁盘后,必须先格式化并装载设备,然后才能使用它。

如需详细了解如何使用 REST 创建实例,请参阅 Compute Engine API

格式化并装载本地 SSD 设备

您可以单独格式化和装载每个本地 SSD 磁盘,也可以将多个本地 SSD 磁盘合并到一个逻辑卷中。

格式化并装载各个本地 SSD 分区

将本地 SSD 连接到实例,最简单的方式是使用单个分区格式化和装载每个设备。或者,您可以将多个分区合并到单个逻辑卷中

Linux 实例

在 Linux 实例上格式化并装载新的本地 SSD。您可以使用任何所需的分区格式和配置。对于本示例,请创建单个 ext4 分区。

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

    转到“虚拟机实例”

  2. 点击附加了新的本地 SSD 的实例旁边的 SSH 按钮。浏览器会建立连到该实例的终端连接。

  3. 在终端中,使用 find 命令标识要装载的本地 SSD。

    $ find /dev/ | grep google-local-nvme-ssd
    

    SCSI 模式下的本地 SSD 具有类似 google-local-ssd-0 的标准名称。NVMe 模式下的本地 SSD 具有与 google-local-nvme-ssd-0 类似的名称,如以下输出所示:

     $ find /dev/ | grep google-local-nvme-ssd
    
     /dev/disk/by-id/google-local-nvme-ssd-0
    
  4. 使用 ext4 文件系统格式化本地 SSD。此命令将删除本地 SSD 中的所有现有数据。

    $ sudo mkfs.ext4 -F /dev/disk/by-id/[SSD_NAME]
    

    [SSD_NAME] 替换为您要设置格式的本地 SSD 的 ID。例如,指定 google-local-nvme-ssd-0 以格式化实例上的第一个 NVMe 本地 SSD。

  5. 使用 mkdir 命令创建用于装载设备的目录。

    $ sudo mkdir -p /mnt/disks/[MNT_DIR]
    

    [MNT_DIR] 替换为要装载本地 SSD 磁盘的目录路径。

  6. 将本地 SSD 装载到虚拟机。

    $ sudo mount /dev/disk/by-id/[SSD_NAME] /mnt/disks/[MNT_DIR]
    

    替换以下内容:

    • [SSD_NAME]:要装载的本地 SSD 的 ID。
    • [MNT_DIR]:要装载本地 SSD 的目录。
  7. 配置对该设备的读写权限。对于本示例,为所有用户授予对设备的写入访问权限。

    $ sudo chmod a+w /mnt/disks/[MNT_DIR]
    

    [MNT_DIR] 替换为装载本地 SSD 的目录。

或者,您可以将本地 SSD 添加到 /etc/fstab 文件,以便在实例重启时设备会自动重新装载。如果实例停止,此条目不会起到保留本地 SSD 上的数据的作用。如需了解完整详情,请参阅本地 SSD 数据持久性

指定 /etc/fstab 条目文件时,请务必添加 nofail 选项,这样,即使本地 SSD 不存在,实例也能继续启动。例如,如果您截取了启动磁盘的快照,并创建了一个没有挂接任何本地 SSD 磁盘的新实例,那么该实例可以继续完成启动过程,而不会无限期地暂停。

  1. 创建 /etc/fstab 条目。使用 blkid 命令查找该设备上文件系统的 UUID,并修改 /etc/fstab 文件以将该 UUID 添加到装载选项中。您可以使用单个命令完成此步骤。

    例如,对于 NVMe 模式的本地 SSD,请使用以下命令:

    $ echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-local-nvme-ssd-0` /mnt/disks/[MNT_DIR] ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
    

    对于非 NVMe 模式的本地 SSD(例如 SCSI),请使用以下命令:

    $ echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-local-ssd-0` /mnt/disks/[MNT_DIR] ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
    

    [MNT_DIR] 替换为装载本地 SSD 的目录。

  2. 使用 cat 命令验证您的 /etc/fstab 条目内容正确:

    $ cat /etc/fstab
    

如果从此实例的启动磁盘创建快照并使用它创建没有本地 SSD 的单独实例,请修改 /etc/fstab 文件并移除此本地 SSD 的条目。即使配置了 nofail 选项,您也应该使 /etc/fstab 文件与挂接到该实例的分区保持同步,并在创建启动磁盘快照之前移除这些条目。

Windows 实例

使用 Windows 磁盘管理工具在 Windows 实例上格式化和装载本地 SSD。

  1. 通过 RDP 连接到实例。对于本示例,前往“虚拟机实例”页面,点击挂接本地 SSD 的实例旁边的 RDP 按钮。输入用户名和密码后,将打开一个窗口,其中包含服务器的桌面界面。

  2. 右键点击 Windows“开始”按钮并选择磁盘管理

    从 Windows“开始”按钮的右键菜单中选择 Windows“磁盘管理器”工具。

  3. 如果您之前尚未初始化本地 SSD,则该工具会提示您为新分区选择分区方案。选择 GPT,然后点击确定

    在磁盘初始化窗口中选择分区方案。

  4. 本地 SSD 初始化后,右键点击未分配的磁盘可用空间,然后选择新建简单卷

    从所挂接的磁盘创建新的简单卷。

  5. 按照新建简单卷向导中的说明配置新卷。您可以使用任何想要的分区格式,但对于本示例,请选择 NTFS。另外,请勾选执行快速格式化以加快格式化过程。

    在“新建简单卷”向导中选择分区格式类型。

  6. 完成向导步骤并且完成卷格式化后,请检查新的本地 SSD 以确保其处于 Healthy 状态。

    查看 Windows 可识别的磁盘列表,验证本地 SSD 是否处于良好状态。

大功告成!现在可以将文件写入本地 SSD 了。

将多个本地 SSD 分区格式化并装载到单个逻辑卷中

不同于持久性 SSD,附加到实例的每个本地 SSD 设备具有固定的 375 GB 容量。如果要将多个本地 SSD 分区合并到单个逻辑卷中,则必须自行定义跨这些分区的卷管理。

Linux 实例

使用 mdadm 创建 RAID 0 阵列。此示例使用单个 ext4 文件系统格式化阵列,但您可以应用您想要的任何文件系统。

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

    转到“虚拟机实例”

  2. 点击附加了新的本地 SSD 的实例旁边的 SSH 按钮。浏览器会建立连到该实例的终端连接。

  3. 在终端中安装 mdadm 工具。mdadm 的安装过程包括暂停脚本的用户提示,因此请手动运行此过程。

    Debian 和 Ubuntu:

    $ sudo apt update && sudo apt install mdadm --no-install-recommends
    

    CentOS 和 RHEL:

    $ sudo yum install mdadm -y
    

    SLES 和 openSUSE:

    $ sudo zypper install -y mdadm
    

  4. 使用 find 命令标识要一起装载的所有本地 SSD。

    在本示例中,实例装载了 8 个 NVMe 模式的本地 SSD 分区:

    $  find /dev/ | grep google-local-nvme-ssd
    
     /dev/disk/by-id/google-local-nvme-ssd-7
     /dev/disk/by-id/google-local-nvme-ssd-6
     /dev/disk/by-id/google-local-nvme-ssd-5
     /dev/disk/by-id/google-local-nvme-ssd-4
     /dev/disk/by-id/google-local-nvme-ssd-3
     /dev/disk/by-id/google-local-nvme-ssd-2
     /dev/disk/by-id/google-local-nvme-ssd-1
     /dev/disk/by-id/google-local-nvme-ssd-0
    

    find 不保证顺序。只要输出行数与预期的 SSD 分区数量一致,设备按不同的顺序列出也可以。SCSI 模式下的本地 SSD 具有类似 google-local-ssd 的标准名称。NVMe 模式下的本地 SSD 具有类型 google-local-nvme-ssd 的名称。

  5. 使用 mdadm 将多个本地 SSD 设备合并到一个名为 /dev/md0 的阵列中。本示例合并 NVMe 模式下的 8 个本地 SSD 设备。对于 SCSI 模式下的本地 SSD 设备,请指定从 find 命令获取的名称:

    $ sudo mdadm --create /dev/md0 --level=0 --raid-devices=8 \
     /dev/disk/by-id/google-local-nvme-ssd-0 \
     /dev/disk/by-id/google-local-nvme-ssd-1 \
     /dev/disk/by-id/google-local-nvme-ssd-2 \
     /dev/disk/by-id/google-local-nvme-ssd-3 \
     /dev/disk/by-id/google-local-nvme-ssd-4 \
     /dev/disk/by-id/google-local-nvme-ssd-5 \
     /dev/disk/by-id/google-local-nvme-ssd-6 \
     /dev/disk/by-id/google-local-nvme-ssd-7
    
    mdadm: Defaulting to version 1.2 metadata
    mdadm: array /dev/md0 started.
    
    

    您可以使用 mdadm --detail 确认阵列的详细信息。通过添加 --prefer=by-id 标志,系统将使用 /dev/disk/by-id 路径列出设备。

     sudo mdadm --detail --prefer=by-id /dev/md0
     

    对于数组中的每个设备,输出应类似如下所示。

     ...
     Number   Major   Minor   RaidDevice State
        0      259      0         0      active sync   /dev/disk/by-id/google-local-nvme-ssd-0
     ...
     

  6. 使用 ext4 文件系统格式化完整的 /dev/md0 阵列。

    $ sudo mkfs.ext4 -F /dev/md0
    
  7. 创建一个用于装载 /dev/md0 的目录。对于本示例,请创建 /mnt/disks/ssd-array 目录:

    $ sudo mkdir -p /mnt/disks/[MNT_DIR]
    

    [MNT_DIR] 替换为要装载本地 SSD 阵列的目录。

  8. /dev/md0 阵列装载到 /mnt/disks/ssd-array 目录:

    $ sudo mount /dev/md0 /mnt/disks/[MNT_DIR]
    

    [MNT_DIR] 替换为要装载本地 SSD 阵列的目录。

  9. 配置对该设备的读写权限。对于本示例,为所有用户授予对设备的写入访问权限。

    $ sudo chmod a+w /mnt/disks/[MNT_DIR]
    

    [MNT_DIR] 替换为装载本地 SSD 阵列的目录。

或者,您可以将本地 SSD 添加到 /etc/fstab 文件,以便在实例重启时设备会自动重新装载。如果实例停止,此条目不会起到保留本地 SSD 上的数据的作用。如需了解详情,请参阅本地 SSD 数据持久性

指定 /etc/fstab 条目文件时,请务必添加 nofail 选项,这样,即使本地 SSD 不存在,实例也能继续启动。例如,如果您截取启动磁盘的快照并创建没有附加任何本地 SSD 的新实例,则该实例可以继续启动流程,而不是无限期地暂停。

  1. 创建 /etc/fstab 条目。使用 blkid 命令查找该设备上文件系统的 UUID,并修改 /etc/fstab 文件以将该 UUID 添加到装载选项中。指定 nofail 选项,以使系统在本地 SSD 不可用的情况下也能启动。您可以使用单个命令完成此步骤。例如:

    $ echo UUID=`sudo blkid -s UUID -o value /dev/md0` /mnt/disks/[MNT_DIR] ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
    

    [MNT_DIR] 替换为装载本地 SSD 阵列的目录。

  2. 如果您在 /etc/fstab 文件中使用类似于 /dev/md0 的设备名称,而不是 UUID,则需要修改文件 /etc/mdadm/mdadm.conf,确保阵列在启动时会自动重新组合。为此,请完成以下两个步骤:

    1. 确保磁盘阵列在启动时自动扫描并重新组合。
      $ sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
      
    2. 更新 initramfs,以使该阵列在前期启动过程中可用。
      $ sudo update-initramfs -u
      
  3. 使用 cat 命令验证您的 /etc/fstab 条目内容正确:

    $ cat /etc/fstab
    

如果从此实例的启动磁盘创建快照并使用它创建没有本地 SSD 的单独实例,请修改 /etc/fstab 文件并移除此本地 SSD 阵列的条目。即使配置了 nofail 选项,您也应该使 /etc/fstab 文件与挂接到该实例的分区保持同步,并在创建启动磁盘快照之前移除这些条目。

Windows 实例

使用 Windows 磁盘管理工具在 Windows 实例上格式化和装载本地 SSD 阵列。

  1. 通过 RDP 连接到实例。对于本示例,前往“虚拟机实例”页面,点击挂接本地 SSD 的实例旁边的 RDP 按钮。输入用户名和密码后,将打开一个窗口,其中包含服务器的桌面界面。

  2. 右键点击 Windows“开始”按钮并选择磁盘管理

    从 Windows“开始”按钮的右键菜单中选择 Windows“磁盘管理器”工具。

  3. 如果您之前尚未初始化本地 SSD,则该工具会提示您为新分区选择分区方案。选择 GPT,然后点击确定

    在磁盘初始化窗口中选择分区方案。

  4. 本地 SSD 初始化后,右键点击未分配的磁盘可用空间,然后选择新建带区卷

    从所挂接的磁盘新建带区卷。

  5. 选择要加入带区阵列中的本地 SSD 分区。在本示例中,请选择所有分区,然后将它们合并到单个本地 SSD 设备中。

    选择要加入阵列中的本地 SSD 分区。

  6. 按照新建带区卷向导中的说明配置新卷。您可以使用任何想要的分区格式,但对于本示例,请选择 NTFS。另外,请勾选执行快速格式化以加快格式化过程。

    在“新建带区卷向导”中选择分区格式类型。

  7. 完成向导步骤并且完成卷格式化后,请检查新的本地 SSD 以确保其处于 Healthy 状态。

    查看 Windows 可识别的磁盘列表,验证本地 SSD 是否处于良好状态。

现在可以将文件写入本地 SSD 了。

后续步骤