Crear y usar máquinas virtuales interrumpibles


En esta página se explica cómo crear y usar una instancia de máquina virtual (VM) interrumpible. Las máquinas virtuales interrumpibles 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 detener (interrumpir temporalmente) estas VMs si necesita recuperar esos recursos para otras tareas. Las VMs interrumpibles siempre se detienen al cabo de 24 horas. Las máquinas virtuales interrumpibles solo se recomiendan para aplicaciones tolerantes a fallos que puedan gestionar la interrupción temporal de máquinas virtuales. Asegúrate de que tu aplicación pueda gestionar las suspensiones antes de crear una VM preemptible. Para conocer los riesgos y el valor de las VMs interrumpibles, consulta la documentación sobre instancias de VMs interrumpibles.

Antes de empezar

  • Consulta la documentación sobre instancias de máquinas virtuales no garantizadas.
  • 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.

    Go

    Para usar las Go muestras 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.

    Java

    Para usar las Java muestras 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.

    Node.js

    Para usar las Node.js muestras 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.

    Python

    Para usar las Python muestras 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 no garantizada

Crea una máquina virtual interrumpible con la CLI de gcloud o la API de Compute Engine. Para usar la consolaGoogle Cloud , crea una máquina virtual de acceso puntual.

gcloud

Con gcloud compute, usa el mismo comando instances create que usarías para crear una VM normal, pero añade la marca --preemptible.

gcloud compute instances create [VM_NAME] --preemptible

donde [VM_NAME] es el nombre de la VM.

Go

import (
	"context"
	"fmt"
	"io"

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

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

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

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

	// List of public operating system (OS) images:
	// https://cloud.google.com/compute/docs/images/os-details.
	newestDebianReq := &computepb.GetFromFamilyImageRequest{
		Project: "debian-cloud",
		Family:  "debian-11",
	}
	newestDebian, err := imagesClient.GetFromFamily(ctx, newestDebianReq)
	if err != nil {
		return fmt.Errorf("unable to get image from family: %w", err)
	}

	inst := &computepb.Instance{
		Name: proto.String(instanceName),
		Disks: []*computepb.AttachedDisk{
			{
				InitializeParams: &computepb.AttachedDiskInitializeParams{
					DiskSizeGb:  proto.Int64(10),
					SourceImage: newestDebian.SelfLink,
					DiskType:    proto.String(fmt.Sprintf("zones/%s/diskTypes/pd-standard", zone)),
				},
				AutoDelete: proto.Bool(true),
				Boot:       proto.Bool(true),
			},
		},
		Scheduling: &computepb.Scheduling{
			// Set the preemptible setting
			Preemptible: proto.Bool(true),
		},
		MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/n1-standard-1", zone)),
		NetworkInterfaces: []*computepb.NetworkInterface{
			{
				Name: proto.String("global/networks/default"),
			},
		},
	}

	req := &computepb.InsertInstanceRequest{
		Project:          projectID,
		Zone:             zone,
		InstanceResource: inst,
	}

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

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

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

	return nil
}

Java


import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
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.Operation;
import com.google.cloud.compute.v1.Scheduling;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreatePreemptibleInstance {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // projectId: 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”
    // instanceName: name of the new virtual machine.
    String projectId = "your-project-id-or-number";
    String zone = "zone-name";
    String instanceName = "instance-name";

    createPremptibleInstance(projectId, zone, instanceName);
  }

  // Send an instance creation request with preemptible settings to the Compute Engine API
  // and wait for it to complete.
  public static void createPremptibleInstance(String projectId, String zone, String instanceName)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {

    String machineType = String.format("zones/%s/machineTypes/e2-small", zone);
    String sourceImage = "projects/debian-cloud/global/images/family/debian-11";
    long diskSizeGb = 10L;
    String networkName = "default";

    try (InstInstancesClienttancesClient = InstInstancesClientate()) {

      AttaAttachedDiskk =
          AttaAttachedDiskBuilder()
              .setBoot(true)
              .setAutoDelete(true)
              .setType(AttaAttachedDiske.PERSISTENT.toString())
              .setIsetInitializeParams                // Describe the size and source image of the boot disk to attach to the instance.
                  AttaAttachedDiskInitializeParamsBuilder()
                      .setSourceImage(sourceImage)
                      .setDiskSizeGb(diskSizeGb)
                      .build())
              .build();

      // Use the default VPC network.
      NetwNetworkInterfaceworkInterface = NetwNetworkInterfaceBuilder()
          .setName(networkName)
          .build();

      // Collect information into the Instance object.
      InstInstancetanceResource =
          InstInstanceBuilder()
              .setName(instanceName)
              .setMachineType(machineType)
              .addDisks(disk)
              .addNetworkInterfaces(networkInterface)
              // Set the preemptible setting.
              .setScheduling(ScheSchedulingBuilder()
                  .setPsetPreemptiblee)
                  .build())
              .build();

      System.out.printf("Creating instance: %s at %s %n", instanceName, zone);

      // Prepare the request to insert an instance.
      InseInsertInstanceRequestertInstanceRequest = InseInsertInstanceRequestBuilder()
          .setProject(projectId)
          .setZone(zone)
          .setInstanceResource(instanceResource)
          .build();

      // Wait for the create operation to complete.
      OperOperationponse = instancesClient.insertAsync(insertInstanceRequest)
          .get(3, TimeUnit.MINUTES);
      ;

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

      System.out.printf("Instance created : %s\n", instanceName);
      System.out.println("Operation Status: " + respresponse.getStatus()   }
  }
}

Node.js

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const instanceName = 'YOUR_INSTANCE_NAME';

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

async function createPreemptible() {
  const instancesClient = new compute.InstancesClient();

  const [response] = await instancesClient.insert({
    instanceResource: {
      name: instanceName,
      disks: [
        {
          initializeParams: {
            diskSizeGb: '64',
            sourceImage:
              'projects/debian-cloud/global/images/family/debian-11/',
          },
          autoDelete: true,
          boot: true,
        },
      ],
      scheduling: {
        // Set the preemptible setting
        preemptible: true,
      },
      machineType: `zones/${zone}/machineTypes/e2-small`,
      networkInterfaces: [
        {
          name: 'global/networks/default',
        },
      ],
    },
    project: projectId,
    zone,
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.ZoneOperationsClient();

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

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

createPreemptible();

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_preemptible_instance(
    project_id: str, zone: str, instance_name: str
) -> compute_v1.Instance:
    """
    Create a new preemptible 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, preemptible=True)
    return instance

REST

En la API, crea una solicitud normal para crear una VM, pero incluye la propiedad preemptible en scheduling y asigna el valor true. Por ejemplo:

POST https://compute.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances

{
  'machineType': 'zones/[ZONE]/machineTypes/[MACHINE_TYPE]',
  'name': '[INSTANCE_NAME]',
  'scheduling':
  {
    'preemptible': true
  },
  ...
}

Cuotas de CPUs prioritarias

Las máquinas virtuales interrumpibles requieren cuotas de CPU disponibles, al igual que las máquinas virtuales estándar. Para evitar que las VMs no garantizadas consuman las cuotas de CPU de tus VMs estándar, puedes solicitar una cuota especial de "CPU no garantizada". Cuando Compute Engine te conceda la cuota de CPUs interrumpibles en esa región, todas las VMs interrumpibles se contabilizarán en esa cuota y todas las VMs estándar seguirán contabilizándose en la cuota de CPUs estándar.

En las regiones en las que no tengas cuota de CPU interrumpible, puedes usar la cuota de CPU estándar para lanzar máquinas virtuales interrumpibles. También necesitas suficiente cuota de IP y de disco, como de costumbre. La cuota de CPUs interrumpibles no se puede ver en la CLI de gcloud ni en las páginas de cuota de la consolaGoogle Cloud , a menos que Compute Engine haya concedido la cuota.

Para obtener más información sobre las cuotas, consulta la página Cuotas de recursos.

Iniciar una VM interrumpida

Al igual que cualquier otra VM, si una VM interrumpible se detiene o se interrumpe, puedes iniciarla de nuevo y volver a ponerla en el estado RUNNING. Al iniciar una VM interrumpible, se reinicia el contador de 24 horas, pero, como sigue siendo una VM interrumpible, Compute Engine puede interrumpirla antes de que transcurran 24 horas. No se puede convertir una VM interrumpible en una VM estándar mientras está en ejecución.

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

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

Cuando Compute Engine expulsa una VM, puedes usar una secuencia de comandos de apagado para intentar realizar acciones de limpieza antes de que se expulse la VM. 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 inactividad de una notificación de desalojo, consulta la sección Proceso de desalojo de la documentación conceptual.

A continuación, se muestra un script de apagado que puedes añadir a una VM preemptible en ejecución o a una nueva VM preemptible cuando la crees. 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_USERNAME]"
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."

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 en tu estación de trabajo local.
  2. Abre el archivo para editarlo y cambia las siguientes variables:
    • [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://.
  3. Guarda los cambios.
  4. Añade la secuencia de comandos de apagado a una VM nueva o a una VM que ya tengas.

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. Consulta la documentación de autenticación para obtener instrucciones sobre cómo crear una VM con los permisos adecuados.

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

Identificar máquinas virtuales interrumpibles

Para comprobar si una VM es una VM interrumpible, sigue los pasos para identificar el modelo de aprovisionamiento y la acción de finalización de una VM.

Determinar si se ha interrumpido una VM

Determina si una VM se ha retirado con laGoogle Cloud console, la CLI de gcloud o la API.

Consola

Puedes comprobar si se ha retirado una VM consultando 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"

Puedes usar el parámetro de filtro para acotar aún más los resultados. Por ejemplo, para ver solo los eventos de desalojo de las VMs de un grupo de instancias gestionado, haz lo siguiente:

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

gcloud devuelve una respuesta similar a la siguiente:

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

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

gcloud compute operations describe \
    systemevent-xxxxxxxx

gcloud devuelve una respuesta similar a la siguiente:

...
operationType: compute.instances.preempted
progress: 100
selfLink: https://compute.googleapis.com/compute/v1/projects/[PROJECT_ID]/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, envía una solicitud GET al URI de las operaciones de la zona.

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

La respuesta contiene una lista de operaciones recientes.

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

Para acotar la respuesta y mostrar solo las operaciones de preferencia, puedes añadir un filtro a tu solicitud a la API: operationType="compute.instances.preempted". Para ver las operaciones de desalojo de una VM específica, añade el parámetro targetLink al filtro: operationType="compute.instances.preempted" AND targetLink="https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[VM_NAME]".

También puedes determinar si se ha retirado 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 de instancia 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 un script 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 VMs para forzar su interrupción. Usa esta función para probar cómo gestionan tus aplicaciones las máquinas virtuales interrumpibles. Consulta cómo probar tus políticas de disponibilidad para saber cómo probar los eventos de mantenimiento en tus VMs.

También puedes simular la interrupción de una máquina virtual deteniéndola. Esta opción se puede usar en lugar de simular un evento de mantenimiento y no tiene límites de cuota.

Prácticas recomendadas

A continuación, te ofrecemos algunas prácticas recomendadas para sacar el máximo partido a las instancias de VM preemptivas.

Usar la API de instancias en bloque

En lugar de crear VMs individuales, puedes usar la API de instancias en bloque.

Elige formas de máquina más pequeñas

Los recursos de las VMs interrumpibles proceden del exceso y de la capacidad de copia de seguridad. Google Cloud Suele ser más fácil obtener capacidad para los tipos de máquinas más pequeños, es decir, los tipos de máquinas con menos recursos, como vCPUs y memoria. Si seleccionas un tipo de máquina personalizada más pequeño, es posible que encuentres más capacidad para las máquinas virtuales no garantizadas, pero es aún más probable que haya capacidad para los tipos de máquinas 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 de máquinas virtuales interrumpibles grandes 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 clústeres de máquinas virtuales interrumpibles grandes.

Diseña tus aplicaciones para que toleren fallos e 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 preemptivas 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 sería muy diferente a la 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. Puedes probar el comportamiento de tu aplicación en un evento de expropiación deteniendo la instancia de VM.

Volver a crear VMs que se han interrumpido

Si tu instancia de VM se ha interrumpido, prueba a crear VMs interrumpibles una o dos veces antes de volver a las VMs estándar. En función de tus requisitos, puede que te convenga combinar VMs estándar y VMs preemptivas en tus clústeres para asegurarte de que el trabajo se lleva a cabo a un ritmo adecuado.

Usar 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