Crie e use VMs preemptivas


Esta página explica como criar e usar uma instância de máquina virtual (VM) preemptiva. As VMs preemptivas estão disponíveis com descontos de até 60-91% em comparação ao preço das VMs padrão. No entanto, o Compute Engine poderá interromper (preemptar) essas VMs se precisar recuperar esses recursos para outras tarefas. As VMs preemptivas sempre param após 24 horas. As VMs preemptivas são recomendadas apenas para aplicativos tolerantes a falhas que podem suportar a preempção da VM. Certifique-se de que seu aplicativo possa lidar com preempções antes de decidir criar uma VM preemptiva. Para compreender os riscos e o valor das VMs preemptivas, leia a documentação das instâncias de VM preemptivas .

Antes de começar

Crie uma VM preemptiva

Crie uma VM preemptiva usando a CLI gcloud ou a API Compute Engine. Para usar o console do Google Cloud, crie uma VM Spot .

gcloud

Com gcloud compute , use o mesmo comando instances create que você usaria para criar uma VM normal, mas adicione a sinalização --preemptible .

gcloud compute instances create [VM_NAME] --preemptible

onde [VM_NAME] é o nome da VM.

Ir

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 (InstancesClient instancesClient = InstancesClient.create()) {

      AttachedDisk disk =
          AttachedDisk.newBuilder()
              .setBoot(true)
              .setAutoDelete(true)
              .setType(AttachedDisk.Type.PERSISTENT.toString())
              .setInitializeParams(
                  // Describe the size and source image of the boot disk to attach to the instance.
                  AttachedDiskInitializeParams.newBuilder()
                      .setSourceImage(sourceImage)
                      .setDiskSizeGb(diskSizeGb)
                      .build())
              .build();

      // Use the default VPC network.
      NetworkInterface networkInterface = NetworkInterface.newBuilder()
          .setName(networkName)
          .build();

      // Collect information into the Instance object.
      Instance instanceResource =
          Instance.newBuilder()
              .setName(instanceName)
              .setMachineType(machineType)
              .addDisks(disk)
              .addNetworkInterfaces(networkInterface)
              // Set the preemptible setting.
              .setScheduling(Scheduling.newBuilder()
                  .setPreemptible(true)
                  .build())
              .build();

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

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

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

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

      System.out.printf("Instance created : %s\n", instanceName);
      System.out.println("Operation Status: " + response.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();

Pitão

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

DESCANSAR

Na API, construa uma solicitação normal para criar uma VM , mas inclua a propriedade preemptible no scheduling e defina-a como true . Por exemplo:

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

Cotas de CPU preemptivas

As VMs preemptivas exigem cotas de CPU disponíveis, como as VMs padrão. Para evitar que VMs preemptivas consumam as cotas de CPU de suas VMs padrão, você pode solicitar uma cota especial de "CPU preemptiva". Depois que o Compute Engine conceder a você uma cota de CPU preemptiva nessa região, todas as VMs preemptivas serão contabilizadas nessa cota, e todas as VMs padrão continuarão a ser contabilizadas na cota de CPU padrão.

Em regiões onde não há cota de CPU preemptiva, você pode usar a cota de CPU padrão para iniciar VMs preemptivas. Você também precisa de IP e cota de disco suficientes, como sempre. A cota de CPU preemptiva não fica visível na CLI gcloud ou nas páginas de cota do console do Google Cloud, a menos que o Compute Engine tenha concedido a cota.

Para obter mais informações sobre cotas, visite a página Cotas de recursos .

Iniciar uma VM preemptada

Como qualquer outra VM, se uma VM preemptiva for interrompida ou preemptada, você poderá iniciá-la novamente e trazê-la de volta ao estado RUNNING . Iniciar uma VM preemptiva redefine o contador de 24 horas, mas como ainda é uma VM preemptiva, o Compute Engine pode fazer a preempção antes de 24 horas. Não é possível converter uma VM preemptiva em uma VM padrão enquanto ela está em execução.

Se o Compute Engine interromper uma VM preemptiva em um grupo de instâncias gerenciadas (MIG) com escalonamento automático ou cluster do Google Kubernetes Engine (GKE), o grupo reiniciará a VM quando os recursos estiverem disponíveis novamente.

Lidar com a preempção com um script de desligamento

Quando o Compute Engine interrompe uma VM, você pode usar um script de desligamento para tentar executar ações de limpeza antes que a VM seja interrompida. Por exemplo, você pode interromper um processo em execução e copiar um arquivo de ponto de verificação para o Cloud Storage . Notavelmente, a duração máxima do período de desligamento é menor para um aviso de preempção do que para um desligamento iniciado pelo usuário. Para obter mais informações sobre o período de desligamento para um aviso de preempção, consulte Processo de preempção na documentação conceitual.

A seguir está um script de desligamento que você pode adicionar a uma VM preemptiva em execução ou adicionar a uma nova VM preemptiva ao criá-la. Este script é executado quando a VM começa a desligar, antes que o comando kill normal do sistema operacional interrompa todos os processos restantes. Depois de interromper normalmente o programa desejado, o script executa um upload paralelo de um arquivo de ponto de verificação para um intervalo do 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 adicionar este script a uma VM, configure o script para funcionar com um aplicativo na sua VM e adicione-o aos metadados da VM.

  1. Copie ou baixe o script de desligamento para sua estação de trabalho local.
  2. Abra o arquivo para edição e altere as seguintes variáveis:
    • [PROGRAM_NAME] é o nome do processo ou programa que você deseja encerrar. Por exemplo, apache2 ou nginx .
    • [LOCAL_USER] é o nome de usuário com o qual você está conectado à máquina virtual.
    • [BUCKET_NAME] é o nome do intervalo do Cloud Storage onde você quer salvar o arquivo de ponto de verificação do programa. Observe que o nome do bucket não começa com gs:// neste caso.
  3. Salve suas alterações.
  4. Adicione o script de desligamento a uma nova VM ou a uma VM existente .

Este script pressupõe o seguinte:

  • A VM foi criada com pelo menos acesso de leitura/gravação ao Cloud Storage. Consulte a documentação de autenticação para obter instruções sobre como criar uma VM com os escopos apropriados.

  • Você tem um bucket do Cloud Storage e permissão para gravar nele.

Identifique VMs preemptivas

Para verificar se uma VM é preemptiva, siga as etapas para Identificar o modelo de provisionamento e a ação de encerramento de uma VM .

Determinar se uma VM foi preemptada

Determine se uma VM foi interrompida com o console do Google Cloud , a CLI gcloud ou a API .

Console

Você pode verificar se uma VM foi interrompida verificando os logs de atividades do sistema.

  1. No console do Google Cloud, acesse a página Registros .

    Vá para registros

  2. Selecione seu projeto e clique em Continuar .

  3. Adicione compute.instances.preempted ao filtro por rótulo ou campo de pesquisa de texto .

  4. Opcionalmente, você também pode inserir um nome de VM se quiser ver operações de preempção para uma VM específica.

  5. Pressione Enter para aplicar os filtros especificados. O console do Google Cloud atualiza a lista de registros para mostrar apenas as operações em que uma VM foi interrompida.

  6. Selecione uma operação na lista para ver detalhes sobre a VM que foi interrompida.

gcloud


Use o comando gcloud compute operations list com um parâmetro de filtro para obter uma lista de eventos de preempção no seu projeto.

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

Você pode usar o parâmetro filter para ampliar o escopo dos resultados. Por exemplo, para ver eventos de preempção apenas para VMs em um grupo gerenciado de instâncias:

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

gcloud retorna uma resposta semelhante a:

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

Um tipo de operação compute.instances.preempted indica que a VM foi preemptada. Você pode usar o comando operations describe para obter mais informações sobre uma operação de preempção específica.

gcloud compute operations describe \
    systemevent-xxxxxxxx

gcloud retorna uma resposta semelhante a:

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

DESCANSAR


Para obter uma lista das operações recentes do sistema, envie uma solicitação GET ao URI das operações da zona.

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

A resposta contém uma lista de operações recentes.

{
  "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 definir o escopo da resposta para mostrar apenas operações de preempção, você pode adicionar um filtro à sua solicitação de API: operationType="compute.instances.preempted" . Para ver operações de preempção para uma VM específica, adicione um parâmetro targetLink ao filtro: operationType="compute.instances.preempted" AND targetLink="https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[VM_NAME]" .

Como alternativa, você pode determinar se uma VM foi interrompida dentro da própria VM. Isso é útil se você quiser lidar com um encerramento devido a uma preempção do Compute Engine de maneira diferente de um encerramento normal em um script de encerramento . Para fazer isso, basta verificar no servidor de metadados o valor preempted nos metadados de instância padrão da sua VM.

Por exemplo, use curl na sua VM para obter o valor de preempted :

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

Se esse valor for TRUE , a VM foi interrompida pelo Compute Engine; caso contrário, será FALSE .

Se quiser usar isso fora de um script de desligamento, você pode anexar ?wait_for_change=true ao URL. Isso executa uma solicitação HTTP GET suspensa que retorna apenas quando os metadados foram alterados e a VM foi interrompida.

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

Testar configurações de preempção

Você pode executar eventos de manutenção simulados em suas VMs para forçá-las a realizar a preempção. Use esse recurso para testar como seus aplicativos lidam com VMs preemptivas. Leia testando suas políticas de disponibilidade para saber como testar eventos de manutenção em suas VMs.

Você também pode simular a preempção de uma VM parando a VM , o que pode ser usado em vez de simular um evento de manutenção e evita limites de cota.

Melhores práticas

Aqui estão algumas práticas recomendadas para ajudar você a aproveitar ao máximo as instâncias de VM preemptivas.

Usando a API de instância em massa

Em vez de criar VMs únicas, você pode usar a API de instância em massa .

Escolha formatos de máquinas menores

Recursos para VMs preemptivas saem do excesso e fazem backup Google Cloudcapacidade. Geralmente, é mais fácil obter capacidade para tipos de máquinas menores , ou seja, tipos de máquinas com menos recursos, como vCPUs e memória. Você pode encontrar mais capacidade para VMs preemptivas selecionando um tipo de máquina personalizada menor, mas a capacidade é ainda mais provável para tipos de máquinas predefinidas menores. Por exemplo, em comparação com a capacidade do tipo de máquina predefinido n2-standard-32 , a capacidade do tipo de máquina personalizado n2-custom-24-96 é mais provável, mas a capacidade do tipo de máquina predefinido n2-standard-16 é ainda mais provável.

Execute grandes clusters de VM preemptivos fora dos horários de pico

A carga em Google Cloud data centers varia de acordo com o local e a hora do dia, mas geralmente é mais baixa à noite e nos finais de semana. Assim, as noites e os fins de semana são os melhores horários para executar grandes clusters de VMs preemptivos.

Projete seus aplicativos para serem tolerantes a falhas e preempções

É importante estar preparado para o fato de que ocorrerão mudanças nos padrões de preempção em diferentes momentos. Por exemplo, se uma zona sofrer uma interrupção parcial, um grande número de VMs preemptivas poderá ser substituído para abrir espaço para VMs padrão que precisam ser movidas como parte da recuperação. Nessa pequena janela de tempo, a taxa de preempção seria muito diferente da de qualquer outro dia. Se o seu aplicativo pressupõe que as preempções são sempre feitas em pequenos grupos, talvez você não esteja preparado para tal evento. Você pode testar o comportamento do seu aplicativo em um evento de preempção interrompendo a instância da VM.

Tente novamente criar VMs que foram interrompidas

Se sua instância de VM tiver sido preemptiva, tente criar novas VMs preemptivas uma ou duas vezes antes de voltar às VMs padrão. Dependendo dos seus requisitos, pode ser uma boa ideia combinar VMs padrão e preemptivas nos seus clusters para garantir que o trabalho prossegue a um ritmo adequado.

Use scripts de desligamento

Gerencie avisos de desligamento e preempção com um script de desligamento que pode salvar o progresso de um trabalho para que ele possa continuar de onde parou, em vez de recomeçar do zero.

O que vem a seguir?