Crear y usar máquinas virtuales de Spot


En esta página se explica cómo crear y gestionar máquinas virtuales de Spot, entre otras cosas:

  • Cómo crear, iniciar e identificar máquinas virtuales de Spot
  • Cómo detectar, gestionar y probar la interrupción de máquinas virtuales de acceso puntual
  • Prácticas recomendadas para las VMs de acceso puntual

Las VMs de acceso puntual son instancias de máquina virtual (VM) con el modelo de aprovisionamiento de acceso puntual. Las máquinas virtuales de acceso puntual están disponibles con un descuento de hasta el 60-91% en comparación con el precio de las máquinas virtuales estándar. Sin embargo, Compute Engine puede reclamar los recursos interrumpiendo las VMs de acceso puntual en cualquier momento. Las máquinas virtuales de acceso puntual solo se recomiendan para aplicaciones tolerantes a fallos que puedan soportar la interrupción temporal de máquinas virtuales. Asegúrate de que tu aplicación pueda gestionar la expropiación antes de decidirte a crear VMs Spot.

Antes de empezar

  • Lee la documentación conceptual sobre las VMs de acceso puntual:
    • Consulta las limitaciones y los precios de las VMs de acceso puntual.
    • Para evitar que las máquinas virtuales Spot consuman tus cuotas de CPUs, GPUs y discos de máquinas virtuales estándar, considera solicitar cuota interrumpible para las máquinas virtuales Spot.
  • Si aún no lo has hecho, configura la autenticación. La autenticación verifica tu identidad para acceder a Google Cloud servicios y APIs. Para ejecutar código o ejemplos desde un entorno de desarrollo local, puedes autenticarte en Compute Engine seleccionando una de las siguientes opciones:

    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. Instala Google Cloud CLI. Después de la instalación, inicializa la CLI de Google Cloud ejecutando el siguiente comando:

      gcloud init

      Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

    2. Set a default region and zone.

    Terraform

    Para usar las muestras de Terraform de esta página en un entorno de desarrollo local, instala e inicializa la CLI de gcloud y, a continuación, configura las credenciales predeterminadas de la aplicación con tus credenciales de usuario.

      Instala Google Cloud CLI.

      Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

      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.

      If an authentication error is returned, and you are using an external identity provider (IdP), confirm that you have signed in to the gcloud CLI with your federated identity.

    Para obtener más información, consulta Set up authentication for a local development environment.

    REST

    Para usar las muestras de la API REST de esta página en un entorno de desarrollo local, debes usar las credenciales que proporciones a la CLI de gcloud.

      Instala Google Cloud CLI.

      Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

    Para obtener más información, consulta el artículo Autenticarse para usar REST de la documentación sobre autenticación de Google Cloud .

Crear una VM de Spot

Crea una máquina virtual de Spot con la Google Cloud consola, la CLI de gcloud o la API de Compute Engine. Una VM de acceso puntual es cualquier VM configurada para usar el modelo de aprovisionamiento de acceso puntual:

  • Modelo de aprovisionamiento de la VM definido como Spot en la Google Cloud consola
  • --provisioning-model=SPOT en la CLI de gcloud
  • "provisioningModel": "SPOT" en la API de Compute Engine

Consola

  1. En la Google Cloud consola, ve a la página Crear una instancia.

    Ir a Crear una instancia

  2. En el menú de navegación, haz clic en Opciones avanzadas. En el panel Avanzado que aparece, sigue estos pasos:

    1. En la sección Modelo de aprovisionamiento, seleccione Spot en la lista Modelo de aprovisionamiento de la VM.
    2. Opcional: Para seleccionar la acción de finalización que se produce cuando Compute Engine expulsa la VM, sigue estos pasos:

      1. Despliega la sección Configuración avanzada del modelo de aprovisionamiento de la VM.
      2. En la lista Al finalizar la VM, selecciona una de las siguientes opciones:
        • Para detener la VM durante la expropiación, selecciona Detener (opción predeterminada).
        • Para eliminar la VM durante la expropiación, selecciona Eliminar.
  3. Opcional: Especifica otras opciones de configuración. Para obtener más información, consulta Opciones de configuración durante la creación de instancias.

  4. Para crear e iniciar la VM, haz clic en Crear.

gcloud

Para crear una VM desde la CLI de gcloud, usa el comando gcloud compute instances create. Para crear máquinas virtuales de acceso puntual, debes incluir la marca --provisioning-model=SPOT. También puedes especificar una acción de finalización para las VMs de Spot incluyendo la marca --instance-termination-action.

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

Haz los cambios siguientes:

  • VM_NAME: name de la nueva VM.
  • TERMINATION_ACTION: Opcional: especifica qué acción se debe llevar a cabo cuando Compute Engine desaloje la VM. Puede ser STOP (comportamiento predeterminado) o DELETE.

Para obtener más información sobre las opciones que puedes especificar al crear una VM, consulta Opciones de configuración durante la creación de instancias. Por ejemplo, para crear Spot VMs con un tipo de máquina e imagen específicos, usa el siguiente comando:

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

Haz los cambios siguientes:

  • VM_NAME: name de la nueva VM.
  • IMAGE: especifica una de las siguientes opciones:
    • IMAGE: una versión específica de una imagen pública o la familia de imágenes. Por ejemplo, una imagen concreta es --image=debian-10-buster-v20200309.
    • Una familia de imágenes. De esta forma, se crea la VM a partir de la imagen de SO más reciente que no esté obsoleta. Por ejemplo, si especificas --image-family=debian-10, Compute Engine crea una VM a partir de la versión más reciente de la imagen del SO de la familia de imágenes Debian 10.
  • IMAGE_PROJECT: el proyecto que contiene la imagen. Por ejemplo, si especifica debian-10 como familia de imágenes, especifique debian-cloud como proyecto de imágenes.
  • MACHINE_TYPE: el tipo de máquina predefinido o personalizado de la nueva VM.
  • TERMINATION_ACTION: Opcional: especifica qué acción se debe llevar a cabo cuando Compute Engine desaloje la VM. Puede ser STOP (comportamiento predeterminado) o DELETE.

    Para obtener una lista de los tipos de máquinas disponibles en una zona, usa el comando gcloud compute machine-types list con la marca --zones.

Terraform

Puedes usar un recurso de Terraform para crear una instancia de spot con un bloque de programación.


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

Para crear una VM a partir de la API de Compute Engine, usa el método instances.insert. Debes especificar un tipo de máquina y un nombre para la VM. También puedes especificar una imagen para el disco de arranque.

Para crear máquinas virtuales de Spot, debes incluir el campo "provisioningModel": spot. También puedes especificar una acción de finalización para las VMs de Spot incluyendo el campo "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"
 },
 ...
}

Haz los cambios siguientes:

  • PROJECT_ID: el ID de proyecto del proyecto en el que se creará la VM.
  • ZONE: la zona en la que se creará la VM. La zona también debe admitir el tipo de máquina que se va a usar en la nueva VM.
  • MACHINE_TYPE: el tipo de máquina predefinido o personalizado de la nueva VM.
  • VM_NAME: el nombre de la nueva VM.
  • IMAGE_PROJECT: el proyecto que contiene la imagen. Por ejemplo, si especifica family/debian-10 como familia de imágenes, especifique debian-cloud como proyecto de imágenes.
  • IMAGE: especifica una de las siguientes opciones:
    • Una versión específica de una imagen pública. Por ejemplo, una imagen específica es "sourceImage": "projects/debian-cloud/global/images/debian-10-buster-v20200309" donde debian-cloud es el IMAGE_PROJECT.
    • Una familia de imágenes. De esta forma, se crea la VM a partir de la imagen de SO más reciente que no esté obsoleta. Por ejemplo, si especificas "sourceImage": "projects/debian-cloud/global/images/family/debian-10" donde debian-cloud es IMAGE_PROJECT, Compute Engine crea una VM a partir de la versión más reciente de la imagen del SO de la familia de imágenes de Debian 10.
  • TERMINATION_ACTION: Opcional: especifica qué acción se debe llevar a cabo cuando Compute Engine desaloje la VM. Puede ser STOP (comportamiento predeterminado) o DELETE.

Para obtener más información sobre las opciones que puedes especificar al crear una VM, consulta Opciones de configuración durante la creación de instancias.

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

Para crear varias Spot VMs con las mismas propiedades, puedes crear una plantilla de instancia y usarla para crear un grupo de instancias gestionado (MIG). Para obtener más información, consulta las prácticas recomendadas.

Iniciar VMs de acceso puntual

Al igual que otras VMs, las VMs de acceso puntual se inician al crearse. Del mismo modo, si las VMs de instancia de spot se detienen, puedes reiniciarlas para que vuelvan al estado RUNNING. Puedes detener y reiniciar las máquinas virtuales de acceso puntual interrumpidas tantas veces como quieras, siempre que haya capacidad. Para obtener más información, consulta el artículo sobre el ciclo de vida de las instancias de máquina virtual.

Si Compute Engine detiene una o varias VMs de Spot en un grupo de instancias administrado (MIG) con autoescalado o en un clúster de Google Kubernetes Engine (GKE), el grupo reinicia las VMs cuando los recursos vuelven a estar disponibles.

Identificar el modelo de aprovisionamiento y la acción de finalización de una VM

Identifica el modelo de aprovisionamiento de una VM para saber si es una VM estándar, una VM de acceso puntual o una VM interrumpible. En el caso de una máquina virtual de acceso puntual, también puedes identificar la acción de finalización. Puede identificar el modelo de aprovisionamiento y la acción de finalización de una VM mediante laGoogle Cloud consola, la CLI de gcloud o la API de Compute Engine.

Consola

  1. Ve a la página Instancias de VM.

    Ve a la página Instancias de VM.

  2. Haz clic en el nombre de la máquina virtual que quieras identificar. Se abrirá la página Detalles de la instancia de VM.

  3. Ve a la sección Gestión, en la parte inferior de la página. En la subsección Políticas de disponibilidad, marque las siguientes opciones:

    • Si el modelo de aprovisionamiento de la VM es Spot, la VM es una Spot VM.
      • Al cancelar la VM: indica qué acción se debe llevar a cabo cuando Compute Engine cancele la VM, ya sea Detener o Eliminar la VM.
    • De lo contrario, si el modelo de aprovisionamiento de VMs se define como Estándar o :
      • Si la opción Interrumpibilidad está activada (), la VM es una VM interrumpible.
      • De lo contrario, la VM será una VM estándar.

gcloud

Para describir una VM desde gcloud CLI, usa el comando gcloud compute instances describe:

gcloud compute instances describe VM_NAME

donde VM_NAME es el nombre de la máquina virtual que quieres comprobar.

En el resultado, comprueba el campo scheduling para identificar la VM:

  • Si el resultado incluye el campo provisioningModel con el valor SPOT, como en el siguiente ejemplo, la VM es una VM spot.

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

    donde TERMINATION_ACTION indica la acción que se debe llevar a cabo cuando Compute Engine interrumpe temporalmente la VM: detener (STOP) o eliminar (DELETE) la VM. Si falta el campo instanceTerminationAction, el valor predeterminado es STOP.

  • De lo contrario, si el resultado incluye el campo provisioningModel con el valor standard o si el resultado omite el campo provisioningModel:

    • Si el resultado incluye el campo preemptible con el valor true, la VM es una VM interrumpible.
    • De lo contrario, la VM será una VM estándar.

REST

Para describir una VM desde la API de Compute Engine, usa el método instances.get:

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

Haz los cambios siguientes:

  • PROJECT_ID: el ID de proyecto del proyecto en el que se encuentra la VM.
  • ZONE: la zona en la que se encuentra la VM.
  • VM_NAME: el nombre de la VM que quieras comprobar.

En el resultado, comprueba el campo scheduling para identificar la VM:

  • Si el resultado incluye el campo provisioningModel con el valor SPOT, como en el siguiente ejemplo, la VM es una VM spot.

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

    donde TERMINATION_ACTION indica la acción que se debe llevar a cabo cuando Compute Engine interrumpe temporalmente la VM: detener (STOP) o eliminar (DELETE) la VM. Si falta el campo instanceTerminationAction, el valor predeterminado es STOP.

  • De lo contrario, si el resultado incluye el campo provisioningModel con el valor standard o si el resultado omite el campo provisioningModel:

    • Si el resultado incluye el campo preemptible con el valor true, la VM es una VM interrumpible.
    • De lo contrario, la VM será una VM estándar.

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
    )

Gestionar la interrupción de una máquina virtual de acceso puntual

Para saber cómo gestionar la expropiación de una VM de acceso puntual, consulta las siguientes secciones:

Gestionar la expropiación con una secuencia de comandos de apagado

Cuando Compute Engine interrumpe temporalmente una máquina virtual de acceso puntual, puedes usar una secuencia de comandos de cierre para intentar realizar acciones de limpieza antes de que se interrumpa temporalmente la máquina virtual. Por ejemplo, puedes detener correctamente un proceso en ejecución y copiar un archivo de punto de control en Cloud Storage. Cabe destacar que la duración máxima del periodo de apagado es menor en el caso de un aviso de desalojo que en el de un apagado iniciado por el usuario. Para obtener más información sobre el periodo de apagado de una notificación de desalojo, consulta el artículo Proceso de desalojo de la documentación conceptual sobre las VMs de acceso puntual.

A continuación, se muestra un ejemplo de un script de apagado que puedes añadir a una VM de Spot en ejecución o añadir al crear una VM de Spot. Esta secuencia de comandos se ejecuta cuando la VM empieza a apagarse, antes de que el comando kill normal del sistema operativo detenga todos los procesos restantes. Después de detener correctamente el programa deseado, la secuencia de comandos sube en paralelo un archivo de punto de control a un segmento de 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."

En esta secuencia de comandos se da por hecho lo siguiente:

  • La VM se ha creado con al menos acceso de lectura y escritura a Cloud Storage. Para obtener instrucciones sobre cómo crear una VM con los ámbitos adecuados, consulta la documentación de autenticación.

  • Tienes un segmento de Cloud Storage y permiso para escribir en él.

Para añadir esta secuencia de comandos a una VM, configúrela para que funcione con una aplicación en su VM y añádala a los metadatos de la VM.

  1. Copia o descarga la secuencia de comandos de apagado:

    • Copia la secuencia de comandos de apagado anterior después de sustituir lo siguiente:

      • PROGRAM_NAME es el nombre del proceso o del programa que quieres cerrar. Por ejemplo, apache2 o nginx.
      • LOCAL_USER es el nombre de usuario con el que has iniciado sesión en la máquina virtual.
      • BUCKET_NAME es el nombre del segmento de Cloud Storage en el que quieres guardar el archivo de punto de control del programa. Ten en cuenta que, en este caso, el nombre del segmento no empieza por gs://.
    • Descarga la secuencia de comandos de apagado en tu estación de trabajo local y, a continuación, sustituye las siguientes variables en el archivo:

      • [PROGRAM_NAME] es el nombre del proceso o programa que quieres cerrar. Por ejemplo, apache2 o nginx.
      • [LOCAL_USER] es el nombre de usuario con el que has iniciado sesión en la máquina virtual.
      • [BUCKET_NAME] es el nombre del segmento de Cloud Storage en el que quieres guardar el archivo de punto de control del programa. Ten en cuenta que, en este caso, el nombre del segmento no empieza por gs://.
  2. Añade la secuencia de comandos de apagado a una VM nueva o a una VM que ya tengas.

Detectar la expropiación de máquinas virtuales de acceso puntual

Determina si Compute Engine ha desalojado las VMs de Spot mediante la Google Cloud console, CLI de gcloud o la API de Compute Engine.

Consola

Para comprobar si se ha retirado una VM, consulta los registros de actividad del sistema.

  1. En la Google Cloud consola, ve a la página Registros.

    Ir a Registros

  2. Selecciona el proyecto y haz clic en Continuar.

  3. Añade compute.instances.preempted al campo Filtrar por etiqueta o buscar texto.

  4. También puedes introducir el nombre de una VM si quieres ver las operaciones de desalojo de una VM específica.

  5. Pulsa Intro para aplicar los filtros especificados. La Google Cloud consola actualiza la lista de registros para mostrar solo las operaciones en las que se ha retirado una VM.

  6. Selecciona una operación de la lista para ver los detalles de la VM que se ha retirado.

gcloud

Usa el comando gcloud compute operations list con un parámetro filter para obtener una lista de eventos de preferencia en tu proyecto.

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

Si quiere, puede usar parámetros de filtro adicionales para acotar aún más los resultados. Por ejemplo, para ver solo los eventos de desalojo de las instancias de un grupo de instancias gestionado, usa el siguiente comando:

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

donde BASE_INSTANCE_NAME es el nombre base especificado como prefijo de los nombres de todas las VMs de este grupo de instancias gestionado.

El resultado debería ser similar al siguiente:

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

El tipo de operación compute.instances.preempted indica que la instancia de VM se ha desalojado. Puedes usar el comando gcloud compute operations describe para obtener más información sobre una operación de desalojo específica.

gcloud compute operations describe SYSTEM_EVENT \
    --zone=ZONE

Haz los cambios siguientes:

  • SYSTEM_EVENT: el evento del sistema de la salida del comando gcloud compute operations list. Por ejemplo, systemevent-xxxxxxxx.
  • ZONE: la zona del evento del sistema (por ejemplo, us-central1-f).

El resultado debería ser similar al siguiente:

...
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

Para obtener una lista de las operaciones recientes del sistema de un proyecto y una zona concretos, usa el método zoneOperations.get.

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

Haz los cambios siguientes:

Si quieres acotar la respuesta para que solo se muestren las operaciones de preferencia, puedes añadir un filtro a tu solicitud de la API:

operationType="compute.instances.preempted"

También puedes añadir un parámetro targetLink al filtro para ver las operaciones de preferencia de una VM específica:

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

Sustituye lo siguiente: + PROJECT_ID: el ID del proyecto. + ZONE: la zona. + VM_NAME: el nombre de una máquina virtual específica de esta zona y este proyecto.

La respuesta contiene una lista de operaciones recientes. Por ejemplo, una expropiación se parece a lo siguiente:

{
  "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.",
  ...
}

También puedes determinar si se ha desalojado una VM desde la propia VM. Esto es útil si quieres gestionar un cierre debido a una suspensión de Compute Engine de forma diferente a un cierre normal en una secuencia de comandos de cierre. Para ello, comprueba el valor preempted en los metadatos predeterminados de tu VM.

Por ejemplo, usa curl en tu máquina virtual para obtener el valor de preempted:

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

Si este valor es TRUE, Compute Engine ha desalojado la máquina virtual; de lo contrario, es FALSE.

Si quieres usarlo fuera de una secuencia de comandos de cierre, puedes añadir ?wait_for_change=true a la URL. Envía una solicitud HTTP GET pendiente que solo devuelve un valor cuando los metadatos han cambiado y la máquina virtual se ha retirado.

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

Probar la configuración de la preferencia

Puedes ejecutar eventos simulados de mantenimiento en tus máquinas virtuales para forzarlas a que se interrumpan. Usa esta función para probar cómo gestionan tus aplicaciones las VMs de spot. Consulta Simular un evento de mantenimiento del host para saber cómo probar eventos de mantenimiento en tus instancias.

Prácticas recomendadas

A continuación, se incluyen algunas prácticas recomendadas para sacar el máximo partido a las VMs esporádicas.

  • Usa plantillas de instancia. En lugar de crear VMs de Spot de una en una, puedes usar plantillas de instancia para crear varias VMs de Spot con las mismas propiedades. Para usar los MIGs, es necesario indicar plantillas de instancia. También puedes crear varias VMs de Spot con la API de instancias en bloque.

  • Usa MIGs para distribuir regionalmente y recrear automáticamente las Spot VMs. Usa grupos de instancias gestionados para que las cargas de trabajo de las máquinas virtuales de acceso puntual sean más flexibles y resilientes. Por ejemplo, usa MIGs regionales para distribuir las VMs en varias zonas, lo que ayuda a mitigar los errores de disponibilidad de recursos. Además, puedes usar la autorreparación para recrear automáticamente las VMs de Spot después de que se hayan interrumpido.

  • Elige tipos de máquinas más pequeños. Los recursos de las VMs de acceso puntual proceden de la capacidad Google Cloud excedente y de reserva. A menudo, es más fácil obtener capacidad para las VMs de Spot con tipos de máquinas más pequeños, es decir, tipos de máquinas con menos recursos, como vCPUs y memoria. Es posible que encuentres más capacidad para las máquinas virtuales de Spot si seleccionas un tipo de máquina personalizado más pequeño, pero es aún más probable que haya capacidad para los tipos de máquina predefinidos más pequeños. Por ejemplo, en comparación con la capacidad del n2-standard-32 tipo de máquina predefinido, es más probable que haya capacidad para el n2-custom-24-96 tipo de máquina personalizada, pero es aún más probable que haya capacidad para el n2-standard-16 tipo de máquina predefinido.

  • Ejecutar clústeres grandes de máquinas virtuales de acceso puntual durante las horas de menor actividad. La carga de los centros de datos de Google Cloud varía en función de la ubicación y la hora del día, pero suele ser menor por las noches y los fines de semana. Por lo tanto, las noches y los fines de semana son los mejores momentos para ejecutar grandes clústeres de VMs de spot.

  • Diseña tus aplicaciones para que sean tolerantes a fallos y a interrupciones. Es importante estar preparado para los cambios en los patrones de preferencia en diferentes momentos. Por ejemplo, si una zona sufre una interrupción parcial, se pueden desalojar un gran número de VMs de Spot para dejar espacio a las VMs estándar que deban moverse como parte de la recuperación. En ese breve periodo, la tasa de preferencia tendría un aspecto muy diferente al de cualquier otro día. Si tu aplicación da por hecho que las apropiaciones siempre se realizan en grupos pequeños, es posible que no esté preparada para este tipo de eventos.

  • Vuelve a intentar crear máquinas virtuales de acceso puntual que se hayan interrumpido. Si tus VMs de acceso puntual se han interrumpido, prueba a crear VMs de acceso puntual una o dos veces antes de volver a las VMs estándar. En función de tus requisitos, puede que sea una buena idea combinar máquinas virtuales estándar y máquinas virtuales Spot en tus clústeres para asegurarte de que el trabajo se lleva a cabo a un ritmo adecuado.

  • Usa secuencias de comandos de apagado. Gestiona las notificaciones de apagado y de preferencia con un script de apagado que pueda guardar el progreso de un trabajo para que pueda continuar donde lo dejó, en lugar de empezar de cero.

Siguientes pasos