Membuat dan menggunakan Spot VM


Halaman ini menjelaskan cara membuat dan mengelola Spot VM, termasuk hal berikut:

  • Cara membuat, memulai, dan mengidentifikasi Spot VM
  • Cara mendeteksi, menangani, dan menguji preemption Spot VM
  • Praktik terbaik untuk Spot VM

Spot VM adalah instance virtual machine (VM) dengan model penyediaan spot. Spot VM tersedia dengan diskon hingga 60-91% jika dibandingkan dengan harga VM standar. Namun, Compute Engine dapat mengklaim kembali resource tersebut dengan melakukan preempt terhadap Spot VM kapan saja. Spot VM hanya direkomendasikan untuk aplikasi fault-tolerant yang dapat tahan terhadap preemption VM. Pastikan aplikasi Anda dapat menangani preemption sebelum Anda memutuskan untuk membuat Spot VM.

Sebelum memulai

  • Baca dokumentasi konseptual untuk Spot VM:
  • Jika Anda belum melakukannya, siapkan autentikasi. Autentikasi adalah proses verifikasi identitas Anda untuk mengakses layanan dan API Google Cloud. Untuk menjalankan kode atau contoh dari lingkungan pengembangan lokal, Anda dapat mengautentikasi ke Compute Engine dengan memilih salah satu opsi berikut:

    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

      Untuk menggunakan contoh Terraform di halaman ini dalam lingkungan pengembangan lokal, instal dan lakukan inisialisasi gcloud CLI, lalu siapkan Kredensial Default Aplikasi dengan kredensial pengguna Anda.

      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.

      Untuk informasi selengkapnya, lihat Set up authentication for a local development environment.

      REST

      Untuk menggunakan contoh REST API di halaman ini dalam lingkungan pengembangan lokal, gunakan kredensial yang Anda berikan ke gcloud CLI.

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

        gcloud init

      Untuk informasi selengkapnya, lihat Melakukan autentikasi untuk menggunakan REST dalam dokumentasi autentikasi Google Cloud.

Membuat Spot VM

Buat Spot VM menggunakan Konsol Google Cloud, gcloud CLI, atau Compute Engine API. Spot VM adalah VM yang dikonfigurasi untuk menggunakan model penyediaan spot:

  • Model penyediaan VM disetel ke Spot di Konsol Google Cloud
  • --provisioning-model=SPOT di gcloud CLI
  • "provisioningModel": "SPOT" di Compute Engine API

Konsol

  1. Di konsol Google Cloud, buka halaman Create an instance.

    Buka halaman Buat instance

  2. Kemudian, lakukan hal berikut:

    1. Di bagian Kebijakan ketersediaan, pilih Spot dari daftar Model penyediaan VM. Setelan ini menonaktifkan opsi mulai ulang otomatis dan pengelolaan host untuk VM dan mengaktifkan opsi tindakan penghentian.
    2. Opsional: Dalam daftar Saat penghentian VM, pilih apa yang terjadi saat Compute Engine melakukan preempt terhadap VM:
      • Untuk menghentikan VM selama preemption, pilih Hentikan (default).
      • Untuk menghapus VM selama preemption, pilih Hapus.
  3. Opsional: Tentukan opsi VM lainnya. Untuk mengetahui informasi selengkapnya, baca Membuat dan memulai instance VM.

  4. Untuk membuat dan meluncurkan VM, klik Create.

gcloud

Untuk membuat VM dari gcloud CLI, gunakan perintah gcloud compute instances create. Untuk membuat Spot VM, Anda harus menyertakan flag --provisioning-model=SPOT. Anda juga dapat menentukan tindakan penghentian untuk Spot VM dengan menyertakan flag --instance-termination-action juga.

gcloud compute instances create VM_NAME \
    --provisioning-model=SPOT \
    --instance-termination-action=TERMINATION_ACTION

Ganti kode berikut:

  • VM_NAME: nama VM baru.
  • TERMINATION_ACTION: Opsional: tentukan tindakan yang akan diambil saat Compute Engine melakukan preempt terhadap VM, baik STOP (perilaku default) atau DELETE.

Untuk informasi selengkapnya tentang opsi yang dapat Anda tentukan saat membuat VM, lihat Membuat dan memulai instance VM. Misalnya, untuk membuat Spot VM dengan jenis mesin dan image tertentu, gunakan perintah berikut:

gcloud compute instances create VM_NAME \
    --provisioning-model=SPOT \
    [--image=IMAGE | --image-family=IMAGE_FAMILY] \
    --image-project=IMAGE_PROJECT \
    --machine-type=MACHINE_TYPE \
    --instance-termination-action=TERMINATION_ACTION

Ganti kode berikut:

  • VM_NAME: nama VM baru.
  • IMAGE: menentukan salah satu dari hal berikut:
    • IMAGE: versi tertentu dari image publik atau kelompok image. Misalnya, image tertentu adalah --image=debian-10-buster-v20200309.
    • Kelompok image. Ini akan membuat VM dari OS image terbaru yang masih digunakan. Misalnya, jika Anda menentukan --image-family=debian-10, Compute Engine akan membuat VM dari OS image versi terbaru dalam kelompok image Debian 10.
  • IMAGE_PROJECT: project yang berisi image. Misalnya, jika Anda menentukan debian-10 sebagai kelompok image, tentukan debian-cloud sebagai project image.
  • MACHINE_TYPE: jenis mesin bawaan atau kustom untuk VM baru.
  • TERMINATION_ACTION: Opsional: tentukan tindakan yang akan diambil saat Compute Engine melakukan preempt terhadap VM, baik STOP (perilaku default) atau DELETE.

    Untuk mendapatkan daftar jenis mesin yang tersedia di zona, gunakan perintah gcloud compute machine-types list dengan flag --zones.

Terraform

Anda dapat menggunakan resource Terraform untuk membuat instance spot menggunakan blok penjadwalan


resource "google_compute_instance" "spot_vm_instance" {
  name         = "spot-instance-name"
  machine_type = "f1-micro"
  zone         = "us-central1-c"

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

  scheduling {
    preemptible                 = true
    automatic_restart           = false
    provisioning_model          = "SPOT"
    instance_termination_action = "STOP"
  }

  network_interface {
    # A default network is created for all GCP projects
    network = "default"
    access_config {
    }
  }
}

REST

Untuk membuat VM dari Compute Engine API, gunakan metode instances.insert. Anda harus menentukan jenis dan nama mesin untuk VM. Anda juga dapat menentukan image untuk boot disk.

Untuk membuat Spot VM, Anda harus menyertakan kolom "provisioningModel": spot. Anda juga dapat menentukan tindakan penghentian untuk Spot VM dengan menyertakan juga kolom "instanceTerminationAction".

POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances
{
 "machineType": "zones/ZONE/machineTypes/MACHINE_TYPE",
 "name": "VM_NAME",
 "disks": [
   {
     "initializeParams": {
       "sourceImage": "projects/IMAGE_PROJECT/global/images/IMAGE"
     },
     "boot": true
   }
 ]
 "scheduling":
 {
     "provisioningModel": "SPOT",
     "instanceTerminationAction": "TERMINATION_ACTION"
 },
 ...
}

Ganti kode berikut:

  • PROJECT_ID: project ID dari project tempat VM dibuat.
  • ZONE: zona tempat VM dibuat. Zona tersebut juga harus mendukung jenis mesin yang akan digunakan untuk VM baru.
  • MACHINE_TYPE: jenis mesin bawaan atau kustom untuk VM baru.
  • VM_NAME: nama VM baru.
  • IMAGE_PROJECT: project yang berisi image. Misalnya, jika Anda menentukan family/debian-10 sebagai kelompok image, tentukan debian-cloud sebagai project image.
  • IMAGE: menentukan salah satu dari hal berikut:
    • Versi tertentu dari image publik. Misalnya, image tertentu adalah "sourceImage": "projects/debian-cloud/global/images/debian-10-buster-v20200309" dengan debian-cloud berupa IMAGE_PROJECT.
    • Kelompok image. Ini akan membuat VM dari OS image terbaru yang masih digunakan. Misalnya, jika Anda menentukan "sourceImage": "projects/debian-cloud/global/images/family/debian-10" dengan debian-cloud berupa IMAGE_PROJECT, Compute Engine akan membuat VM dari versi terbaru OS image di kelompok image Debian 10.
  • TERMINATION_ACTION: Opsional: tentukan tindakan yang akan diambil saat Compute Engine melakukan preempt terhadap VM, baik STOP (perilaku default) atau DELETE.

Untuk informasi selengkapnya tentang opsi yang dapat Anda tentukan saat membuat VM, lihat Membuat dan memulai instance VM.

Go


import (
	"context"
	"fmt"
	"io"

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

// createSpotInstance creates a new Spot VM instance with Debian 10 operating system.
func createSpotInstance(w io.Writer, projectID, zone, instanceName string) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"

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

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

	req := &computepb.GetFromFamilyImageRequest{
		Project: "debian-cloud",
		Family:  "debian-11",
	}

	image, err := imagesClient.GetFromFamily(ctx, req)
	if err != nil {
		return fmt.Errorf("getImageFromFamily: %w", err)
	}

	diskType := fmt.Sprintf("zones/%s/diskTypes/pd-standard", zone)
	disks := []*computepb.AttachedDisk{
		{
			AutoDelete: proto.Bool(true),
			Boot:       proto.Bool(true),
			InitializeParams: &computepb.AttachedDiskInitializeParams{
				DiskSizeGb:  proto.Int64(10),
				DiskType:    proto.String(diskType),
				SourceImage: proto.String(image.GetSelfLink()),
			},
			Type: proto.String(computepb.AttachedDisk_PERSISTENT.String()),
		},
	}

	req2 := &computepb.InsertInstanceRequest{
		Project: projectID,
		Zone:    zone,
		InstanceResource: &computepb.Instance{
			Name:        proto.String(instanceName),
			Disks:       disks,
			MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/%s", zone, "n1-standard-1")),
			NetworkInterfaces: []*computepb.NetworkInterface{
				{
					Name: proto.String("global/networks/default"),
				},
			},
			Scheduling: &computepb.Scheduling{
				ProvisioningModel: proto.String(computepb.Scheduling_SPOT.String()),
			},
		},
	}
	op, err := instancesClient.Insert(ctx, req2)
	if err != nil {
		return fmt.Errorf("insert: %w", err)
	}

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

	instance, err := instancesClient.Get(ctx, &computepb.GetInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
	})

	if err != nil {
		return fmt.Errorf("createInstance: %w", err)
	}

	fmt.Fprintf(w, "Instance created: %v\n", instance)
	return nil
}

Java


import com.google.cloud.compute.v1.AccessConfig;
import com.google.cloud.compute.v1.AccessConfig.Type;
import com.google.cloud.compute.v1.Address.NetworkTier;
import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.ImagesClient;
import com.google.cloud.compute.v1.InsertInstanceRequest;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import com.google.cloud.compute.v1.Scheduling;
import com.google.cloud.compute.v1.Scheduling.ProvisioningModel;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateSpotVm {
  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 Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Name of the virtual machine to check.
    String instanceName = "your-instance-name";
    // Name of the zone you want to use. For example: "us-west3-b"
    String zone = "your-zone";

    createSpotInstance(projectId, instanceName, zone);
  }

  // Create a new Spot VM instance with Debian 11 operating system.
  public static Instance createSpotInstance(String projectId, String instanceName, String zone)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    String image;
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (ImagesClient imagesClient = ImagesClient.create()) {
      image = imagesClient.getFromFamily("debian-cloud", "debian-11").getSelfLink();
    }
    AttachedDisk attachedDisk = buildAttachedDisk(image, zone);
    String machineTypes = String.format("zones/%s/machineTypes/%s", zone, "n1-standard-1");

    // Send an instance creation request to the Compute Engine API and wait for it to complete.
    Instance instance =
            createInstance(projectId, zone, instanceName, attachedDisk, true, machineTypes, false);

    System.out.printf("Spot instance '%s' has been created successfully", instance.getName());

    return instance;
  }

  // disks: a list of compute_v1.AttachedDisk objects describing the disks
  //     you want to attach to your new instance.
  // machine_type: machine type of the VM being created. This value uses the
  //     following format: "zones/{zone}/machineTypes/{type_name}".
  //     For example: "zones/europe-west3-c/machineTypes/f1-micro"
  // external_access: boolean flag indicating if the instance should have an external IPv4
  //     address assigned.
  // spot: boolean value indicating if the new instance should be a Spot VM or not.
  private static Instance createInstance(String projectId, String zone, String instanceName,
                                         AttachedDisk disk, boolean isSpot, String machineType,
                                         boolean externalAccess)
          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.
    try (InstancesClient client = InstancesClient.create()) {
      Instance instanceResource =
              buildInstanceResource(instanceName, disk, machineType, externalAccess, isSpot);

      InsertInstanceRequest build = InsertInstanceRequest.newBuilder()
              .setProject(projectId)
              .setRequestId(UUID.randomUUID().toString())
              .setZone(zone)
              .setInstanceResource(instanceResource)
              .build();
      client.insertCallable().futureCall(build).get(60, TimeUnit.SECONDS);

      return client.get(projectId, zone, instanceName);
    }
  }

  private static Instance buildInstanceResource(String instanceName, AttachedDisk disk,
                                                String machineType, boolean externalAccess,
                                                boolean isSpot) {
    NetworkInterface networkInterface =
            networkInterface(externalAccess);
    Instance.Builder builder = Instance.newBuilder()
            .setName(instanceName)
            .addDisks(disk)
            .setMachineType(machineType)
            .addNetworkInterfaces(networkInterface);

    if (isSpot) {
      // Set the Spot VM setting
      Scheduling.Builder scheduling = builder.getScheduling()
              .toBuilder()
              .setProvisioningModel(ProvisioningModel.SPOT.name())
              .setInstanceTerminationAction("STOP");
      builder.setScheduling(scheduling);
    }

    return builder.build();
  }

  private static NetworkInterface networkInterface(boolean externalAccess) {
    NetworkInterface.Builder build = NetworkInterface.newBuilder()
            .setNetwork("global/networks/default");

    if (externalAccess) {
      AccessConfig.Builder accessConfig = AccessConfig.newBuilder()
              .setType(Type.ONE_TO_ONE_NAT.name())
              .setName("External NAT")
              .setNetworkTier(NetworkTier.PREMIUM.name());
      build.addAccessConfigs(accessConfig.build());
    }

    return build.build();
  }

  private static AttachedDisk buildAttachedDisk(String sourceImage, String zone) {
    AttachedDiskInitializeParams initializeParams = AttachedDiskInitializeParams.newBuilder()
            .setSourceImage(sourceImage)
            .setDiskSizeGb(10)
            .setDiskType(String.format("zones/%s/diskTypes/pd-standard", zone))
            .build();
    return AttachedDisk.newBuilder()
            .setInitializeParams(initializeParams)
            // Remember to set auto_delete to True if you want the disk to be deleted
            // when you delete your VM instance.
            .setAutoDelete(true)
            .setBoot(true)
            .build();
  }
}

Python

from __future__ import annotations

import re
import sys
from typing import Any
import warnings

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


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

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

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


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

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

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


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

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

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

    Returns:
        Whatever the operation.result() returns.

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

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

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

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

    return result


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

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

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

    if internal_ip:
        network_interface.network_i_p = internal_ip

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

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

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

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

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

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

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

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

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

    operation = instance_client.insert(request=request)

    wait_for_extended_operation(operation, "instance creation")

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


def create_spot_instance(
    project_id: str, zone: str, instance_name: str
) -> compute_v1.Instance:
    """
    Create a new Spot VM instance with Debian 10 operating system.

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

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

Untuk membuat beberapa Spot VM dengan properti yang sama, Anda dapat membuat template instance, dan menggunakan template tersebut untuk membuat grup instance terkelola (MIG). Untuk mengetahui informasi selengkapnya, baca praktik terbaik.

Memulai Spot VM

Seperti VM lainnya, Spot VM dimulai saat pembuatan. Demikian pula, jika Spot VM dihentikan, Anda dapat memulai ulang VM untuk melanjutkan status RUNNING. Anda dapat menghentikan dan memulai ulang Spot VM yang di-preempt sebanyak yang Anda inginkan, selama masih ada kapasitas. Untuk mengetahui informasi selengkapnya, baca Siklus proses instance VM.

Jika Compute Engine menghentikan satu atau beberapa Spot VM di cluster grup instance terkelola (MIG) atau Google Kubernetes Engine (GKE) penskalaan otomatis, grup akan memulai ulang VM saat resource tersedia kembali.

Mengidentifikasi model penyediaan dan tindakan penghentian VM

Identifikasi model penyediaan VM untuk melihat apakah VM tersebut adalah VM standar, Spot VM, atau preemptible VM. Untuk Spot VM, Anda juga dapat mengidentifikasi tindakan penghentian. Anda dapat mengidentifikasi model penyediaan dan tindakan penghentian VM menggunakan Google Cloud Console, gcloud CLI, atau Compute Engine API.

Konsol

  1. Buka halaman VM instances.

    Buka halaman instance VM

  2. Klik Nama VM yang ingin Anda identifikasi. Halaman Detail instance VM akan terbuka.

  3. Buka bagian Pengelolaan di bagian bawah halaman. Di subbagian Kebijakan ketersediaan, periksa opsi berikut:

    • Jika model penyediaan VM disetel ke Spot, VM tersebut adalah Spot VM.
      • Saat penghentian VM menunjukkan tindakan yang harus dilakukan saat Compute Engine melakukan preempt terhadap VM, baik Menghentikan atau Menghapus VM.
    • Atau, jika model penyediaan VM disetel ke Standar atau :
      • Jika opsi Kemungkinan untuk dihentikan disetel ke Aktif, VM tersebut adalah preemptible VM.
      • Jika tidak, VM tersebut adalah VM standar.

gcloud

Untuk mendeskripsikan VM dari gcloud CLI, gunakan perintah gcloud compute instances describe:

gcloud compute instances describe VM_NAME

dengan VM_NAME adalah nama VM yang ingin Anda periksa.

Pada output, periksa kolom scheduling untuk mengidentifikasi VM:

  • Jika outputnya menyertakan kolom provisioningModel yang ditetapkan ke SPOT, seperti yang berikut ini, VM tersebut adalah Spot VM.

    ...
    scheduling:
    ...
    provisioningModel: SPOT
    instanceTerminationAction: TERMINATION_ACTION
    ...
    

    dengan TERMINATION_ACTION menunjukkan tindakan yang harus dilakukan saat Compute Engine melakukan preempt terhadap VM, baik menghentikan (STOP) atau menghapus (DELETE) VM. Jika kolom instanceTerminationAction tidak ada, nilai defaultnya adalah STOP.

  • Atau, jika output menyertakan kolom provisioningModel yang ditetapkan ke standard atau jika output menghilangkan kolom provisioningModel:

    • Jika outputnya menyertakan kolom preemptible yang ditetapkan ke true, VM tersebut adalah preemptible VM.
    • Jika tidak, VM tersebut adalah VM standar.

REST

Untuk mendeskripsikan VM dari Compute Engine API, gunakan metode instances.get:

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

Ganti kode berikut:

  • PROJECT_ID: project ID dari project tempat VM berada.
  • ZONE: zona tempat VM berada.
  • VM_NAME: nama VM yang ingin Anda periksa.

Pada output, periksa kolom scheduling untuk mengidentifikasi VM:

  • Jika outputnya menyertakan kolom provisioningModel yang ditetapkan ke SPOT, seperti yang berikut ini, VM tersebut adalah Spot VM.

    {
      ...
      "scheduling":
      {
         ...
         "provisioningModel": "SPOT",
         "instanceTerminationAction": "TERMINATION_ACTION"
         ...
      },
      ...
    }
    

    dengan TERMINATION_ACTION menunjukkan tindakan yang harus dilakukan saat Compute Engine melakukan preempt terhadap VM, baik menghentikan (STOP) atau menghapus (DELETE) VM. Jika kolom instanceTerminationAction tidak ada, nilai defaultnya adalah STOP.

  • Atau, jika output menyertakan kolom provisioningModel yang ditetapkan ke standard atau jika output menghilangkan kolom provisioningModel:

    • Jika outputnya menyertakan kolom preemptible yang ditetapkan ke true, VM tersebut adalah preemptible VM.
    • Jika tidak, VM tersebut adalah VM standar.

Go


import (
	"context"
	"fmt"
	"io"

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

// isSpotVM checks if a given instance is a Spot VM or not.
func isSpotVM(w io.Writer, projectID, zone, instanceName string) (bool, error) {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"
	ctx := context.Background()
	client, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return false, fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer client.Close()

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

	instance, err := client.Get(ctx, req)
	if err != nil {
		return false, fmt.Errorf("GetInstance: %w", err)
	}

	isSpot := instance.GetScheduling().GetProvisioningModel() == computepb.Scheduling_SPOT.String()

	var isSpotMessage string
	if !isSpot {
		isSpotMessage = " not"
	}
	fmt.Fprintf(w, "Instance %s is%s spot\n", instanceName, isSpotMessage)

	return instance.GetScheduling().GetProvisioningModel() == computepb.Scheduling_SPOT.String(), nil
}

Java


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

public class CheckIsSpotVm {
  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 Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Name of the virtual machine to check.
    String instanceName = "your-route-name";
    // Name of the zone you want to use. For example: "us-west3-b"
    String zone = "your-zone";

    boolean isSpotVm = isSpotVm(projectId, instanceName, zone);
    System.out.printf("Is %s spot VM instance - %s", instanceName, isSpotVm);
  }

  // Check if a given instance is Spot VM or not.
  public static boolean isSpotVm(String projectId, String instanceName, String zone)
          throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (InstancesClient client = InstancesClient.create()) {
      Instance instance = client.get(projectId, zone, instanceName);

      return instance.getScheduling().getProvisioningModel()
              .equals(Scheduling.ProvisioningModel.SPOT.name());
    }
  }
}

Python

from google.cloud import compute_v1


def is_spot_vm(project_id: str, zone: str, instance_name: str) -> bool:
    """
    Check if a given instance is Spot VM or not.
    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone you want to use. For example: "us-west3-b"
        instance_name: name of the virtual machine to check.
    Returns:
        The Spot VM status of the instance.
    """
    instance_client = compute_v1.InstancesClient()
    instance = instance_client.get(
        project=project_id, zone=zone, instance=instance_name
    )
    return (
        instance.scheduling.provisioning_model
        == compute_v1.Scheduling.ProvisioningModel.SPOT.name
    )

Menangani preemption dengan skrip penonaktifan

Saat Compute Engine melakukan preempt Spot VM, Anda dapat menggunakan skrip penonaktifan untuk mencoba melakukan tindakan pembersihan sebelum VM di-preempt. Misalnya, Anda dapat menghentikan dengan lancar proses yang sedang berjalan dan menyalin file checkpoint ke Cloud Storage. Secara khusus, durasi maksimum periode penonaktifan lebih singkat untuk pemberitahuan pengambilan alih daripada untuk penonaktifan yang dimulai pengguna. Untuk informasi selengkapnya tentang periode penonaktifan untuk pemberitahuan penghentian, lihat Proses penghentian dalam dokumentasi konseptual untuk Spot VM.

Berikut adalah contoh skrip penonaktifan yang dapat Anda tambahkan ke VM Spot yang sedang berjalan atau yang ditambahkan saat membuat VM Spot baru. Skrip ini berjalan saat VM mulai dinonaktifkan, sebelum perintah kill normal sistem operasi menghentikan semua proses yang tersisa. Setelah menghentikan program yang diinginkan dengan lancar, skrip akan melakukan upload paralel file checkpoint ke bucket Cloud Storage.

#!/bin/bash

MY_PROGRAM="PROGRAM_NAME" # For example, "apache2" or "nginx"
MY_USER="LOCAL_USER"
CHECKPOINT="/home/$MY_USER/checkpoint.out"
BUCKET_NAME="BUCKET_NAME" # For example, "my-checkpoint-files" (without gs://)

echo "Shutting down!  Seeing if ${MY_PROGRAM} is running."

# Find the newest copy of $MY_PROGRAM
PID="$(pgrep -n "$MY_PROGRAM")"

if [[ "$?" -ne 0 ]]; then
  echo "${MY_PROGRAM} not running, shutting down immediately."
  exit 0
fi

echo "Sending SIGINT to $PID"
kill -2 "$PID"

# Portable waitpid equivalent
while kill -0 "$PID"; do
   sleep 1
done

echo "$PID is done, copying ${CHECKPOINT} to gs://${BUCKET_NAME} as ${MY_USER}"

su "${MY_USER}" -c "gcloud storage cp $CHECKPOINT gs://${BUCKET_NAME}/"

echo "Done uploading, shutting down."

Skrip ini mengasumsikan hal berikut:

  • VM dibuat dengan setidaknya akses baca-tulis ke Cloud Storage. Untuk mengetahui petunjuk cara membuat VM dengan cakupan yang sesuai, baca dokumentasi autentikasi.

  • Anda sudah memiliki bucket Cloud Storage dan izin untuk menulis ke bucket tersebut.

Untuk menambahkan skrip ini ke VM, konfigurasikan skrip agar berfungsi dengan aplikasi di VM Anda dan tambahkan ke metadata VM.

  1. Salin atau download skrip penonaktifan:

    • Salin skrip penonaktifan sebelumnya setelah mengganti yang berikut:

      • PROGRAM_NAME adalah nama proses atau program yang ingin Anda hentikan. Misalnya apache2 atau nginx.
      • LOCAL_USER adalah nama pengguna yang Anda gunakan untuk login ke virtual machine.
      • BUCKET_NAME adalah nama bucket Cloud Storage tempat Anda ingin menyimpan file checkpoint program. Perlu diperhatikan bahwa dalam hal ini, nama bucket tidak diawali dengan gs://.
    • Download skrip penonaktifan ke workstation lokal, lalu ganti variabel berikut di file:

      • [PROGRAM_NAME] adalah nama proses atau program yang ingin Anda nonaktifkan. Misalnya apache2 atau nginx.
      • [LOCAL_USER] adalah nama pengguna yang Anda gunakan untuk login ke virtual machine.
      • [BUCKET_NAME] adalah nama bucket Cloud Storage tempat Anda ingin menyimpan file checkpoint program. Perlu diperhatikan bahwa dalam hal ini, nama bucket tidak diawali dengan gs://.
  2. Tambahkan skrip penonaktifan ke VM baru atau VM yang ada.

Mendeteksi preemption Spot VM

Tentukan apakah Spot VM di-preempt oleh Compute Engine menggunakan Konsol Google Cloud, gcloud CLI, atau Compute Engine API.

Konsol

Anda dapat memeriksa apakah VM di-preempt dengan memeriksa log aktivitas sistem.

  1. Di Konsol Google Cloud, buka halaman Log.

    Buka Log

  2. Pilih project Anda, lalu klik Lanjutkan.

  3. Tambahkan compute.instances.preempted ke kolom filter menurut label atau penelusuran teks.

  4. Anda juga dapat memasukkan nama VM jika ingin melihat operasi preemption untuk VM tertentu.

  5. Tekan enter untuk menerapkan filter yang ditentukan. Konsol Google Cloud memperbarui daftar log untuk hanya menampilkan operasi dengan VM yang di-preempt.

  6. Pilih operasi dalam daftar untuk melihat detail tentang VM yang di-preempt.

gcloud

Gunakan perintah gcloud compute operations list dengan parameter filter untuk mendapatkan daftar peristiwa preemption dalam project Anda.

gcloud compute operations list \
    --filter="operationType=compute.instances.preempted"

Secara opsional, Anda dapat menggunakan parameter filter tambahan untuk menentukan cakupan hasil lebih lanjut. Misalnya, untuk melihat peristiwa preemption hanya untuk instance dalam grup instance terkelola, gunakan perintah berikut:

gcloud compute operations list \
    --filter="operationType=compute.instances.preempted AND targetLink:instances/BASE_INSTANCE_NAME"

dengan BASE_INSTANCE_NAME adalah nama dasar yang ditentukan sebagai awalan untuk nama semua VM dalam grup instance terkelola ini.

Output-nya mirip dengan berikut ini:

NAME                  TYPE                         TARGET                                        HTTP_STATUS STATUS TIMESTAMP
systemevent-xxxxxxxx  compute.instances.preempted  us-central1-f/instances/example-instance-xxx  200         DONE   2015-04-02T12:12:10.881-07:00

Jenis operasi compute.instances.preempted menunjukkan bahwa instance VM di-preempt. Anda dapat menggunakan perintah gcloud compute operations describe untuk mendapatkan informasi lebih lanjut tentang operasi preemption tertentu.

gcloud compute operations describe SYSTEM_EVENT \
    --zone=ZONE

Ganti kode berikut:

  • SYSTEM_EVENT: peristiwa sistem dari output perintah gcloud compute operations list—misalnya, systemevent-xxxxxxxx.
  • ZONE: zona peristiwa sistem—misalnya, us-central1-f.

Outputnya mirip dengan hal berikut ini:

...
operationType: compute.instances.preempted
progress: 100
selfLink: https://compute.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f/operations/systemevent-xxxxxxxx
startTime: '2015-04-02T12:12:10.881-07:00'
status: DONE
statusMessage: Instance was preempted.
...

REST

Guna mendapatkan daftar operasi sistem terbaru untuk project dan zona tertentu, gunakan metode zoneOperations.get.

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

Ganti kode berikut:

Secara opsional, untuk mencakup respons agar hanya menampilkan operasi preemption, Anda dapat menambahkan filter ke permintaan API:

operationType="compute.instances.preempted"

Atau, guna melihat operasi preemption untuk VM tertentu, tambahkan parameter targetLink ke filter:

operationType="compute.instances.preempted" AND
targetLink="https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME

Ganti kode berikut: + PROJECT_ID: project ID. + ZONE: zona. + VM_NAME: nama VM tertentu dalam zona dan project ini.

Respons berisi daftar operasi terbaru. Misalnya, preemption terlihat serupa dengan berikut ini:

{
  "kind": "compute#operation",
  "id": "15041793718812375371",
  "name": "systemevent-xxxxxxxx",
  "zone": "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f",
  "operationType": "compute.instances.preempted",
  "targetLink": "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-f/instances/example-instance",
  "targetId": "12820389800990687210",
  "status": "DONE",
  "statusMessage": "Instance was preempted.",
  ...
}

Atau, Anda dapat menentukan apakah VM di-preempt dari dalam VM itu sendiri. Hal ini berguna jika Anda ingin menangani penonaktifan karena preemption Compute Engine yang berbeda dengan penonaktifan normal dalam skrip penonaktifan. Untuk melakukannya, cukup periksa nilai preempted di server metadata dalam metadata default VM Anda.

Misalnya, gunakan curl dari dalam VM Anda guna mendapatkan nilai untuk preempted:

curl "http://metadata.google.internal/computeMetadata/v1/instance/preempted" -H "Metadata-Flavor: Google"
TRUE

Jika nilai ini TRUE, VM akan di-preempt oleh Compute Engine. Jika tidak, VM adalah FALSE.

Jika ingin menggunakannya di luar skrip penonaktifan, Anda dapat menambahkan ?wait_for_change=true ke URL. Tindakan ini menjalankan permintaan HTTP GET yang tidak berfungsi, yang hanya ditampilkan saat metadata telah berubah dan VM telah di-preempt.

curl "http://metadata.google.internal/computeMetadata/v1/instance/preempted?wait_for_change=true" -H "Metadata-Flavor: Google"
TRUE

Cara menguji setelan preemption

Anda dapat menjalankan skenario pemeliharaan tersimulasi di VM untuk memaksanya melakukan preempt. Gunakan fitur ini untuk menguji cara aplikasi Anda menangani Spot VM. Baca Menyimulasikan skenario pemeliharaan host untuk mempelajari cara menguji skenario pemeliharaan di instance Anda.

Anda juga dapat melakukan simulasi preemption VM dengan menghentikan instance VM, yang dapat digunakan sebagai pengganti simulasi skenario pemeliharaan dan menghindari batas kuota.

Praktik terbaik

Berikut ini beberapa praktik terbaik untuk membantu Anda memaksimalkan Spot VM.

  • Menggunakan template instance. Daripada membuat Spot VM satu per satu, Anda dapat menggunakan template instance untuk membuat beberapa Spot VM dengan properti yang sama. Template instance diperlukan untuk menggunakan MIG. Atau, Anda juga dapat membuat beberapa Spot VM menggunakan Bulk Instance API.

  • Menggunakan MIG untuk mendistribusikan Spot VM secara regional dan membuat ulang Spot VM secara otomatis. Gunakan MIG untuk membuat workload di Spot VM lebih fleksibel dan tangguh. Misalnya, gunakan MIG regional untuk mendistribusikan VM di beberapa zona, yang membantu mengurangi error ketersediaan resource. Selain itu, gunakan autohealing untuk membuat ulang Spot VM secara otomatis setelah di-preempt.

  • Memilih jenis mesin yang lebih kecil. Resource untuk Spot VM berasal dari kelebihan dan pencadangan kapasitas Google Cloud. Kapasitas untuk Spot VM sering kali lebih mudah didapatkan untuk jenis mesin yang lebih kecil, yaitu jenis mesin dengan resource yang lebih sedikit, seperti vCPU dan memori. Anda mungkin menemukan lebih banyak kapasitas untuk Spot VM dengan memilih jenis mesin kustom yang lebih kecil, tetapi kapasitas bahkan lebih mungkin tersedia untuk jenis mesin yang telah ditetapkan dengan ukuran lebih kecil. Misalnya, dibandingkan dengan kapasitas untuk jenis mesin yang telah ditetapkan n2-standard-32, kapasitas untuk jenis mesin kustom n2-custom-24-96 lebih mungkin tersedia, tetapi kapasitas untuk jenis mesin yang telah ditetapkan n2-standard-16 bahkan lebih mungkin lagi tersedia.

  • Menjalankan cluster Spot VM yang besar selain masa sibuk. Muatan pada pusat data Google Cloud bervariasi menurut lokasi dan waktu, tetapi umumnya paling rendah pada malam hari dan akhir pekan. Dengan demikian, malam hari dan akhir pekan adalah waktu terbaik untuk menjalankan cluster Spot VM yang besar.

  • Mendesain aplikasi Anda agar mampu menangani fault dan preemption Anda harus siap dengan adanya perubahan pola preemption pada waktu yang berbeda. Misalnya, jika suatu zona mengalami pemadaman sebagian, sejumlah besar Spot VM dapat di-preempt untuk memberi ruang bagi VM standar yang perlu dipindahkan sebagai bagian dari pemulihan. Dalam jangka waktu yang singkat itu, tingkat preemption akan terlihat sangat berbeda dari hari lainnya. Jika aplikasi Anda berasumsi bahwa preemption selalu dilakukan dalam kelompok kecil, Anda mungkin tidak siap untuk peristiwa tersebut.

  • Mencoba lagi membuat Spot VM yang telah di-preempt. Jika Spot VM Anda telah di-preempt, coba buat Spot VM baru satu atau dua kali sebelum beralih kembali ke VM standar. Bergantung pada persyaratan Anda, sebaiknya gabungkan VM standar dan Spot VM di cluster Anda untuk memastikan bahwa pekerjaan dilakukan dengan kecepatan yang memadai.

  • Menggunakan skrip penonaktifan. Kelola pemberitahuan penonaktifan dan preemption dengan skrip penonaktifan yang dapat menyimpan progres tugas sehingga dapat melanjutkan tugas, bukan memulai dari awal.

Apa langkah selanjutnya?