在虚拟机之间共享 Persistent Disk 卷


您可以将多写入者模式的 SSD Persistent Disk 卷同时挂接到最多两个 N2 虚拟机实例,使两个虚拟机均可读写该磁盘。如需为新的 Persistent Disk 卷启用多写入者模式,请创建新的 Persistent Disk 卷,并在 gcloud CLI 中指定 --multi-writer 标志或在 Compute Engine API 中指定 multiWriter 属性。

采用多写入者模式的 Persistent Disk 卷提供共享块存储功能,并为构建分布式存储系统和类似高可用性服务提供了基础架构基础。在使用多写入者模式的 Persistent Disk 卷时,请使用能够跨多个虚拟机协调对 Persistent Disk 设备的访问的横向扩容存储软件系统。这些存储系统的示例包括 Lustre 和 IBM Spectrum Scale。大多数单虚拟机文件系统(如 EXT4、XFS 和 NTFS)都不支持使用共享块存储。如需了解详情,请参阅本文档中的最佳实践。如果您需要全托管式文件存储,可以在 Compute Engine 虚拟机上装载 Filestore 文件共享

多写入者模式下的 Persistent Disk 卷支持部分 SCSI-3 永久性预留 (SCSI PR) 命令。高可用性应用可以将这些命令用于 I/O 防护和故障切换配置。

支持以下 SCSI PR 命令:

  • IN {REPORT CAPABILITIES, READ FULL STATUS, READ RESERVATION, READ KEYS}
  • OUT {REGISTER, REGISTER AND IGNORE EXISTING KEY, RESERVE, PREEMPT, CLEAR, RELEASE}

准备工作

  • 设置身份验证(如果尚未设置)。身份验证是通过其进行身份验证以访问 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. 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 类型 Persistent Disk 卷
  • 您可以在任何可用区创建多写入者模式的 Persistent Disk 卷,但只能将该磁盘挂接到以下位置中的虚拟机:
    • australia-southeast1
    • europe-west1
    • us-central1(仅限 us-central1-aus-central1-c 可用区)
    • us-east1(仅限 us-east1-d 可用区)
    • us-west1(仅限 us-west1-bus-west1-c 可用区)
  • 挂接的虚拟机必须具有 N2 机器类型
  • 大小下限:10 GB
  • 挂接的虚拟机数量上限:2
  • 多写入者模式 Persistent Disk 卷不支持 Persistent Disk 指标
  • 多写入者模式中的磁盘不能更改为只读模式。
  • 您无法使用磁盘映像或快照创建多写入者模式的 Persistent Disk 卷。
  • 您无法通过多写入者模式的 Persistent Disk 卷创建快照或映像。
  • 降低 IOPS 限制。如需了解详情,请参阅磁盘性能
  • 您无法调整多写入者 Persistent Disk 卷的大小。
  • 使用 Google Cloud CLI 创建虚拟机时,您无法使用 --create-disk 标志创建多写入者 Persistent Disk 卷。

最佳实践

  • 使用 SCSI PR 命令实现的 I/O 防护会使 Persistent Disk 数据具有崩溃一致状态。某些文件系统不具有崩溃一致性,因此如果使用 SCSI PR 命令,这些文件系统可能会损坏。
  • 许多文件系统(如 EXT4、XFS 和 NTFS)并非旨在与共享块存储一起使用,并且没有同步或执行源自多个虚拟机实例的操作的机制。
  • 在使用多写入者模式的 Persistent Disk 卷之前,请务必了解您的文件系统以及如何将该文件系统安全地用于共享块存储以及同时来自多个虚拟机的访问。

性能

在多写入者模式下创建的 Persistent Disk 卷具有特定的 IOPS 和吞吐量限制。

可用区级 SSD 永久性磁盘多写入者模式
最大持续 IOPS
读取 IOPS/GB 30
写入 IOPS/GB 30
读取 IOPS/实例 15000–100000*
写入 IOPS/实例 15000–100000*
最大持续吞吐量 (MB/s)
读取吞吐量/GB 0.48
写入吞吐量/GB 0.48
读取吞吐量/实例 240–1200*
写入吞吐量/实例 240–1200*
* 永久性磁盘 IOPS 和吞吐量性能取决于磁盘大小、实例的 vCPU 数量和 I/O 块大小等诸多因素
将多写入者磁盘挂接到多个虚拟机实例不会影响总体性能或费用。按磁盘的性能限制会分摊到每个虚拟机。

如需了解如何在多个虚拟机之间共享永久性磁盘,请参阅在虚拟机之间共享永久性磁盘

在虚拟机实例之间共享一个可用区级 Persistent Disk 卷

本部分介绍在多个虚拟机之间共享可用区级 Persistent Disk 卷的不同方法。

在多个虚拟机之间共享一个只读模式的磁盘

您可以在只读模式下将非启动 Persistent Disk 卷挂接到多个虚拟机,如此便可在多个虚拟机之间共享静态数据。在多个虚拟机之间通过一个 Persistent Disk 卷共享静态数据要比将数据复制到各个虚拟机的唯一磁盘上要便宜。

如果您需要在多个虚拟机之间共享动态存储空间,则可以使用以下任一选项:

控制台

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

    转到虚拟机实例

  2. 在项目的虚拟机列表中,点击要挂接磁盘的虚拟机的名称。虚拟机实例详情页面随即打开。

  3. 在“实例详情”页面上,点击修改

  4. 额外磁盘部分,点击以下任一选项:

    1. 添加磁盘,以将设为只读模式的磁盘添加到虚拟机。
    2. 挂接现有磁盘,以选择现有磁盘,并将其以只读模式挂接到您的虚拟机。
  5. 为您的磁盘指定其他选项。

  6. 点击完成以应用更改。

  7. 点击保存以将更改应用于虚拟机。

  8. 连接到虚拟机并装载磁盘。

  9. 重复此过程,以在只读模式下将磁盘添加到其他虚拟机。

gcloud

在 gcloud CLI 中,使用 compute instances attach-disk 命令并指定选项为 ro--mode 标志。

gcloud compute instances attach-disk INSTANCE_NAME \
  --disk DISK_NAME \
  --mode ro

替换以下内容:

  • INSTANCE_NAME:要挂接可用区级 Persistent Disk 卷的虚拟机的名称
  • DISK_NAME:您要挂接的磁盘的名称

挂接磁盘后,连接到虚拟机并装载磁盘。

对您要以只读模式添加此磁盘的每个虚拟机重复此命令。

Java

Java

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

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


import com.google.cloud.compute.v1.AttachDiskInstanceRequest;
import com.google.cloud.compute.v1.AttachedDisk;
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 AttachDisk {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Cloud project you want to use.
    String projectId = "your-project-id";

    // Name of the zone in which the instance you want to use resides.
    String zone = "zone-name";

    // Name of the compute instance you want to attach a disk to.
    String instanceName = "instance-name";

    // Full or partial URL of a persistent disk that you want to attach. This can be either
    // be a regional or zonal disk.
    // Valid formats:
    //     * https://www.googleapis.com/compute/v1/projects/{project}/zones/{zone}/disks/{disk_name}
    //     * /projects/{project}/zones/{zone}/disks/{disk_name}
    //     * /projects/{project}/regions/{region}/disks/{disk_name}
    String diskLink = String.format("/projects/%s/zones/%s/disks/%s",
        "project", "zone", "disk_name");

    // Specifies in what mode the disk will be attached to the instance. Available options are
    // `READ_ONLY` and `READ_WRITE`. Disk in `READ_ONLY` mode can be attached to
    // multiple instances at once.
    String mode = "READ_ONLY";

    attachDisk(projectId, zone, instanceName, diskLink, mode);
  }

  // Attaches a non-boot persistent disk to a specified compute instance.
  // The disk might be zonal or regional.
  // You need following permissions to execute this action:
  // https://cloud.google.com/compute/docs/disks/regional-persistent-disk#expandable-1
  public static void attachDisk(String projectId, String zone, String instanceName, String diskLink,
      String mode)
      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()) {

      AttachDiskInstanceRequest attachDiskInstanceRequest = AttachDiskInstanceRequest.newBuilder()
          .setProject(projectId)
          .setZone(zone)
          .setInstance(instanceName)
          .setAttachedDiskResource(AttachedDisk.newBuilder()
              .setSource(diskLink)
              .setMode(mode)
              .build())
          .build();

      Operation response = instancesClient.attachDiskAsync(attachDiskInstanceRequest)
          .get(3, TimeUnit.MINUTES);

      if (response.hasError()) {
        System.out.println("Attach disk failed! " + response);
        return;
      }
      System.out.println("Attach disk - operation status: " + response.getStatus());
    }
  }
}

Python

Python

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

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

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 attach_disk(
    project_id: str, zone: str, instance_name: str, disk_link: str, mode: str
) -> None:
    """
    Attaches a non-boot persistent disk to a specified compute instance. The disk might be zonal or regional.

    You need following permissions to execute this action:
    https://cloud.google.com/compute/docs/disks/regional-persistent-disk#expandable-1

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone:name of the zone in which the instance you want to use resides.
        instance_name: name of the compute instance you want to attach a disk to.
        disk_link: full or partial URL to a persistent disk that you want to attach. This can be either
            regional or zonal disk.
            Expected formats:
                * https://www.googleapis.com/compute/v1/projects/[project]/zones/[zone]/disks/[disk_name]
                * /projects/[project]/zones/[zone]/disks/[disk_name]
                * /projects/[project]/regions/[region]/disks/[disk_name]
        mode: Specifies in what mode the disk will be attached to the instance. Available options are `READ_ONLY`
            and `READ_WRITE`. Disk in `READ_ONLY` mode can be attached to multiple instances at once.
    """
    instances_client = compute_v1.InstancesClient()

    request = compute_v1.AttachDiskInstanceRequest()
    request.project = project_id
    request.zone = zone
    request.instance = instance_name
    request.attached_disk_resource = compute_v1.AttachedDisk()
    request.attached_disk_resource.source = disk_link
    request.attached_disk_resource.mode = mode

    operation = instances_client.attach_disk(request)

    wait_for_extended_operation(operation, "disk attachement")

REST

在 API 中,构建对 compute.instances.attachDisk 方法的 POST 请求。在请求正文中,将 mode 参数指定为 READ_ONLY

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME/attachDisk

{
 "source": "zones/ZONE/disks/DISK_NAME",
 "mode": "READ_ONLY"
}

替换以下内容:

  • INSTANCE_NAME:要挂接可用区级 Persistent Disk 卷的虚拟机的名称
  • PROJECT_ID:您的项目 ID
  • ZONE:磁盘所在的地区
  • DISK_NAME:您要挂接的磁盘的名称

挂接磁盘后,连接到虚拟机并装载磁盘。

对您要以只读模式添加此磁盘的每个虚拟机重复此请求。

在虚拟机之间共享一个多写入者模式的 SSD Persistent Disk 卷

您可以在同一可用区的多个 N2 虚拟机之间共享一个多写入者模式的 SSD Persistent Disk 卷。如需详细了解此模式的工作原理,请参阅 Persistent Disk 多写入者模式。您可以按照以下过程创建并挂接多写入者 Persistent Disk 卷:

gcloud

使用 gcloud CLI 创建和挂接可用区级 Persistent Disk 卷:

  1. 使用 gcloud beta compute disks create 命令创建可用区级 Persistent Disk 卷。添加 --multi-writer 标志,以指示该磁盘在多写入者模式下必须可以在虚拟机之间共享。

    gcloud beta compute disks create DISK_NAME \
       --size DISK_SIZE \
       --type pd-ssd \
       --multi-writer
    

    替换以下内容:

    • DISK_NAME:新磁盘的名称
    • DISK_SIZE:新磁盘的大小(以 GB 为单位)。可接受的大小范围介于 1 GB 到 65536 GB 之间(对于 SSD Persistent Disk 卷),或介于 200 GB 到 65536 GB 之间(对于多写入者模式的标准 Persistent Disk 卷)。
  2. 创建磁盘后,将其挂接到 N2 机器类型的任何正在运行或已停止的虚拟机。使用 gcloud compute instances attach-disk 命令

    gcloud compute instances attach-disk INSTANCE_NAME \
       --disk DISK_NAME
    

    替换以下内容:

    • INSTANCE_NAME:要添加新的可用区级 Persistent Disk 卷的 N2 虚拟机的名称
    • DISK_NAME:要挂接到虚拟机的新磁盘的名称
  3. 重复执行 gcloud compute instances attach-disk 命令,但将 INSTANCE_NAME 替换为第二个虚拟机的名称。

创建新磁盘并将其挂接到虚拟机后,使用共享磁盘文件系统格式化和装载磁盘。大多数文件系统无法使用共享的存储空间。在将您的文件系统与多写入者 Persistent Disk 卷搭配使用之前,请确认您的文件系统支持这些功能。您无法通过将磁盘装载到单个虚拟机的同一进程将磁盘装载到多个虚拟机。

REST

使用 Compute Engine API 在多写入者模式下创建 SS Persistent Disk 卷并将其挂接到 N2 虚拟机。

  1. 在 API 中,构建一个 POST 请求以使用 disks.insert 方法创建可用区级 Persistent Disk 卷。请添加 namesizeGbtype 属性。如需新建一个空白且未格式化的非启动磁盘,请不要为此磁盘指定来源映像或来源快照。添加 multiWriter 属性(值为 True)以指示该磁盘在多写入者模式下必须可在虚拟机之间共享。

    POST https://compute.googleapis.com/compute/beta/projects/PROJECT_ID/zones/ZONE/disks
    
    {
    "name": "DISK_NAME",
    "sizeGb": "DISK_SIZE",
    "type": "zones/ZONE/diskTypes/pd-ssd",
    "multiWriter": "True"
    }
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • ZONE:您的虚拟机和新磁盘所在的可用区
    • DISK_NAME:新磁盘的名称
    • DISK_SIZE:新磁盘的大小(以 GB 为单位)。可接受的大小范围介于 1 GB 到 65536 GB 之间(对于 SSD Persistent Disk 卷),或介于 200 GB 到 65536 GB 之间(对于多写入者模式的标准 Persistent Disk 卷)。
  2. 构建一个向 compute.instances.attachDisk 方法发出的 POST 请求,并在其中添加您刚创建的可用区级 Persistent Disk 卷的网址。

    POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME/attachDisk
    
    {
    "source": "/compute/v1/projects/PROJECT_ID/zones/ZONE/disks/DISK_NAME"
    }
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • ZONE:您的虚拟机和新磁盘所在的可用区
    • INSTANCE_NAME:要添加新的 Persistent Disk 卷的虚拟机的名称
    • DISK_NAME:新磁盘的名称
  3. 重复执行 disks.insert 命令,但指定第二个虚拟机。

创建新磁盘并将其挂接到虚拟机后,使用共享磁盘文件系统格式化和装载磁盘。大多数文件系统无法使用共享的存储空间。在将您的文件系统与多写入者 Persistent Disk 卷搭配使用之前,请确认您的文件系统支持这些功能。

后续步骤