클론으로 디스크 복제


이 문서에서는 디스크 클론 작동 방식과 디스크 클론을 만드는 방법을 설명합니다. 디스크 클론을 사용하면 기존 디스크의 즉시 사용 가능한 복제를 만들 수 있습니다. 다음과 같이 VM에 즉시 연결할 수 있는 기존 디스크의 동일한 사본을 만들려는 시나리오에서 디스크 클론을 만듭니다.

  • 프로덕션을 중단하지 않고 디버깅하기 위해 프로덕션 데이터를 복제하여 스테이징 환경 만들기
  • 데이터베이스 백업 확인용 사본 만들기
  • 비부팅 디스크 데이터를 새 프로젝트로 이동
  • VM 수평 확장 시 디스크 복제

재해 복구로부터 보호하려면 디스크 클론을 사용하는 대신 표준 스냅샷을 사용하여 디스크를 백업합니다. 새 디스크를 만들지 않고 정기적으로 디스크 콘텐츠를 캡처하려면 클론보다 스토리지 효율이 더 높은 인스턴트 스냅샷을 사용합니다. 디스크 보호를 강화하는 옵션은 데이터 보호 옵션을 참조하세요.

시작하기 전에

  • 아직 인증을 설정하지 않았다면 설정합니다. 인증은 Google Cloud 서비스 및 API에 액세스하기 위해 ID를 확인하는 프로세스입니다. 로컬 개발 환경에서 코드 또는 샘플을 실행하려면 다음과 같이 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 사용 인증을 참조하세요.

디스크 클론 작동 방식

디스크를 클론할 때는 소스 디스크의 모든 데이터가 포함된 새 디스크를 만듭니다. 기존 디스크가 VM 인스턴스에 연결된 경우에도 디스크 클론을 만들 수 있습니다.

클론의 디스크 유형은 소스 디스크 유형과 같아야 합니다. 그러나 클론에서 디스크 크기와 같은 속성을 수정할 수 있습니다. 클론을 삭제할 위험 없이 소스 디스크를 삭제할 수도 있습니다.

지원되는 디스크 유형

다음 디스크 유형에만 디스크 클론을 만들 수 있습니다.

  • Persistent Disk: 모든 유형의 Persistent Disk
  • Google Cloud Hyperdisk: Hyperdisk Balanced, Hyperdisk Extreme, Hyperdisk Throughput

제한사항

디스크 유형에 따라 디스크 클론에 다음과 같은 제한사항이 적용됩니다.

일반 제한사항

모든 디스크 유형의 클론에는 다음 제한사항이 적용됩니다.

  • 클론의 디스크 유형은 소스 디스크와 동일해야 합니다.
  • 다른 영역에 있는 기존 영역 디스크의 영역 디스크 클론을 만들 수 없습니다.
  • 클론의 크기는 최소한 소스 디스크의 크기여야 합니다. Google Cloud 콘솔을 사용하여 클론을 만드는 경우 디스크 크기를 지정할 수 없으며 클론 크기는 소스 디스크 크기와 동일합니다.
  • 고객 제공 암호화 키 또는 고객 관리 암호화 키를 사용하여 소스 디스크를 암호화하는 경우 동일한 키를 사용하여 클론을 암호화해야 합니다. 자세한 내용은 암호화된 소스 디스크의 클론 만들기를 참조하세요.
  • 클론이 생성되는 동안에는 소스 디스크를 삭제할 수 없습니다.
  • 클론이 생성되는 동안에는 소스 디스크가 연결된 컴퓨팅 인스턴스의 전원을 켤 수 없습니다.
  • 소스 디스크가 연결된 VM과 함께 삭제되도록 표시된 경우 클론이 생성되는 동안에는 VM을 삭제할 수 없습니다.
  • 지정된 소스 디스크 또는 해당 클론의 클론을 30초마다 최대 한 개씩 만들 수 있습니다.
  • 지정된 소스 디스크 또는 해당 클론의 동시 디스크 클론은 최대 1,000개까지 가능합니다. 이 한도를 초과하면 internalError가 반환됩니다. 하지만 디스크 클론을 만들고 나중에 삭제하는 경우 이 한도에는 삭제된 디스크 클론이 포함되지 않습니다.
  • 디스크가 클론되면 해당 디스크 또는 클론의 후속 클론은 원본 소스 디스크의 동시 디스크 클론 1,000개 한도에 계산되며 30초마다 클론 최대 1개 생성 한도에 계산됩니다.
  • 디스크를 클론할 때 사용할 수 있는 공간은 제한됩니다. 15분마다 클론된 디스크 최대 1TB를 만들 수 있으며 버스트 요청 한도는 257TB입니다.

Persistent Disk 클론 제한사항

Persistent Disk의 디스크 클론에는 다음과 같은 제한사항이 적용됩니다.

  • 리전 디스크에서 영역 디스크 클론을 만들 수 없습니다.
  • 영역 소스 디스크에서 리전 디스크 클론을 만들려면 리전 디스크 클론의 복제본 영역 중 하나가 소스 디스크 영역과 일치해야 합니다.
  • 평균적으로 생성되고 3분 이내에 리전 디스크 클론을 사용할 수 있습니다. 하지만 디스크가 완전히 복제되고 복구 지점 목표(RPO)가 거의 0이 될 때까지는 수십 분이 걸릴 수 있습니다.

Google Cloud Hyperdisk 클론 제한사항

Hyperdisk Balanced, Hyperdisk Extreme, Hyperdisk Throughput 볼륨에만 디스크 클론을 만들 수 있습니다.

오류 메시지

클론 빈도 한도를 초과하면 다음 오류가 발생하여 요청이 실패합니다.

RATE LIMIT: ERROR: (gcloud.compute.disks.create) Could not fetch resource:
 - Operation rate exceeded for resource RESOURCE. Too frequent operations from the source resource.

디스크 클론 만들기

이 섹션에서는 기존 디스크를 복제하여 디스크 클론을 만드는 방법을 설명합니다.

자세한 단계는 디스크 클론 만들기 유형에 따라 이 문서의 다음 섹션 중 하나를 참조하세요.

영역 디스크 클론 만들기

Google Cloud 콘솔, Google Cloud CLI 또는 REST를 사용하여 소스 디스크와 동일한 영역에 있는 기존 디스크의 영역 디스크 클론을 만들 수 있습니다.

콘솔

  1. Google Cloud Console에서 디스크 페이지로 이동합니다.

    디스크로 이동

  2. 디스크 목록에서 클론할 디스크로 이동합니다.

  3. 작업 열에서 메뉴 버튼을 클릭하고 디스크 클론을 선택합니다.

    클론 만들기

    표시된 디스크 클론 패널에서 다음을 수행합니다.

    1. 이름 필드에 클론된 디스크의 이름을 지정합니다.
    2. 위치에서 단일 영역이 선택되었는지 확인합니다.
    3. 속성에서 클론된 디스크의 기타 세부정보를 검토합니다.
    4. 클론된 디스크 만들기를 마치려면 만들기를 클릭합니다.

gcloud

영역 소스 디스크를 클론하고 새 영역 디스크를 만들려면 disks create 명령어를 실행하고 --source-disk 플래그를 지정합니다.

gcloud compute disks create TARGET_DISK_NAME \
    --description="cloned disk" \
    --source-disk=projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME

다음을 바꿉니다.

  • TARGET_DISK_NAME: 새 디스크 이름
  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • SOURCE_DISK_NAME: 소스 디스크의 이름입니다.

Terraform

디스크 클론을 만들려면 google_compute_disk 리소스를 사용합니다.

resource "google_compute_disk" "default" {
  name  = "disk-name1"
  type  = "pd-ssd"
  zone  = "us-central1-a"
  image = "debian-11-bullseye-v20220719"
  labels = {
    environment = "dev"
  }
  physical_block_size_bytes = 4096
}

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"
)

// createDiskFromDisk creates a new disk with the contents of
// an already existitng disk. Type, and size and zone may differ.
func createDiskFromDisk(
	w io.Writer,
	projectID, zone, diskName, diskType, sourceDiskLink string,
	diskSizeGb int64,
) error {
	// projectID := "your_project_id"
	// zone := "us-west3-b" // should match diskType below
	// diskName := "your_disk_name"
	// diskType := "zones/us-west3-b/diskTypes/pd-ssd"
	// sourceDiskLink := "projects/your_project_id/global/disks/disk_name"
	// diskSizeGb := 120

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

	req := &computepb.InsertDiskRequest{
		Project: projectID,
		Zone:    zone,
		DiskResource: &computepb.Disk{
			Name:       proto.String(diskName),
			Zone:       proto.String(zone),
			Type:       proto.String(diskType),
			SourceDisk: proto.String(sourceDiskLink),
			SizeGb:     proto.Int64(diskSizeGb),
		},
	}

	op, err := disksClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create disk: %w", err)
	}

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

	fmt.Fprintf(w, "Disk created\n")

	return nil
}

자바

Java

이 샘플을 사용해 보기 전에 Compute Engine 빠른 시작: 클라이언트 라이브러리 사용Java 설정 안내를 따르세요. 자세한 내용은 Compute Engine Java API 참고 문서를 확인하세요.

Compute Engine에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


import com.google.cloud.compute.v1.Disk;
import com.google.cloud.compute.v1.DisksClient;
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 CreateFromSource {

  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 project = "YOUR_PROJECT_ID";

    // Name of the zone in which you want to create the disk.
    String zone = "europe-central2-b";

    // Name of the disk you want to create.
    String diskName = "YOUR_DISK_NAME";

    // 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"
    String diskType = String.format("zones/%s/diskTypes/pd-ssd", zone);

    // Size of the new disk in gigabytes.
    int diskSizeGb = 10;

    // A link to the disk you want to use as a source for the new disk.
    // This value uses the following format:
    // "projects/{project_name}/zones/{zone}/disks/{disk_name}"
    String diskLink = String.format("projects/%s/zones/%s/disks/%s", "PROJECT_NAME", "ZONE",
        "DISK_NAME");

    createDiskFromDisk(project, zone, diskName, diskType, diskSizeGb, diskLink);
  }

  // Creates a disk in a project in a given zone.
  public static void createDiskFromDisk(String project, String zone, String diskName,
      String diskType, int diskSizeGb, String diskLink)
      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 `disksClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (DisksClient disksClient = DisksClient.create()) {

      // Create the disk.
      Disk disk = Disk.newBuilder()
          .setZone(zone)
          .setSizeGb(diskSizeGb)
          .setSourceDisk(diskLink)
          .setType(diskType)
          .setName(diskName)
          .build();

      // Wait for the insert instance operation to complete.
      Operation operation = disksClient.insertAsync(project, zone, disk)
          .get(3, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.println("Disk creation failed!");
        throw new Error(operation.getError().toString());
      }
      System.out.println(
          "Disk created from source. Operation Status: " + operation.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 create_disk_from_disk(
    project_id: str,
    zone: str,
    disk_name: str,
    disk_type: str,
    disk_size_gb: int,
    disk_link: str,
) -> compute_v1.Disk:
    """
    Creates a disk in a project in a given zone.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone in which you want to create the disk.
        disk_name: name of the disk you want to create.
        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
        disk_link: a link to the disk you want to use as a source for the new disk.
            This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"

    Returns:
        An attachable disk.
    """
    disk_client = compute_v1.DisksClient()
    disk = compute_v1.Disk()
    disk.zone = zone
    disk.size_gb = disk_size_gb
    disk.source_disk = disk_link
    disk.type_ = disk_type
    disk.name = disk_name
    operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)

    wait_for_extended_operation(operation, "disk creation")

    return disk_client.get(project=project_id, zone=zone, disk=disk_name)

REST

영역 소스 디스크를 클론하고 새 영역 디스크를 만들려면 POST 요청을 compute.disks.insert 메서드에 보냅니다. 요청 본문에서 namesourceDisk 매개변수를 지정합니다. 클론은 소스 디스크에서 생략된 모든 속성을 상속합니다.

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/disks

{
  "name": "TARGET_DISK_NAME"
  "sourceDisk": "projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME"
}

다음을 바꿉니다.

  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • TARGET_DISK_NAME: 새 디스크 이름
  • SOURCE_DISK_NAME: 소스 디스크의 이름

영역 디스크에서 리전 디스크 클론 만들기

기존 영역 Persistent Disk 볼륨을 클론하여 새로운 리전 Persistent Disk 디스크를 만들 수 있습니다. 영역 디스크를 리전 디스크로 마이그레이션하려면 영역 디스크 스냅샷을 만들고 스냅샷을 새 리전 디스크로 복원하는 대신 이 옵션을 사용하는 것이 좋습니다.

콘솔

  1. Google Cloud Console에서 디스크 페이지로 이동합니다.

    디스크로 이동

  2. 디스크 목록에서 클론할 영역 Persistent Disk 볼륨으로 이동합니다.

  3. 작업 열에서 메뉴 버튼을 클릭하고 디스크 클론을 선택합니다.

    클론 만들기

    표시된 디스크 클론 패널에서 다음을 수행합니다.

    1. 이름 필드에 클론된 디스크의 이름을 지정합니다.
    2. 위치리전을 선택한 후 새 클론된 리전 디스크의 보조 복제본 영역을 선택합니다.
    3. 속성에서 클론된 디스크의 기타 세부정보를 검토합니다.
    4. 클론된 디스크 만들기를 마치려면 만들기를 클릭합니다.

gcloud

영역 디스크에서 리전 디스크 클론을 만들려면 gcloud compute disks create 명령어를 실행하고 --region--replica-zones 매개변수를 지정합니다.

gcloud compute disks create TARGET_DISK_NAME \
  --description="zonal to regional cloned disk" \
  --region=CLONED_REGION \
  --source-disk=SOURCE_DISK_NAME \
  --source-disk-zone=SOURCE_DISK_ZONE \
  --replica-zones=SOURCE_DISK_ZONE,REPLICA_ZONE_2 \
  --project=PROJECT_ID

다음을 바꿉니다.

  • TARGET_DISK_NAME: 새 리전 디스크 클론 이름
  • CLONED_REGION: 소스 및 클론된 디스크의 리전
  • SOURCE_DISK_NAME: 클론할 영역 디스크의 이름
  • SOURCE_DISK_ZONE: 소스 디스크 영역. 또한 이 영역이 리전 디스크 클론의 첫 번째 복제본 영역이 됩니다.
  • REPLICA_ZONE_2: 새 리전 디스크 클론의 두 번째 복제본 영역
  • PROJECT_ID: 디스크를 클론할 프로젝트 ID

Terraform

영역 디스크에서 리전 디스크 클론을 만들려면 선택적으로 영역 디스크의 스냅샷을 만든 후 스냅샷을 클론할 수 있습니다. 이렇게 하려면 다음 리소스를 따르세요.

resource "google_compute_region_disk" "regiondisk" {
  name                      = "region-disk-name"
  snapshot                  = google_compute_snapshot.snapdisk.id
  type                      = "pd-ssd"
  region                    = "us-central1"
  physical_block_size_bytes = 4096
  size                      = 11

  replica_zones = ["us-central1-a", "us-central1-f"]
}

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"
)

// createRegionalDiskFromDisk creates a new regional disk with the contents of
// an already existitng zonal disk. Disk type and size may differ.
func createRegionalDiskFromDisk(
	w io.Writer,
	projectID, region string, replicaZones []string,
	diskName, diskType, sourceDiskLink string,
	diskSizeGb int64,
) error {
	// projectID := "your_project_id"
	// region := "us-west3" // should match diskType below
	// diskName := "your_disk_name"
	// diskType := "regions/us-west3/diskTypes/pd-ssd"
	// sourceDiskLink := "projects/your_project_id/global/disks/disk_name"
	// diskSizeGb := 120

	// Exactly two replica zones must be specified
	replicaZoneURLs := []string{
		fmt.Sprintf("projects/%s/zones/%s", projectID, replicaZones[0]),
		fmt.Sprintf("projects/%s/zones/%s", projectID, replicaZones[1]),
	}

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

	req := &computepb.InsertRegionDiskRequest{
		Project: projectID,
		Region:  region,
		DiskResource: &computepb.Disk{
			Name:         proto.String(diskName),
			Region:       proto.String(region),
			Type:         proto.String(diskType),
			SourceDisk:   proto.String(sourceDiskLink),
			SizeGb:       proto.Int64(diskSizeGb),
			ReplicaZones: replicaZoneURLs,
		},
	}

	op, err := disksClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create disk: %w", err)
	}

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

	fmt.Fprintf(w, "Disk created\n")

	return nil
}

자바

Java

이 샘플을 사용해 보기 전에 Compute Engine 빠른 시작: 클라이언트 라이브러리 사용Java 설정 안내를 따르세요. 자세한 내용은 Compute Engine Java API 참고 문서를 확인하세요.

Compute Engine에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


import com.google.cloud.compute.v1.Disk;
import com.google.cloud.compute.v1.Operation;
import com.google.cloud.compute.v1.RegionDisksClient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class RegionalCreateFromSource {

  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 project = "YOUR_PROJECT_ID";

    // Name of the zone in which you want to create the disk.
    String region = "europe-central2";

    // An iterable collection of zone names in which you want to keep
    // the new disks' replicas. One of the replica zones of the clone must match
    // the zone of the source disk.
    List<String> replicaZones = new ArrayList<>();

    // Name of the disk you want to create.
    String diskName = "YOUR_DISK_NAME";

    // 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"
    String diskType = String.format("zones/%s/diskTypes/pd-ssd", "ZONE_NAME");

    // Size of the new disk in gigabytes.
    int diskSizeGb = 10;

    // A link to the disk you want to use as a source for the new disk.
    // This value uses the following format:
    // "projects/{project_name}/zones/{zone}/disks/{disk_name}"
    String diskLink = String.format("projects/%s/zones/%s/disks/%s", "PROJECT_NAME", "ZONE",
        "DISK_NAME");

    // A link to the snapshot you want to use as a source for the new disk.
    // This value uses the following format:
    // "projects/{project_name}/global/snapshots/{snapshot_name}"
    String snapshotLink = String.format("projects/%s/global/snapshots/%s", "PROJECT_NAME",
        "SNAPSHOT_NAME");

    createRegionalDisk(project, region, replicaZones, diskName, diskType, diskSizeGb,
        Optional.ofNullable(diskLink), Optional.ofNullable(snapshotLink));
  }

  // Creates a regional disk from an existing zonal disk in a given project.
  public static void createRegionalDisk(
      String project, String region, List<String> replicaZones, String diskName, String diskType,
      int diskSizeGb, Optional<String> diskLink, Optional<String> snapshotLink)
      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 `regionDisksClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (RegionDisksClient regionDisksClient = RegionDisksClient.create()) {

      Disk.Builder diskBuilder = Disk.newBuilder()
          .addAllReplicaZones(replicaZones)
          .setName(diskName)
          .setType(diskType)
          .setSizeGb(diskSizeGb)
          .setRegion(region);

      // Set source disk if diskLink is not empty.
      diskLink.ifPresent(diskBuilder::setSourceDisk);

      // Set source snapshot if the snapshot link is not empty.
      snapshotLink.ifPresent(diskBuilder::setSourceSnapshot);

      // Wait for the operation to complete.
      Operation operation = regionDisksClient.insertAsync(project, region, diskBuilder.build())
          .get(3, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.println("Disk creation failed!");
        throw new Error(operation.getError().toString());
      }
      System.out.println(
          "Regional disk created. Operation Status: " + operation.getStatus());
    }
  }
}

Python

Python

이 샘플을 사용해 보기 전에 Compute Engine 빠른 시작: 클라이언트 라이브러리 사용Python 설정 안내를 따르세요. 자세한 내용은 Compute Engine Python API 참고 문서를 확인하세요.

Compute Engine에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

from __future__ import annotations

from collections.abc import Iterable
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 create_regional_disk(
    project_id: str,
    region: str,
    replica_zones: Iterable[str],
    disk_name: str,
    disk_type: str,
    disk_size_gb: int,
    disk_link: str | None = None,
    snapshot_link: str | None = None,
) -> compute_v1.Disk:
    """
    Creates a regional disk from an existing zonal disk in a given project.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        region: name of the region in which you want to create the disk.
        replica_zones: an iterable collection of zone names in which you want to keep
            the new disks' replicas. One of the replica zones of the clone must match
            the zone of the source disk.
        disk_name: name of the disk you want to create.
        disk_type: the type of disk you want to create. This value uses the following format:
            "regions/{region}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
            For example: "regions/us-west3/diskTypes/pd-ssd"
        disk_size_gb: size of the new disk in gigabytes
        disk_link: a link to the disk you want to use as a source for the new disk.
            This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
        snapshot_link: a link to the snapshot you want to use as a source for the new disk.
            This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}"

    Returns:
        An attachable regional disk.
    """
    disk_client = compute_v1.RegionDisksClient()
    disk = compute_v1.Disk()
    disk.replica_zones = replica_zones
    disk.size_gb = disk_size_gb
    if disk_link:
        disk.source_disk = disk_link
    if snapshot_link:
        disk.source_snapshot = snapshot_link
    disk.type_ = disk_type
    disk.region = region
    disk.name = disk_name
    operation = disk_client.insert(
        project=project_id, region=region, disk_resource=disk
    )

    wait_for_extended_operation(operation, "disk creation")

    return disk_client.get(project=project_id, region=region, disk=disk_name)

REST

영역 디스크에서 리전 디스크 클론을 만들려면 POST 요청을 compute.disks.insert 메서드에 보내고 sourceDiskreplicaZone 매개변수를 지정합니다.

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/regions/CLONED_REGION/disks

{
  "name": "TARGET_DISK_NAME"
  "sourceDisk": "projects/PROJECT_ID/zones/SOURCE_DISK_ZONE/disks/SOURCE_DISK_NAME"
  "replicaZone": "SOURCE_DISK_ZONE,REPLICA_ZONE_2"
}

다음을 바꿉니다.

  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • TARGET_DISK_NAME: 새 리전 디스크 클론 이름
  • CLONED_REGION: 소스 및 클론된 디스크의 리전
  • SOURCE_DISK_NAME: 클론할 영역 디스크의 이름
  • SOURCE_DISK_ZONE: 소스 디스크 영역. 또한 이 영역이 리전 디스크 클론의 첫 번째 복제본 영역이 됩니다.
  • REPLICA_ZONE_2: 새 리전 디스크 클론의 두 번째 복제본 영역

암호화된 소스 디스크의 디스크 클론 만들기

고객 제공 암호화 키(CSEK) 또는 고객 관리 암호화 키를 사용하여 디스크를 암호화할 수 있습니다.

CSEK 암호화 디스크의 디스크 클론 만들기

CSEK를 사용하여 소스 디스크를 암호화하는 경우 같은 키를 사용하여 클론을 암호화해야 합니다.

콘솔

  1. Google Cloud Console에서 디스크 페이지로 이동합니다.

    디스크로 이동

  2. 영역 영구 디스크 목록에서 클론할 디스크를 찾습니다.

  3. 작업 열에서 메뉴 버튼을 클릭하고 디스크 클론을 선택합니다.

    클론 만들기

    표시된 디스크 클론 패널에서 다음을 수행합니다.

    1. 이름 필드에 클론된 디스크의 이름을 지정합니다.
    2. 복호화 및 암호화 필드에 소스 디스크 암호화 키를 입력합니다.
    3. 속성에서 클론된 디스크의 기타 세부정보를 검토합니다.
    4. 클론된 디스크 만들기를 마치려면 만들기를 클릭합니다.

gcloud

CSEK로 암호화된 소스 디스크의 디스크 클론을 만들려면 gcloud compute disks create 명령어를 실행하고 --csek-key-file 플래그를 사용하여 소스 디스크 암호화 키를 제공합니다. RSA 래핑 키를 사용하는 경우 gcloud beta compute disks create 명령어를 사용합니다.

gcloud compute disks create TARGET_DISK_NAME \
  --description="cloned disk" \
  --source-disk=projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME \
  --csek-key-file example-key-file.json

다음을 바꿉니다.

  • TARGET_DISK_NAME: 새 디스크 이름
  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • SOURCE_DISK_NAME: 소스 디스크의 이름

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"
)

// Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk.
// The encryption key must be the same for the source disk and the new disk.
// The disk type and size may differ.
func createDiskFromCustomerEncryptedDisk(
	w io.Writer,
	projectID, zone, diskName, diskType string,
	diskSizeGb int64,
	diskLink, encryptionKey string,
) error {
	// projectID := "your_project_id"
	// zone := "us-west3-b" // should match diskType below
	// diskName := "your_disk_name"
	// diskType := "zones/us-west3/diskTypes/pd-ssd"
	// diskSizeGb := 120
	// diskLink := "projects/your_project_id/global/disks/disk_name"
	// encryptionKey := "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" // in base64

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

	req := &computepb.InsertDiskRequest{
		Project: projectID,
		Zone:    zone,
		DiskResource: &computepb.Disk{
			Name:       proto.String(diskName),
			Zone:       proto.String(zone),
			Type:       proto.String(diskType),
			SizeGb:     proto.Int64(diskSizeGb),
			SourceDisk: proto.String(diskLink),
			DiskEncryptionKey: &computepb.CustomerEncryptionKey{
				RawKey: &encryptionKey,
			},
		},
	}

	op, err := disksClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create disk: %w", err)
	}

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

	fmt.Fprintf(w, "Disk created\n")

	return nil
}

자바

Java

이 샘플을 사용해 보기 전에 Compute Engine 빠른 시작: 클라이언트 라이브러리 사용Java 설정 안내를 따르세요. 자세한 내용은 Compute Engine Java API 참고 문서를 확인하세요.

Compute Engine에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


import com.google.cloud.compute.v1.CustomerEncryptionKey;
import com.google.cloud.compute.v1.Disk;
import com.google.cloud.compute.v1.DisksClient;
import com.google.cloud.compute.v1.InsertDiskRequest;
import com.google.cloud.compute.v1.Operation;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CloneEncryptedDisk {

  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 project = "YOUR_PROJECT_ID";

    // Name of the zone in which you want to create the disk.
    String zone = "europe-central2-b";

    // Name of the disk you want to create.
    String diskName = "YOUR_DISK_NAME";

    // 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"
    String diskType = String.format("zones/%s/diskTypes/pd-ssd", zone);

    // Size of the new disk in gigabytes.
    int diskSizeGb = 10;

    // A link to the disk you want to use as a source for the new disk.
    // This value uses the following format:
    // "projects/{project_name}/zones/{zone}/disks/{disk_name}"
    String diskLink = String.format("projects/%s/zones/%s/disks/%s", "PROJECT_NAME", "ZONE",
        "DISK_NAME");

    // Customer-supplied encryption key used for encrypting data in the source disk.
    // The data will be encrypted with the same key in the new disk.
    byte[] encryptionKey = null;

    createDiskFromCustomerEncryptedKey(project, zone, diskName, diskType, diskSizeGb, diskLink,
        encryptionKey);
  }

  // Creates a zonal non-boot persistent disk in a project with the copy of data
  // from an existing disk.
  // The encryption key must be the same for the source disk and the new disk.
  public static void createDiskFromCustomerEncryptedKey(String project, String zone,
      String diskName, String diskType, int diskSizeGb, String diskLink, byte[] encryptionKey)
      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 `disksClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (DisksClient disksClient = DisksClient.create()) {

      // Create a disk and set the encryption key.
      Disk disk = Disk.newBuilder()
          .setZone(zone)
          .setName(diskName)
          .setType(diskType)
          .setSizeGb(diskSizeGb)
          .setSourceDisk(diskLink)
          .setDiskEncryptionKey(CustomerEncryptionKey
              .newBuilder()
              .setRawKeyBytes(ByteString.copyFrom(encryptionKey))
              .build())
          .build();

      // Wait for the insert disk operation to complete.
      Operation operation = disksClient.insertAsync(
          InsertDiskRequest.newBuilder()
              .setProject(project)
              .setZone(zone)
              .setDiskResource(disk)
              .build()).get(3, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.println("Disk creation failed!");
        throw new Error(operation.getError().toString());
      }
      System.out.println(
          "Disk cloned with customer encryption key. Operation Status: " + operation.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 create_disk_from_customer_encrypted_disk(
    project_id: str,
    zone: str,
    disk_name: str,
    disk_type: str,
    disk_size_gb: int,
    disk_link: str,
    encryption_key: bytes,
) -> compute_v1.Disk:
    """
    Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk.

    The encryption key must be the same for the source disk and the new disk.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone in which you want to create the disk.
        disk_name: name of the disk you want to create.
        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
        disk_link: a link to the disk you want to use as a source for the new disk.
            This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
        encryption_key: customer-supplied encryption key used for encrypting
            data in the source disk. The data will be encrypted with the same key
            in the new disk.

    Returns:
        An attachable copy of an existing disk.
    """
    disk_client = compute_v1.DisksClient()
    disk = compute_v1.Disk()
    disk.zone = zone
    disk.size_gb = disk_size_gb
    disk.source_disk = disk_link
    disk.type_ = disk_type
    disk.name = disk_name
    disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
    disk.disk_encryption_key.raw_key = encryption_key
    operation = disk_client.insert(project=project_id, zone=zone, disk_resource=disk)

    wait_for_extended_operation(operation, "disk creation")

    return disk_client.get(project=project_id, zone=zone, disk=disk_name)

REST

CSEK로 암호화된 소스 디스크의 디스크 클론을 만들려면 POST 요청을 compute.disks.insert 메서드에 보내고 diskEncryptionKey 속성을 사용하여 소스 디스크 암호화 키를 제공합니다. RSA 래핑 키를 사용하는 경우 메서드의 beta 버전을 사용합니다.

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/disks

{
  "name": "TARGET_DISK_NAME"
  "sourceDisk": "projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME"
  "diskEncryptionKey": {
    "rsaEncryptedKey": "ieCx/NcW06PcT7Ep1X6LUTc/hLvUDYyzSZPPVCVPTVEohpeHASqC8uw5TzyO9U+Fka9JFHz0mBibXUInrC/jEk014kCK/NPjYgEMOyssZ4ZINPKxlUh2zn1bV+MCaTICrdmuSBTWlUUiFoDD6PYznLwh8ZNdaheCeZ8ewEXgFQ8V+sDroLaN3Xs3MDTXQEMMoNUXMCZEIpg9Vtp9x2oeQ5lAbtt7bYAAHf5l+gJWw3sUfs0/Glw5fpdjT8Uggrr+RMZezGrltJEF293rvTIjWOEB3z5OHyHwQkvdrPDFcTqsLfh+8Hr8g+mf+7zVPEC8nEbqpdl3GPv3A7AwpFp7MA=="
  },
}

다음을 바꿉니다.

  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • TARGET_DISK_NAME: 새 디스크 이름
  • SOURCE_DISK_NAME: 소스 디스크의 이름

CMEK 암호화 디스크의 디스크 클론 만들기

CMEK를 사용하여 소스 디스크를 암호화하는 경우 같은 키를 사용하여 클론을 암호화해야 합니다.

콘솔

Compute Engine은 소스 디스크 암호화 키를 사용하여 클론을 자동으로 암호화합니다.

gcloud

CSEK로 암호화된 소스 디스크의 디스크 클론을 만들려면 gcloud compute disks create 명령어를 실행하고 --kms-key 플래그를 사용하여 소스 디스크 암호화 키를 제공합니다. RSA 래핑 키를 사용하는 경우 gcloud beta compute disks create 명령어를 사용합니다.

gcloud compute disks create TARGET_DISK_NAME \
  --description="cloned disk" \
  --source-disk=projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME \
  --kms-key projects/KMS_PROJECT_ID/locations/REGION/keyRings/KEY_RING/cryptoKeys/KEY

다음을 바꿉니다.

  • TARGET_DISK_NAME: 새 디스크 이름
  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • SOURCE_DISK_NAME: 소스 디스크의 이름입니다.
  • KMS_PROJECT_ID: 암호화 키의 프로젝트 ID
  • REGION: 암호화 키 리전
  • KEY_RING: 암호화 키의 키링
  • KEY: 암호화 키 이름

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"
)

// Creates a zonal non-boot persistent disk in a project with the copy of data from an existing disk.
// The encryption key must be the same for the source disk and the new disk.
// The disk type and size may differ.
func createDiskFromKmsEncryptedDisk(
	w io.Writer,
	projectID, zone, diskName, diskType string,
	diskSizeGb int64,
	diskLink, kmsKeyLink string,
) error {
	// projectID := "your_project_id"
	// zone := "us-west3-b" // should match diskType below
	// diskName := "your_disk_name"
	// diskType := "zones/us-west3/diskTypes/pd-ssd"
	// diskSizeGb := 120
	// diskLink := "projects/your_project_id/global/disks/disk_name"
	// kmsKeyLink := "projects/your_kms_project_id/locations/us-central1/keyRings/your_key_ring/cryptoKeys/your_key"

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

	req := &computepb.InsertDiskRequest{
		Project: projectID,
		Zone:    zone,
		DiskResource: &computepb.Disk{
			Name:       proto.String(diskName),
			Zone:       proto.String(zone),
			Type:       proto.String(diskType),
			SizeGb:     proto.Int64(diskSizeGb),
			SourceDisk: proto.String(diskLink),
			DiskEncryptionKey: &computepb.CustomerEncryptionKey{
				KmsKeyName: &kmsKeyLink,
			},
		},
	}

	op, err := disksClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create disk: %w", err)
	}

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

	fmt.Fprintf(w, "Disk created\n")

	return nil
}

자바

Java

이 샘플을 사용해 보기 전에 Compute Engine 빠른 시작: 클라이언트 라이브러리 사용Java 설정 안내를 따르세요. 자세한 내용은 Compute Engine Java API 참고 문서를 확인하세요.

Compute Engine에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


import com.google.cloud.compute.v1.CustomerEncryptionKey;
import com.google.cloud.compute.v1.Disk;
import com.google.cloud.compute.v1.DisksClient;
import com.google.cloud.compute.v1.InsertDiskRequest;
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 CloneEncryptedDiskManagedKey {

  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 project = "YOUR_PROJECT_ID";

    // Name of the zone in which you want to create the disk.
    String zone = "europe-central2-b";

    // Name of the disk you want to create.
    String diskName = "YOUR_DISK_NAME";

    // 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"
    String diskType = String.format("zones/%s/diskTypes/pd-ssd", zone);

    // Size of the new disk in gigabytes.
    int diskSizeGb = 10;

    // A link to the disk you want to use as a source for the new disk.
    // This value uses the following format:
    // "projects/{project_name}/zones/{zone}/disks/{disk_name}"
    String diskLink = String.format("projects/%s/zones/%s/disks/%s", "PROJECT_NAME", "ZONE",
        "DISK_NAME");

    // URL of the key from KMS. The key might be from another project, as
    // long as you have access to it. The data will be encrypted with the same key
    // in the new disk. This value uses following format:
    // "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"
    String kmsKeyName = "kms-key-name";

    createDiskFromKmsEncryptedDisk(project, zone, diskName, diskType, diskSizeGb, diskLink,
        kmsKeyName);
  }

  // Creates a zonal non-boot disk in a project with the copy of data from an existing disk.
  // The encryption key must be the same for the source disk and the new disk.
  public static void createDiskFromKmsEncryptedDisk(String project, String zone, String diskName,
      String diskType, int diskSizeGb, String diskLink, String kmsKeyName)
      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 `disksClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (DisksClient disksClient = DisksClient.create()) {

      // Create a disk and set the KMS encryption key name.
      Disk disk = Disk.newBuilder()
          .setZone(zone)
          .setName(diskName)
          .setType(diskType)
          .setSizeGb(diskSizeGb)
          .setSourceDisk(diskLink)
          .setDiskEncryptionKey(CustomerEncryptionKey.newBuilder()
              .setKmsKeyName(kmsKeyName)
              .build())
          .build();

      // Wait for the insert disk operation to complete.
      Operation operation = disksClient.insertAsync(
          InsertDiskRequest.newBuilder()
              .setProject(project)
              .setZone(zone)
              .setDiskResource(disk)
              .build()).get(3, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.println("Disk creation failed!");
        throw new Error(operation.getError().toString());
      }
      System.out.println(
          "Disk cloned with KMS encryption key. Operation Status: " + operation.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.exceptions import BadRequest
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 create_disk_from_kms_encrypted_disk(
    project_id: str,
    zone: str,
    disk_name: str,
    disk_type: str,
    disk_size_gb: int,
    disk_link: str,
    kms_key_name: str,
) -> compute_v1.Disk:
    """
    Creates a zonal non-boot disk in a project with the copy of data from an existing disk.

    The encryption key must be the same for the source disk and the new disk.

    To run this method, the service-<project_id>@compute-system.iam.gserviceaccount.com
    service account needs to have the cloudkms.cryptoKeyEncrypterDecrypter role,
    as described in documentation:
    https://cloud.google.com/compute/docs/disks/customer-managed-encryption#before_you_begin

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone in which you want to create the disk.
        disk_name: name of the disk you want to create.
        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
        disk_link: a link to the disk you want to use as a source for the new disk.
            This value uses the following format: "projects/{project_name}/zones/{zone}/disks/{disk_name}"
        kms_key_name: URL of the key from KMS. The key might be from another project, as
            long as you have access to it. The data will be encrypted with the same key
            in the new disk. This value uses following format:
            "projects/{kms_project_id}/locations/{region}/keyRings/{key_ring}/cryptoKeys/{key}"

    Returns:
        An attachable copy of an existing disk.
    """
    disk_client = compute_v1.DisksClient()
    disk = compute_v1.Disk()
    disk.zone = zone
    disk.size_gb = disk_size_gb
    disk.source_disk = disk_link
    disk.type_ = disk_type
    disk.name = disk_name
    disk.disk_encryption_key = compute_v1.CustomerEncryptionKey()
    disk.disk_encryption_key.kms_key_name = kms_key_name
    try:
        operation = disk_client.insert(
            project=project_id, zone=zone, disk_resource=disk
        )
    except BadRequest as err:
        if "Permission 'cloudkms.cryptoKeyVersions.useToEncrypt' denied" in err.message:
            print(
                f"Please provide the cloudkms.cryptoKeyEncrypterDecrypter role to"
                f"service-{project_id}@compute-system.iam.gserviceaccount.com"
            )
        raise err

    wait_for_extended_operation(operation, "disk creation")

    return disk_client.get(project=project_id, zone=zone, disk=disk_name)

REST

CSEK로 암호화된 소스 디스크의 디스크 클론을 만들려면 POST 요청을 compute.disks.insert 메서드에 보내고 kmsKeyName 속성을 사용하여 소스 디스크 암호화 키를 제공합니다. RSA 래핑 키를 사용하는 경우 메서드의 beta 버전을 사용합니다.

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/disks

{
  "name": "TARGET_DISK_NAME"
  "sourceDisk": "projects/PROJECT_ID/zones/ZONE/disks/SOURCE_DISK_NAME"
  "diskEncryptionKey": {
    "kmsKeyName": "projects/KMS_PROJECT_ID/locations/REGION/keyRings/KEY_RING/cryptoKeys/KEY"
  },
}

다음을 바꿉니다.

  • PROJECT_ID: 디스크를 클론할 프로젝트 ID
  • ZONE: 소스 및 새 디스크의 영역
  • TARGET_DISK_NAME: 새 디스크 이름
  • SOURCE_DISK_NAME: 소스 디스크의 이름입니다.
  • KMS_PROJECT_ID: 암호화 키의 프로젝트 ID
  • REGION: 암호화 키 리전
  • KEY_RING: 암호화 키의 키링
  • KEY: 암호화 키 이름

다음 단계