커스텀 Windows Server 이미지 만들기


기존 Compute Engine Windows Server 이미지에서 맞춤설정된 Windows Server 이미지를 만들 수 있습니다. 이러한 커스텀 이미지를 사용하여 기존 인스턴스와 동일한 부팅 디스크로 인스턴스를 만듭니다.

이러한 커스텀 이미지는 기존 인스턴스의 운영체제 구성을 저장하고 동일 구성을 재사용해 다른 인스턴스를 만들 때 유용합니다.

이 섹션에서는 다음 태스크를 다루지 않습니다.

시작하기 전에

  • 아직 인증을 설정하지 않았다면 설정합니다. 인증은 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. 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.

      Node.js

      이 페이지의 Node.js 샘플을 로컬 개발 환경에서 사용하려면 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 사용 인증을 참조하세요.

Windows Server 이미지 만들기 준비

이미지를 만들기 전에 인스턴스에서 GCESysprep을 실행해 이미지 생성 프로세스를 준비합니다.

GCESysprep은 다음 단계를 완료하여 Compute Engine 이미지가 되도록 인스턴스를 준비합니다.

  1. GCEStartup 예약 태스크를 중지합니다.
  2. 임시 폴더 위치에서 모든 파일을 삭제합니다.
  3. 모든 Windows 이벤트 로그를 지웁니다.
  4. sysprep.exe /generalize /oobe /quit /unattend를 실행합니다.
  5. VM 인스턴스를 처음 부팅할 때 실행할 instance_setup.ps1을 구성합니다.
  6. RDP 인증서를 삭제합니다.
  7. 영구 디스크의 저장된 목록을 삭제합니다.
  8. RDP 및 WinRM 방화벽 규칙을 사용 설정합니다.
  9. Google OS 구성 서비스를 중지합니다.
  10. 인스턴스를 종료합니다.

GCESysprep 작업은 Windows 이벤트 로그 및 직렬 포트 1에 로깅됩니다. Sysprep은 여러 로그 파일에 작성합니다.

GCESysprep을 사용하여 Compute Engine 이미지를 만드는 방법은 다음과 같습니다.

  1. 관리자 권한으로 GCESysprep을 실행합니다.

    GCESysprep
    
  2. 이미지를 만듭니다.

이미지 위치 지정

커스텀 이미지를 만들 때 이중 리전 위치를 제외하고 이미지의 Cloud Storage 위치를 지정할 수 있습니다. 이미지 스토리지 위치를 지정하면 데이터 위치에 대한 규제 및 규정 준수 요구사항뿐 아니라 여러 리전 간에 데이터 중복성을 보장함으로써 고가용성 요건을 충족할 수 있습니다.

스토리지 위치 기능은 선택사항입니다. 위치를 선택하지 않으면 Compute Engine은 이미지 소스와 가장 가까운 멀티 리전에 이미지를 저장합니다. 소스 디스크, 이미지, 스냅샷, Cloud Storage에 저장된 이미지에서 커스텀 이미지를 만들 수 있습니다. 이러한 이미지를 사용하여 새 VM 인스턴스를 만들 수 있습니다.

이 기능이 출시되기 이전의 모든 기존 이미지는 현재 위치에 그대로 유지됩니다. 유일한 변경사항은 모든 이미지의 이미지 위치를 볼 수 있다는 것입니다. 기존 이미지를 옮기려면 원하는 위치에서 이 이미지를 다시 만들어야 합니다.

Windows 이미지 만들기

다음 소스에서 디스크 이미지를 만들 수 있습니다.

  • 영구 디스크(디스크가 인스턴스에 연결된 경우에도 가능)
  • 영구 디스크의 스냅샷
  • 프로젝트의 다른 이미지
  • 다른 프로젝트에서 공유된 이미지
  • Cloud Storage에 저장된 가져온 이미지

콘솔

  1. Google Cloud Console에서 새 이미지 만들기 페이지로 이동합니다.

    새 이미지 만들기

  2. 이미지 이름을 지정합니다.
  3. 이미지를 만들 Windows 운영체제의 소스 디스크를 선택합니다.
  4. 이미지를 저장할 위치를 지정합니다. 소스 디스크 위치 기준(기본값) 드롭다운 메뉴에서 이미지 위치를 선택합니다. 예를 들어 us를 지정하여 us 멀티 리전에 이미지를 저장하거나 us-central1을 지정하여 us-central1 리전에 저장합니다. 선택하지 않으면 Compute Engine은 이미지의 소스 위치와 가장 가까운 멀티 리전에 이미지를 저장합니다.
  5. 이미지 속성을 지정합니다. 예를 들어 이미지에 이미지 계열 이름을 지정하여 이 이미지를 이미지 계열의 일부로 구성할 수 있습니다.
  6. 만들기를 클릭합니다.

gcloud

gcloud computeimages create와 함께 사용하고 이미지를 만들 소스 영구 디스크를 제공합니다. 원하는 경우 실행 중인 인스턴스에 연결되어 있어도 이미지를 만들도록 --force 플래그를 포함합니다.

gcloud compute images create example-image --source-disk [DISK_NAME] \
    --source-disk-zone [ZONE] \
    --storage-location [LOCATION] \
    [--force]
  • [DISK_NAME]은 이미지를 만들 소스 디스크의 이름입니다.
  • [ZONE]은 디스크 영역입니다.
  • [LOCATION]은 이미지가 저장될 리전 또는 멀티 리전을 지정할 수 있는 선택적 플래그입니다. 예를 들어 us를 지정하여 us 멀티 리전에 이미지를 저장하거나 us-central1을 지정하여 us-central1 리전에 저장합니다. 선택하지 않으면 Compute Engine은 이미지의 소스 위치와 가장 가까운 멀티 리전에 이미지를 저장합니다.
  • 선택사항인 --force는 디스크가 실행 중인 인스턴스에 연결된 상태여도 이미지를 만드는 플래그입니다. 이 옵션을 사용하면 이미지의 무결성이 손상될 수 있습니다. 가능하다면 이미지를 만들기 전에 인스턴스를 중지합니다.

이 명령어를 실행하면 gcloud compute에서 제공된 영구 디스크를 기반으로 새 이미지를 만들어 컬렉션에 추가합니다. 다음을 실행하면 이미지가 성공적으로 만들어졌는지 확인할 수 있습니다.

gcloud compute images list

Go

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

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

import (
	"context"
	"fmt"
	"io"
	"strings"

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

// createWindowsOSImage creates a new Windows image from the specified source disk.
func createWindowsOSImage(
	w io.Writer,
	projectID, zone, sourceDiskName, imageName, storageLocation string,
	forceCreate bool,
) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// sourceDiskName := "your_source_disk_name"
	// imageName := "your_image_name"
	// storageLocation := "eu"
	// forceCreate := false

	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()
	disksClient, err := compute.NewDisksRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewDisksRESTClient: %w", err)
	}
	defer disksClient.Close()

	// Getting instances where source disk is attached
	diskRequest := &computepb.GetDiskRequest{
		Project: projectID,
		Zone:    zone,
		Disk:    sourceDiskName,
	}

	sourceDisk, err := disksClient.Get(ctx, diskRequest)
	if err != nil {
		return fmt.Errorf("unable to get disk: %w", err)
	}

	// Сhecking whether the instances is stopped
	for _, fullInstanceName := range sourceDisk.GetUsers() {
		parsedName := strings.Split(fullInstanceName, "/")
		l := len(parsedName)
		if l < 5 {
			return fmt.Errorf(
				"API returned instance name with unexpected format",
			)
		}
		instanceReq := &computepb.GetInstanceRequest{
			Project:  parsedName[l-5],
			Zone:     parsedName[l-3],
			Instance: parsedName[l-1],
		}
		instance, err := instancesClient.Get(ctx, instanceReq)
		if err != nil {
			return fmt.Errorf("unable to get instance: %w", err)
		}

		if instance.GetStatus() != "TERMINATED" && instance.GetStatus() != "STOPPED" {
			if !forceCreate {
				return fmt.Errorf("instance %s should be stopped. "+
					"Please stop the instance using "+
					"GCESysprep command or set forceCreate parameter to true "+
					"(not recommended). More information here: "+
					"https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api",
					parsedName[l-1],
				)
			}
		}
	}

	if forceCreate {
		fmt.Fprintf(w, "Warning: ForceCreate option compromise the integrity of your image. "+
			"Stop the instance before you create the image if possible.",
		)
	}

	req := &computepb.InsertImageRequest{
		Project:     projectID,
		ForceCreate: &forceCreate,
		ImageResource: &computepb.Image{
			Name:             proto.String(imageName),
			SourceDisk:       proto.String(fmt.Sprintf("zones/%s/disks/%s", zone, sourceDiskName)),
			StorageLocations: []string{storageLocation},
		},
	}

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

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

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

	return nil
}

자바

이 샘플을 사용해 보기 전에 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.Image;
import com.google.cloud.compute.v1.ImagesClient;
import com.google.cloud.compute.v1.InsertImageRequest;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateImage {

  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 use.
    String project = "your-project-id";
    // Zone of the disk you copy from.
    String zone = "europe-central2-b";
    // Name of the source disk you copy from.
    String sourceDiskName = "source-disk-name";
    // Name of the image you want to create.
    String imageName = "your-image-name";
    // Storage location for the image. If the value is undefined,
    // function will store the image in the multi-region closest to your image's source location.
    String storageLocation = "eu";
    // Create the image even if the source disk is attached to a running instance.
    boolean forceCreate = false;

    createImage(project, zone, sourceDiskName, imageName, storageLocation, forceCreate);
  }

  // Creates a new disk image from the specified source disk.
  public static void createImage(String project, String zone, String sourceDiskName,
      String imageName, String storageLocation, boolean forceCreate)
      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 `client.close()` method on the client to safely
    // clean up any remaining background resources.
    try (ImagesClient imagesClient = ImagesClient.create();
        InstancesClient instancesClient = InstancesClient.create();
        DisksClient disksClient = DisksClient.create()) {

      Disk disk = disksClient.get(project, zone, sourceDiskName);

      // Getting instances where source disk is attached.
      for (String fullInstanceName : disk.getUsersList()) {
        Map<String, String> instanceInfo = parseInstanceName(fullInstanceName);
        Instance instance = instancesClient.get(instanceInfo.get("instanceProjectId"),
            instanceInfo.get("instanceZone"), instanceInfo.get("instanceName"));

        // Сheck whether the instances are stopped.
        if (!Arrays.asList("TERMINATED", "STOPPED").contains(instance.getStatus())
            && !forceCreate) {
          throw new IllegalStateException(
              String.format(
                  "Instance %s should be stopped. For Windows instances please stop the instance "
                      + "using GCESysprep command. For Linux instances just shut it down normally."
                      + " You can suppress this error and create an image of the disk by setting "
                      + "'forceCreate' parameter to true (not recommended). "
                      + "More information here: "
                      + "* https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api"
                      + "* https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#prepare_instance_for_image",
                  instanceInfo.get("instanceName")));
        }
      }

      if (forceCreate) {
        System.out.println(
            "Warning: forceCreate option compromise the integrity of your image. "
                + "Stop the instance before you create the image if possible.");
      }

      // Create Image.
      Image image = Image.newBuilder()
          .setName(imageName)
          .setSourceDisk(String.format("/zones/%s/disks/%s", zone, sourceDiskName))
          .addStorageLocations(storageLocation.isEmpty() ? "" : storageLocation)
          .build();

      InsertImageRequest insertImageRequest = InsertImageRequest.newBuilder()
          .setProject(project)
          .setForceCreate(forceCreate)
          .setImageResource(image)
          .build();

      Operation response = imagesClient.insertAsync(insertImageRequest).get(5, TimeUnit.MINUTES);

      if (response.hasError()) {
        System.out.println("Image creation failed ! ! " + response);
        return;
      }

      System.out.println("Image created.");
    }
  }


  public static Map<String, String> parseInstanceName(String name) {
    String[] parsedName = name.split("/");
    int splitLength = parsedName.length;

    if (splitLength < 5) {
      throw new IllegalArgumentException(
          "Provide correct instance name in the following format: "
              + "https://www.googleapis.com/compute/v1/projects/PROJECT/zones/ZONE/instances/INSTANCE_NAME");
    }

    return new HashMap<>() {
      {
        put("instanceName", parsedName[splitLength - 1]);
        put("instanceZone", parsedName[splitLength - 3]);
        put("instanceProjectId", parsedName[splitLength - 5]);
      }
    };
  }

}

Node.js

Node.js

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

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

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const sourceDiskName = 'YOUR_SOURCE_DISK_NAME';
// const imageName = 'YOUR_IMAGE_NAME';
// const storageLocation = 'eu';
// const forceCreate = false;

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

function parseInstanceName(name) {
  const parsedName = name.split('/');
  const l = parsedName.length;

  if (parsedName.legth < 5) {
    throw new Error(
      'Provide correct instance name in the following format: https://www.googleapis.com/compute/v1/projects/PROJECT/zones/ZONE/instances/INSTANCE_NAME'
    );
  }

  return [parsedName[l - 1], parsedName[l - 3], parsedName[l - 5]];
}

async function createWindowsOSImage() {
  const imagesClient = new compute.ImagesClient();
  const instancesClient = new compute.InstancesClient();
  const disksClient = new compute.DisksClient();

  // Getting instances where source disk is attached
  const [sourceDisk] = await disksClient.get({
    project: projectId,
    zone,
    disk: sourceDiskName,
  });

  // Сhecking whether the instances is stopped
  for (const fullInstanceName of sourceDisk.users) {
    const [instanceName, instanceZone, instanceProjectId] =
      parseInstanceName(fullInstanceName);
    const [instance] = await instancesClient.get({
      project: instanceProjectId,
      zone: instanceZone,
      instance: instanceName,
    });

    if (
      !['TERMINATED', 'STOPPED'].includes(instance.status) &&
      !forceCreate
    ) {
      throw new Error(
        `Instance ${instanceName} should be stopped. Please stop the instance using GCESysprep command or set forceCreate parameter to true (not recommended). More information here: https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api.`
      );
    }
  }

  if (forceCreate) {
    console.warn(
      'Warning: forceCreate option compromise the integrity of your image. Stop the instance before you create the image if possible.'
    );
  }

  const [response] = await imagesClient.insert({
    project: projectId,
    forceCreate,
    imageResource: {
      name: imageName,
      sourceDisk: `/zones/${zone}/disks/${sourceDiskName}`,
      storageLocations: storageLocation ? [storageLocation] : [],
    },
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.GlobalOperationsClient();

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

  console.log('Image created.');
}

createWindowsOSImage();

Python

Python

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

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

from __future__ import annotations

import sys
from typing import Any
import warnings

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


STOPPED_MACHINE_STATUS = (
    compute_v1.Instance.Status.TERMINATED.name,
    compute_v1.Instance.Status.STOPPED.name,
)


def create_image_from_disk(
    project_id: str,
    zone: str,
    source_disk_name: str,
    image_name: str,
    storage_location: str | None = None,
    force_create: bool = False,
) -> compute_v1.Image:
    """
    Creates a new disk image.

    Args:
        project_id: project ID or project number of the Cloud project you use.
        zone: zone of the disk you copy from.
        source_disk_name: name of the source disk you copy from.
        image_name: name of the image you want to create.
        storage_location: storage location for the image. If the value is undefined,
            function will store the image in the multi-region closest to your image's
            source location.
        force_create: create the image even if the source disk is attached to a
            running instance.

    Returns:
        An Image object.
    """
    image_client = compute_v1.ImagesClient()
    disk_client = compute_v1.DisksClient()
    instance_client = compute_v1.InstancesClient()

    # Get source disk
    disk = disk_client.get(project=project_id, zone=zone, disk=source_disk_name)

    for disk_user in disk.users:
        instance_name = disk_user.split("/")[-1]
        instance = instance_client.get(
            project=project_id, zone=zone, instance=instance_name
        )
        if instance.status in STOPPED_MACHINE_STATUS:
            continue
        if not force_create:
            raise RuntimeError(
                f"Instance {disk_user} should be stopped. For Windows instances please "
                f"stop the instance using `GCESysprep` command. For Linux instances just "
                f"shut it down normally. You can supress this error and create an image of"
                f"the disk by setting `force_create` parameter to true (not recommended). \n"
                f"More information here: \n"
                f" * https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api \n"
                f" * https://cloud.google.com/compute/docs/images/create-delete-deprecate-private-images#prepare_instance_for_image"
            )
        else:
            warnings.warn(
                f"Warning: The `force_create` option may compromise the integrity of your image. "
                f"Stop the {disk_user} instance before you create the image if possible."
            )

    # Create image
    image = compute_v1.Image()
    image.source_disk = disk.self_link
    image.name = image_name
    if storage_location:
        image.storage_locations = [storage_location]

    operation = image_client.insert(project=project_id, image_resource=image)

    wait_for_extended_operation(operation, "image creation from disk")

    return image_client.get(project=project_id, image=image_name)

REST

요청 본문에 sourceDisk URL을 포함하여 images().insert 메서드에 POST 요청을 실행합니다.

POST https://compute.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/images?[FORCE_OPTION]

{
  "name": "[IMAGE_NAME]",
  "sourceDisk": "zones/[ZONE]/disks/[DISK_NAME]",
  "storageLocations": "[LOCATION]",
}

각 항목의 의미는 다음과 같습니다.

  • [PROJECT_ID]는 이 요청의 프로젝트 ID입니다.
  • [FORCE_OPTION]은 소스 디스크가 실행 중인 인스턴스에 연결된 상태여도 이미지를 만드는 옵션입니다. 이 옵션을 설정하려면 POST 줄 끝에 forceCreate=true를 지정합니다. 이 옵션을 사용하면 이미지의 무결성이 손상될 수 있습니다. 가능하다면 이미지를 만들기 전에 인스턴스를 중지합니다.
  • [IMAGE_NAME]은 이미지에 제공한 이름입니다.
  • [ZONE]은 이미지를 만들 소스 디스크의 영역입니다.
  • [DISK_NAME]은 소스 디스크의 이름입니다.
  • [LOCATION]은 이미지의 멀티 리전 또는 리전 스토리지 위치를 선택할 수 있도록 하는 선택적 매개변수입니다. 예를 들어 us를 지정하여 us 멀티 리전에 이미지를 저장하거나 us-central1을 지정하여 us-central1 리전에 저장합니다. 선택하지 않으면 Compute Engine은 이미지의 소스 위치와 가장 가까운 멀티 리전에 이미지를 저장합니다.

이미지 추가에 대한 상세 설명은 이미지 참조를 확인하세요.

Windows 에이전트 및 스크립트 업데이트

Compute Engine에서 최신 에이전트 및 스크립트가 포함된 새 Windows 이미지가 출시되는 경우가 가끔 있습니다. 이 같은 항목은 시작 및 종료 프로세스, 계정 관리, 주소 관리와 함께 Windows 인스턴스를 지원합니다.

Windows 이미지 버전 v20160112부터 Windows 에이전트는 업스트림 출시를 통해 자체 업데이트되고 있습니다. disable-agent-updates 인스턴스 메타데이터 키를 true로 설정하면 에이전트 업데이트를 중지할 수 있습니다. 이전에 출시된 이미지를 사용하는 인스턴스가 있는 경우에는 해당 인스턴스의 Windows 에이전트를 수동으로 업데이트하면 됩니다.

다음 단계