Criar e usar VMs preemptivas


Nesta página, você aprenderá a criar e usar uma instância preemptiva de máquina virtual (VM, na sigla em inglês). 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 poderá interromper essas instâncias (forçar a interrupção) caso precise recuperar esses recursos para outras tarefas. As VMs preemptivas sempre são interrompidas após 24 horas. As VMs preemptivas são recomendadas apenas para aplicativos tolerantes a falhas que resistam à preempção da VM Verifique se esse é o caso do seu aplicativo antes de criar uma instância. Leia a documentação Instâncias de VM preemptiva para entender os riscos e o valor dessas VMs.

Antes de começar

  • Leia a documentação sobre instâncias de VMs preemptivas.
  • Configure a autenticação, caso ainda não tenha feito isso. A autenticação é o processo de verificação da sua identidade para acesso a serviços e APIs do Google Cloud. Para executar códigos ou amostras de um ambiente de desenvolvimento local, autentique-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. Install the Google Cloud CLI, then initialize it by running the following command:

      gcloud init
    2. Set a default region and zone.
    3. Go

      Para usar os exemplos Go desta página em um ambiente de desenvolvimento local, instale e inicialize o gcloud CLI e e configure o Application Default Credentials com suas credenciais de usuário.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      Confira mais informações em Set up authentication for a local development environment.

      Java

      Para usar os exemplos Java desta página em um ambiente de desenvolvimento local, instale e inicialize o gcloud CLI e e configure o Application Default Credentials com suas credenciais de usuário.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      Confira mais informações em Set up authentication for a local development environment.

      Node.js

      Para usar os exemplos Node.js desta página em um ambiente de desenvolvimento local, instale e inicialize o gcloud CLI e e configure o Application Default Credentials com suas credenciais de usuário.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      Confira mais informações em Set up authentication for a local development environment.

      Python

      Para usar os exemplos Python desta página em um ambiente de desenvolvimento local, instale e inicialize o gcloud CLI e e configure o Application Default Credentials com suas credenciais de usuário.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      Confira mais informações em Set up authentication for a local development environment.

      REST

      Para usar as amostras da API REST nesta página em um ambiente de desenvolvimento local, use as credenciais fornecidas para gcloud CLI.

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

        gcloud init

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

Como criar 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 do Spot.

gcloud

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

gcloud compute instances create [VM_NAME] --preemptible

em que [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 (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();

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 uma solicitação 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
  },
  ...
}

Cotas de CPU preemptiva

As VMs preemptivas exigem cotas de CPU disponíveis, como as VMs padrão. Para evitar que VMs preemptivas consumam as cotas de CPU das VMs padrão, solicite 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 serão contabilizadas na cota de CPU padrão.

Em regiões onde não há cotas de CPU preemptiva, use a cota de CPU padrão para iniciar instâncias preemptivas. Também é preciso ter IP e cota de disco suficientes. A cota de CPU preemptiva não é visível pela CLI gcloud ou nas páginas de cota do Console do Google Cloud Platform a menos que essa cota tenha sido concedida no Compute Engine.

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

Iniciar uma VM preemptiva

Como qualquer outra VM, se uma VM preemptiva for parada ou interrompida, é possível iniciá-la novamente e retorná-la ao estado RUNNING. Iniciar uma VM preemptiva redefine o contador de 24 horas, mas como ainda é uma VM preemptiva, o Compute Engine pode antecipar 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 gerenciado de instâncias (MIG, na sigla em inglês) de escalonamento automático ou um cluster do Google Kubernetes Engine (GKE), o grupo reiniciará a VM quando os recursos ficarem disponíveis novamente.

Processar a preempção com um script de desligamento

Quando o Compute Engine interrompe uma VM, é possível usar um script de desligamento para tentar executar ações de limpeza antes que a VM sofra uma interrupção forçada. Por exemplo, é possível interromper normalmente um processo em execução e copiar um arquivo de checkpoint para o Cloud Storage. O período máximo de desligamento é menor para uma notificação de preempção do que para um desligamento iniciado pelo usuário. Para mais informações sobre o período de desativação de uma notificação de preempção, consulte Processo de preempção na documentação conceitual.

O script de desligamento a seguir pode ser adicionado a uma VM preemptiva durante a execução ou quando ela é criada. Esse script é executado quando a VM começa a ser encerrada e antes que o comando kill normal do sistema operacional interrompa todos os processos restantes. Após o encerramento normal do programa desejado, o script fará o upload paralelo de um arquivo de checkpoint para um bucket 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 esse script a uma VM, configure-o para trabalhar com um aplicativo na VM e adicione-o aos metadados da VM.

  1. Copie ou faça download do script de desligamento na estação de trabalho local.
  2. Abra o arquivo para editá-lo e altere as seguintes variáveis:
    • [PROGRAM_NAME] é o nome do processo ou programa que você quer encerrar. Por exemplo, apache2 ou nginx.
    • [LOCAL_USER] é o nome de usuário que você usou para fazer login na máquina virtual.
    • [BUCKET_NAME] é o nome do bucket do Cloud Storage em que você quer salvar o arquivo de checkpoint do programa. Observe que o nome do bucket não começa com gs:// nesse caso.
  3. Salve as alterações.
  4. Adicione o script de encerramento a uma nova VM ou a uma VM atual.

Para esse script, presume-se que:

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

  • você tenha um bucket do Cloud Storage e permissão para gravar nele.

Identificar VMs preemptivas

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

Determinar se uma VM foi interrompida

Determine se uma VM foi preemptiva pelo console do Google Cloud, pela CLI gcloud ou pela API.

Console

Verifique se uma instância foi preemptiva consultando os registros de atividades do sistema.

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

    Ir para os registros

  2. Selecione o projeto e clique em Continuar.

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

  4. Outra opção é inserir um nome de VM se você quiser ver as operações de preempção de uma determinada VM.

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

  6. Selecione uma operação na lista para ver detalhes sobre a instância que passou por interrupção forçada.

gcloud


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

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

É possível usar o parâmetro de filtro para ampliar o escopo dos resultados. Por exemplo, para ver eventos de preempção apenas de VMs dentro de 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 esta:

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 preemptiva. É possível usar o comando operations describe para mais informações sobre uma determinada operação de preempção.

gcloud compute operations describe \
    systemevent-xxxxxxxx

gcloud retorna uma resposta semelhante a esta:

...
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 acessar uma lista das operações recentes do sistema, envie uma solicitação GET para o 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 das 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 que a resposta mostre apenas operações de preempção, adicione um filtro à solicitação de API: operationType="compute.instances.preempted". Para ver as operações de preempção 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]".

Outra opção é determinar se uma VM passou por preempção dentro da própria VM. Isso é útil quando você quer lidar com um encerramento decorrente de uma preempção do Compute Engine de maneira diferente de um encerramento normal em um script de encerramento. Para fazer isso, basta verificar o valor preempted no servidor de metadados nos metadados de instância padrão da VM.

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

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

Se esse valor for TRUE, significa que a VM passou por interrupção forçada pelo Compute Engine. Caso contrário, será FALSE.

Para usar isso fora de um script de encerramento, anexe ?wait_for_change=true ao URL. Será executada uma solicitação HTTP GET pendente que só retorna quando os metadados são 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 as configurações da preempção

Para forçar a preempção em uma VM, execute eventos de manutenção simulados nela. Use esse recurso para testar como seus apps lidam com VMs preemptivas. Leia Como testar suas políticas de disponibilidade para aprender como executar eventos de manutenção simulados nas VMs.

Para simular a preempção de uma VM, interrompa a VM. Isso pode ser usada em vez de simular um evento de manutenção e evitar limites de cota.

Práticas recomendadas

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

Como usar a API de instâncias em massa

Em vez de criar VMs individuais, use a API de instância em massa.

Escolher formas menores de máquina

Os recursos das VM preemptivas saem da capacidade extra e de backup do Google Cloud. Geralmente, é mais fácil conseguir capacidade para tipos de máquinas menores, o que significa tipos de máquinas com menos recursos como vCPUs e memória. É possível conseguir mais capacidade para VMs preemptivas selecionando um tipo de máquina personalizado menor, mas a capacidade é ainda mais provável para tipos de máquina predefinidos menores. Por exemplo, em comparação com a capacidade do tipo de máquina predefinido n2-standard-32, a capacidade para o tipo de máquina personalizado n2-custom-24-96 é mais provável, mas a capacidade para o tipo de máquina predefinido n2-standard-16 é ainda mais provável.

Executar grandes clusters de VM preemptiva fora dos horários de pico

A carga nos data centers do Google Cloud varia de acordo com o local e a hora do dia, mas costuma ser mais baixa nas noites e fins de semana. Assim, o melhor momento para executar grandes clusters de VM preemptiva é durante esses períodos.

Desenvolver os aplicativos para serem tolerantes a falhas e preempção

É importante se preparar para mudanças nos padrões de preempção em diferentes momentos. Por exemplo, se uma zona sofrer uma interrupção parcial, uma grande quantidade de VMs preemptivas poderão preemptivas para abrir espaço para VMs padrão que precisam ser movidas como parte da recuperação. Nesse pequeno período de tempo, a taxa de preempção é muito diferente de qualquer outro dia. Se seu aplicativo considera que as preempções sempre ocorrem em pequenos grupos, pode ser que você não esteja preparado para um evento desse tipo. Interrompa a instância de VM para testar o comportamento do seu aplicativo em um evento de preempção.

Tentar criar novamente as VMs que foram preemptivas

Se a 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 requisitos, combine VMs padrão com preemptivas nos clusters para garantir que o trabalho ocorra em um ritmo adequado.

Usar scripts de desligamento

Gerencie avisos de desligamento e preempção com um script de desligamento que salva o progresso de um trabalho. Portanto, continue de onde você parou em vez de começar do zero.

A seguir