Crie e use VMs preemptíveis


Esta página explica como criar e usar uma instância de máquina virtual (VM) preemptível. As VMs preemptivas estão disponíveis com um desconto de até 60 a 91% em comparação com o preço das VMs padrão. No entanto, o Compute Engine pode parar (preempt) estas VMs se precisar de reaver esses recursos para outras tarefas. As VMs preemptivas param sempre após 24 horas. As VMs preemptivas só são recomendadas para aplicações tolerantes a falhas que possam resistir à preempção de VMs. Certifique-se de que a sua aplicação consegue processar as preempções antes de decidir criar uma VM preempível. Para compreender os riscos e o valor das VMs preemptivas, leia a documentação sobre as instâncias de VM preemptivas.

Antes de começar

  • Leia a documentação sobre instâncias de VM preemptivas.
  • Se ainda não o tiver feito, configure a autenticação. A autenticação valida a sua identidade para aceder a Google Cloud serviços e APIs. Para executar código ou exemplos a partir de um ambiente de desenvolvimento local, pode autenticar-se no Compute Engine selecionando uma das seguintes opções:

    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. Instale a CLI Google Cloud. Após a instalação, inicialize a CLI gcloud executando o seguinte comando:

      gcloud init

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade federada.

    2. Set a default region and zone.

    Go

    Para usar os Go exemplos nesta página num ambiente de desenvolvimento local, instale e inicialize a CLI gcloud e, em seguida, configure as Credenciais predefinidas da aplicação com as suas credenciais de utilizador.

      Instale a CLI Google Cloud.

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade 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 mais informações, consulte Set up authentication for a local development environment.

    Java

    Para usar os Java exemplos nesta página num ambiente de desenvolvimento local, instale e inicialize a CLI gcloud e, em seguida, configure as Credenciais predefinidas da aplicação com as suas credenciais de utilizador.

      Instale a CLI Google Cloud.

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade 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 mais informações, consulte Set up authentication for a local development environment.

    Node.js

    Para usar os Node.js exemplos nesta página num ambiente de desenvolvimento local, instale e inicialize a CLI gcloud e, em seguida, configure as Credenciais predefinidas da aplicação com as suas credenciais de utilizador.

      Instale a CLI Google Cloud.

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade 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 mais informações, consulte Set up authentication for a local development environment.

    Python

    Para usar os Python exemplos nesta página num ambiente de desenvolvimento local, instale e inicialize a CLI gcloud e, em seguida, configure as Credenciais predefinidas da aplicação com as suas credenciais de utilizador.

      Instale a CLI Google Cloud.

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade 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 mais informações, consulte Set up authentication for a local development environment.

    REST

    Para usar os exemplos da API REST nesta página num ambiente de desenvolvimento local, usa as credenciais que fornece à CLI gcloud.

      Instale a CLI Google Cloud.

      Se estiver a usar um fornecedor de identidade (IdP) externo, primeiro tem de iniciar sessão na CLI gcloud com a sua identidade federada.

    Para mais informações, consulte o artigo Autenticar para usar REST na Google Cloud documentação de autenticação.

Crie uma VM preemptiva

Crie uma VM preemptível através da CLI gcloud ou da API Compute Engine. Em alternativa, para usar a Google Cloud consola, crie uma VM do Spot.

gcloud

Com o gcloud compute, use o mesmo comando instances create que usaria para criar uma VM normal, mas adicione a flag --preemptible.

gcloud compute instances create [VM_NAME] --preemptible

onde [VM_NAME] é o nome da 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

Na API, crie um pedido normal para criar uma VM, mas inclua a propriedade preemptible em 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
  },
  ...
}

Quotas de CPU preemptivas

As VMs preemptivas requerem quotas de CPU disponíveis, como as VMs padrão. Para evitar que as VMs preemptíveis consumam as quotas de CPU das suas VMs padrão, pode pedir uma quota especial de "CPU preemptível". Depois de o Compute Engine lhe conceder uma quota de CPU preemptível nessa região, todas as VMs preemptíveis são contabilizadas nessa quota, e todas as VMs padrão continuam a ser contabilizadas na quota de CPU padrão.

Nas regiões onde não tem quota de CPU preemptiva, pode usar a quota de CPU padrão para iniciar VMs preemptivas. Também precisa de ter quota de IP e de disco suficientes, como habitualmente. A quota de CPUs preemptíveis não está visível na CLI gcloud nem nas páginas de quota da consola, a menos que o Compute Engine tenha concedido a quota.Google Cloud

Para mais informações sobre quotas, visite a página Quotas de recursos.

Inicie uma VM preemptiva

Tal como qualquer outra VM, se uma VM preemptível for parada ou preemptada, pode iniciar a VM novamente e repô-la no estado RUNNING. O início de uma VM preemptiva repõe o contador de 24 horas, mas, como continua a ser uma VM preemptiva, o Compute Engine pode ser preemptivo antes de 24 horas. Não é possível converter uma VM preemptível numa VM padrão enquanto estiver em execução.

Se o Compute Engine parar uma VM preemptível num grupo de instâncias geridas (GIG) de escala automática ou num cluster do Google Kubernetes Engine (GKE), o grupo reinicia a VM quando os recursos ficarem novamente disponíveis.

Faça a gestão da preempção com um script de encerramento

Quando o Compute Engine antecipa uma VM, pode usar um script de encerramento para tentar realizar ações de limpeza antes de a VM ser antecipada. Por exemplo, pode parar graciosamente um processo em execução e copiar um ficheiro de ponto de verificação para o Cloud Storage. Em particular, a duração máxima do período de encerramento é mais curta para um aviso de preemptão do que para um encerramento iniciado pelo utilizador. Para mais informações acerca do período de encerramento de um aviso de preempção, consulte o processo de preempção na documentação conceptual.

Segue-se um script de encerramento que pode adicionar a uma VM preemptível em execução ou a uma nova VM preemptível quando a criar. Este script é executado quando a VM começa a ser encerrada, antes de o comando kill normal do sistema operativo parar todos os processos restantes. Depois de parar corretamente o programa pretendido, o script carrega em paralelo um ficheiro de ponto de verificação para um contentor 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 uma aplicação na VM e adicione-o aos metadados da VM.

  1. Copie ou transfira o script de encerramento para a sua estação de trabalho local.
  2. Abra o ficheiro para edição e altere as seguintes variáveis:
    • [PROGRAM_NAME] é o nome do processo ou do programa que quer encerrar. Por exemplo, apache2 ou nginx.
    • [LOCAL_USER] é o nome de utilizador com o qual tem sessão iniciada na máquina virtual.
    • [BUCKET_NAME] é o nome do contentor do Cloud Storage onde quer guardar o ficheiro de ponto de verificação do programa. Tenha em atenção que, neste caso, o nome do contentor não começa com gs://.
  3. Guarde as alterações.
  4. Adicione o script de encerramento 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/escrita ao Cloud Storage. Consulte a documentação de autenticação para ver instruções sobre como criar uma VM com os âmbitos adequados.

  • Tem um contentor do Cloud Storage existente e autorização para escrever nele.

Identifique VMs preemptivas

Para verificar se uma VM é uma VM preemptível, siga os passos para Identificar o modelo de aprovisionamento e a ação de rescisão de uma VM.

Determine se uma VM foi anulada

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

Consola

Pode verificar se uma VM foi anulada verificando os registos de atividade do sistema.

  1. Na Google Cloud consola, aceda à página Registos.

    Aceda aos registos

  2. Selecione o projeto e clique em Continuar.

  3. Adicione compute.instances.preempted ao campo filtrar por etiqueta ou pesquisa de texto.

  4. Opcionalmente, também pode introduzir um nome de VM se quiser ver operações de remoção preventiva para uma VM específica.

  5. Prima Enter para aplicar os filtros especificados. A Google Cloud consola atualiza a lista de registos para mostrar apenas as operações em que uma MV foi anulada.

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

gcloud


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

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

Pode usar o parâmetro filter para restringir ainda mais o âmbito dos resultados. Por exemplo, para ver apenas eventos de antecipação para VMs num grupo de instâncias gerido:

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

gcloud devolve uma resposta semelhante à seguinte:

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 de compute.instances.preempted indica que a VM foi anulada. Pode usar o comando operations describe para obter mais informações sobre uma operação de preemptiva específica.

gcloud compute operations describe \
    systemevent-xxxxxxxx

gcloud devolve uma resposta semelhante à seguinte:

...
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 obter uma lista de operações recentes do sistema, envie um pedido GET para o URI das operações de 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 restringir o âmbito da resposta de modo a mostrar apenas operações de antecipação, pode adicionar um filtro ao seu pedido de API: operationType="compute.instances.preempted". Para ver as operações de preemptividade de 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]".

Em alternativa, pode determinar se uma VM foi anulada a partir da própria VM. Isto é útil se quiser processar um encerramento devido a uma preempção do Compute Engine de forma diferente de um encerramento normal num script de encerramento. Para tal, basta verificar o valor preempted no metadados da instância predefinidos da VM no servidor de metadados.

Por exemplo, use curl a partir da sua VM para obter o valor de preempted:

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

Se este valor for TRUE, a VM foi anulada pelo Compute Engine. Caso contrário, é FALSE.

Se quiser usar esta opção fora de um script de encerramento, pode anexar ?wait_for_change=true ao URL. Isto executa um pedido HTTP GET pendente que só é devolvido quando os metadados são alterados e a VM é anulada.

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

Teste as definições de antecipação

Pode executar eventos de manutenção simulados nas suas VMs para forçar a sua remoção preventiva. Use esta funcionalidade para testar como as suas apps processam VMs preemptivas. Leia o artigo sobre como testar as suas políticas de disponibilidade para saber como testar eventos de manutenção nas suas VMs.

Também pode simular a remoçã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 quota.

Práticas recomendadas

Seguem-se algumas práticas recomendadas para ajudar a tirar o máximo partido das instâncias de VM preemptíveis.

Usar a API de instâncias em massa

Em vez de criar VMs individuais, pode usar a API de instâncias em massa.

Escolha formas de máquinas mais pequenas

Os recursos para VMs preemptíveis provêm da capacidade excedentária e de cópia de segurança Google Cloud Normalmente, é mais fácil obter capacidade para tipos de máquinas mais pequenos, o que significa tipos de máquinas com menos recursos, como vCPUs e memória. Pode encontrar mais capacidade para VMs preemptíveis selecionando um tipo de máquina personalizado mais pequeno, mas é ainda mais provável encontrar capacidade para tipos de máquinas predefinidos mais pequenos. Por exemplo, em comparação com a capacidade do tipo de máquina n2-standard-32 predefinido, a capacidade do tipo de máquina n2-custom-24-96 personalizado é mais provável, mas a capacidade do tipo de máquina n2-standard-16 predefinido é ainda mais provável.

Execute grandes clusters de VMs preemptíveis durante as horas de menor procura

A carga nos Google Cloud centros de dados varia consoante a localização e a hora do dia, mas é geralmente mais baixa à noite e aos fins de semana. Como tal, as noites e os fins de semana são as melhores alturas para executar grandes clusters de VMs preemptíveis.

Crie as suas aplicações para serem tolerantes a falhas e a preemptivas

É importante estar preparado para o facto de existirem alterações nos padrões de antecipação em diferentes momentos. Por exemplo, se uma zona sofrer uma indisponibilidade parcial, um grande número de VMs preemptivas pode ser preemptado para dar lugar a VMs padrão que precisam de ser movidas como parte da recuperação. Nesse pequeno período, a taxa de antecipação seria muito diferente da de qualquer outro dia. Se a sua aplicação pressupõe que as antecipações são sempre feitas em pequenos grupos, pode não estar preparada para um evento deste tipo. Pode testar o comportamento da sua aplicação num evento de preemptividade parando a instância de VM.

Volte a tentar criar VMs que foram antecipadas

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

Use scripts de encerramento

Faça a gestão dos avisos de encerramento e de preemptção com um script de encerramento que pode guardar o progresso de uma tarefa para que possa continuar de onde parou, em vez de começar do zero.

O que se segue?