Membuat dan menggunakan preemptible VM


Halaman ini menjelaskan cara membuat dan menggunakan instance preemptible virtual machine (VM). Preemptible VM tersedia dengan diskon hingga 60-91% dibandingkan dengan harga VM standar. Namun, Compute Engine dapat menghentikan (preempt) VM ini jika perlu mengklaim kembali resource tersebut untuk tugas lainnya. Preemptible VM selalu berhenti setelah 24 jam. Preemptible VM hanya direkomendasikan untuk aplikasi fault-tolerant yang dapat tahan terhadap preemption VM. Pastikan aplikasi Anda dapat menangani preemption sebelum Anda memutuskan untuk membuat preemptible VM. Untuk memahami risiko dan manfaat preemptible VM, baca dokumentasi instance preemptible VM.

Sebelum memulai

  • Baca dokumentasi Instance preemptible VM.
  • Siapkan autentikasi, jika Anda belum melakukannya. Autentikasi adalah proses verifikasi identitas Anda untuk akses ke layanan dan API Google Cloud. Untuk menjalankan kode atau contoh dari lingkungan pengembangan lokal, Anda dapat melakukan autentikasi ke Compute Engine sebagai berikut.

    Pilih tab untuk melihat bagaimana Anda berencana menggunakan contoh di halaman ini:

    Konsol

    Saat menggunakan Konsol Google Cloud untuk mengakses API dan layanan Google Cloud, Anda tidak perlu menyiapkan autentikasi.

    gcloud

    1. Instal Google Cloud CLI, lalu initialize dengan menjalankan perintah berikut:

      gcloud init
    2. Menetapkan region dan zona default.

    Go

    Untuk menggunakan contoh Go di halaman ini dari lingkungan pengembangan lokal, instal dan lakukan inisialisasi gcloud CLI, lalu siapkan Kredensial Default Aplikasi dengan kredensial pengguna Anda.

    1. Menginstal Google Cloud CLI.
    2. Untuk initialize gcloud CLI, jalankan perintah berikut:

      gcloud init
    3. Buat kredensial autentikasi lokal untuk Akun Google Anda:

      gcloud auth application-default login

    Untuk informasi selengkapnya, lihat Siapkan autentikasi untuk lingkungan pengembangan lokal.

    Java

    Untuk menggunakan contoh Java di halaman ini dari lingkungan pengembangan lokal, instal dan lakukan inisialisasi gcloud CLI, lalu siapkan Kredensial Default Aplikasi dengan kredensial pengguna Anda.

    1. Menginstal Google Cloud CLI.
    2. Untuk initialize gcloud CLI, jalankan perintah berikut:

      gcloud init
    3. Buat kredensial autentikasi lokal untuk Akun Google Anda:

      gcloud auth application-default login

    Untuk informasi selengkapnya, lihat Siapkan autentikasi untuk lingkungan pengembangan lokal.

    Node.js

    Untuk menggunakan contoh Node.js di halaman ini dari lingkungan pengembangan lokal, instal dan lakukan inisialisasi gcloud CLI, lalu siapkan Kredensial Default Aplikasi dengan kredensial pengguna Anda.

    1. Menginstal Google Cloud CLI.
    2. Untuk initialize gcloud CLI, jalankan perintah berikut:

      gcloud init
    3. Buat kredensial autentikasi lokal untuk Akun Google Anda:

      gcloud auth application-default login

    Untuk informasi selengkapnya, lihat Siapkan autentikasi untuk lingkungan pengembangan lokal.

    Python

    Untuk menggunakan contoh Python di halaman ini dari lingkungan pengembangan lokal, instal dan lakukan inisialisasi gcloud CLI, lalu siapkan Kredensial Default Aplikasi dengan kredensial pengguna Anda.

    1. Menginstal Google Cloud CLI.
    2. Untuk initialize gcloud CLI, jalankan perintah berikut:

      gcloud init
    3. Buat kredensial autentikasi lokal untuk Akun Google Anda:

      gcloud auth application-default login

    Untuk informasi selengkapnya, lihat Siapkan autentikasi untuk lingkungan pengembangan lokal.

    REST

    Untuk menggunakan contoh REST API di halaman ini dalam lingkungan pengembangan lokal, gunakan kredensial yang Anda berikan ke gcloud CLI.

      Instal Google Cloud CLI, lalu initialize dengan menjalankan perintah berikut:

      gcloud init

Membuat preemptible VM

Buat preemptible VM menggunakan gcloud CLI atau Compute Engine API. Untuk menggunakan konsol Google Cloud, buat VM Spot.

gcloud

Dengan gcloud compute, gunakan perintah instances create yang sama seperti yang Anda gunakan untuk membuat VM normal, tetapi tambahkan flag --preemptible.

gcloud compute instances create [VM_NAME] --preemptible

dengan [VM_NAME] adalah nama 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

Di API, buat permintaan normal untuk membuat VM, tetapi sertakan properti preemptible di bagian scheduling dan tetapkan ke true singkat ini. Contoh:

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

Kuota CPU preemptible

Preemptible VM memerlukan kuota CPU yang tersedia seperti VM standar. Agar preemptible VM tidak menggunakan kuota CPU untuk VM standar, Anda dapat meminta kuota "Preemptible CPU" khusus. Setelah Compute Engine memberi Anda kuota CPU yang dapat dihentikan di region tersebut, semua preemptible VM akan mengurangi kuota tersebut, dan semua VM standar akan terus mengurangi kuota CPU standar.

Di region tempat Anda tidak memiliki kuota CPU yang dapat dihentikan, Anda dapat menggunakan kuota CPU standar untuk meluncurkan preemptible VM. Anda juga memerlukan IP dan kuota disk yang cukup, seperti biasa. Kuota CPU yang dapat dihentikan tidak terlihat di halaman kuota gcloud CLI atau Google Cloud Console, kecuali jika Compute Engine memberikan kuota tersebut.

Untuk mengetahui informasi selengkapnya tentang kuota, buka halaman Resource Quotas.

Memulai VM yang di-preempt

Seperti VM lainnya, jika preemptible VM dihentikan atau di-preempt, Anda dapat memulai VM lagi dan memulihkannya ke status RUNNING. Memulai preemptible VM akan mereset penghitung 24 jam, tetapi karena masih merupakan preemptible VM, Compute Engine dapat melakukan preempt sebelum 24 jam. Preemptible VM ke VM standar tidak dapat dikonversi saat sedang berjalan.

Jika Compute Engine menghentikan preemptible VM di cluster grup instance terkelola (MIG) atau Google Kubernetes Engine (GKE) penskalaan otomatis, grup akan memulai ulang VM saat resource kembali tersedia.

Menangani preemption dengan skrip penonaktifan

Saat VM Anda di-preempt, Anda dapat menggunakan skrip penonaktifan untuk melakukan tindakan pembersihan sebelum VM berhenti. Misalnya, Anda dapat menghentikan proses yang sedang berjalan dengan baik dan menyalin file checkpoint ke Cloud Storage.

Berikut ini adalah skrip penonaktifan yang dapat ditambahkan ke preemptible VM yang sedang berjalan atau yang ditambahkan ke preemptible VM baru saat Anda membuatnya. Skrip ini dijalankan saat VM mulai dimatikan, sebelum perintah kill normal sistem operasi menghentikan semua proses yang tersisa. Setelah menghentikan program yang diinginkan secara halus, skrip akan melakukan upload paralel file checkpoint ke bucket Cloud Storage.

#!/bin/bash

MY_PROGRAM="[PROGRAM_NAME]" # For example, "apache2" or "nginx"
MY_USER="[LOCAL_USERNAME]"
CHECKPOINT="/home/$MY_USER/checkpoint.out"
GSUTIL_OPTS="-m -o GSUtil:parallel_composite_upload_threshold=32M"
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 "gsutil $GSUTIL_OPTS cp $CHECKPOINT gs://${BUCKET_NAME}/"

echo "Done uploading, shutting down."

Untuk menambahkan skrip ini ke VM, konfigurasikan skrip agar berfungsi dengan aplikasi pada VM Anda dan tambahkan ke metadata VM.

  1. Salin atau download skrip penonaktifan ke workstation lokal.
  2. Buka file untuk diedit dan ubah variabel berikut:
    • [PROGRAM_NAME] adalah nama proses atau program yang ingin Anda nonaktifkan. Misalnya apache2 atau nginx.
    • [LOCAL_USER] adalah nama pengguna yang Anda gunakan untuk login ke virtual machine.
    • [BUCKET_NAME] adalah nama bucket Cloud Storage tempat Anda ingin menyimpan file checkpoint program. Perlu diperhatikan bahwa nama bucket tidak diawali dengan gs://.
  3. Simpan perubahan Anda.
  4. Tambahkan skrip penonaktifan ke VM baru atau VM yang ada.

Skrip ini mengasumsikan hal berikut:

  • VM dibuat dengan setidaknya akses baca/tulis ke Cloud Storage. Baca dokumentasi autentikasi untuk mengetahui petunjuk cara membuat VM dengan cakupan yang sesuai.

  • Anda sudah memiliki bucket Cloud Storage dan izin untuk menulis ke bucket tersebut.

Mengidentifikasi preemptible VM

Untuk memeriksa apakah VM adalah preemptible VM, ikuti langkah-langkah untuk Mengidentifikasi model penyediaan dan tindakan penghentian VM.

Menentukan apakah VM di-preempt

Tentukan apakah VM di-preempt dengan Google Cloud Console, gcloud CLI, atau API.

Konsol

Anda dapat memeriksa apakah VM di-preempt dengan memeriksa log aktivitas sistem.

  1. Di Konsol Google Cloud, buka halaman Logs.

    Buka Log

  2. Pilih project Anda, lalu klik Lanjutkan.

  3. Tambahkan compute.instances.preempted ke kolom filter menurut label atau penelusuran teks.

  4. Anda juga dapat memasukkan nama VM jika ingin melihat operasi preemption untuk VM tertentu.

  5. Tekan enter untuk menerapkan filter yang ditentukan. Konsol Google Cloud memperbarui daftar log untuk hanya menampilkan operasi dengan VM yang di-preempt.

  6. Pilih operasi dalam daftar untuk melihat detail tentang VM yang di-preempt.

gcloud


Gunakan perintah gcloud compute operations list dengan parameter filter untuk mendapatkan daftar peristiwa preemption di project Anda.

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

Anda dapat menggunakan parameter filter untuk menentukan cakupan hasil lebih lanjut. Misalnya, untuk melihat peristiwa preemption hanya untuk VM dalam grup instance terkelola:

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

gcloud menampilkan respons yang mirip dengan:

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

Jenis operasi compute.instances.preempted menunjukkan bahwa VM di-preempt. Anda dapat menggunakan perintah operations describe untuk mendapatkan informasi lebih lanjut tentang operasi preemption tertentu.

gcloud compute operations describe \
    systemevent-xxxxxxxx

gcloud menampilkan respons yang mirip dengan:

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


Untuk mendapatkan daftar operasi sistem terbaru, kirim permintaan GET ke URI operasi zona.

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

Respons berisi daftar operasi terbaru.

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

Untuk mencakup respons agar hanya menampilkan operasi preemption, Anda dapat menambahkan filter ke permintaan API: operationType="compute.instances.preempted". Untuk melihat operasi preemption untuk VM tertentu, tambahkan parameter targetLink ke filter: operationType="compute.instances.preempted" AND targetLink="https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[VM_NAME]".

Atau, Anda dapat menentukan apakah VM di-preempt dari dalam VM itu sendiri. Hal ini berguna jika Anda ingin menangani penonaktifan karena preemption Compute Engine yang berbeda dengan penonaktifan normal dalam skrip penonaktifan. Untuk melakukannya, cukup periksa nilai preempted di server metadata dalam metadata instance default VM Anda.

Misalnya, gunakan curl dari dalam VM Anda guna mendapatkan nilai untuk preempted:

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

Jika nilai ini TRUE, VM akan di-preempt oleh Compute Engine. Jika tidak, VM adalah FALSE.

Jika ingin menggunakannya di luar skrip penonaktifan, Anda dapat menambahkan ?wait_for_change=true ke URL. Layanan ini menjalankan permintaan HTTP GET yang tidak berfungsi, yang hanya ditampilkan saat metadata telah berubah dan VM telah di-preempt.

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

Menguji setelan preemption

Anda dapat menjalankan simulasi peristiwa pemeliharaan di VM untuk memaksanya melakukan preempt. Gunakan fitur ini untuk menguji cara aplikasi Anda menangani preemptible VM. Baca menguji kebijakan ketersediaan Anda untuk mempelajari cara menguji peristiwa pemeliharaan di VM Anda.

Anda juga dapat melakukan simulasi preemption VM dengan menghentikan VM, yang dapat digunakan sebagai pengganti simulasi peristiwa pemeliharaan dan untuk menghindari batas kuota.

Praktik terbaik

Berikut ini beberapa praktik terbaik untuk membantu Anda memaksimalkan instance preemptible VM.

Menggunakan bulk instance API

Daripada membuat satu VM, Anda dapat menggunakan bulk instance API.

Memilih bentuk mesin yang lebih kecil

Resource untuk preemptible VM kelebihan dan mencadangkan kapasitas Google Cloud. Kapasitas sering kali lebih mudah diperoleh untuk jenis mesin yang lebih kecil, yang berarti jenis mesin dengan resource yang lebih sedikit seperti vCPU dan memori. Anda mungkin menemukan kapasitas yang lebih besar untuk preemptible VM dengan memilih jenis mesin kustom yang lebih kecil, tetapi kapasitas bahkan lebih cenderung untuk jenis mesin yang telah ditetapkan dengan ukuran lebih kecil. Misalnya, dibandingkan dengan kapasitas untuk jenis mesin yang telah ditetapkan dari n2-standard-32, kapasitas untuk jenis mesin kustom n2-custom-24-96 lebih mungkin, tetapi kapasitas untuk jenis mesin yang telah ditetapkan untuk n2-standard-16 bahkan lebih mungkin terjadi.

Menjalankan cluster preemptible VM berukuran besar selama masa sibuk

Muatan pada pusat data Google Cloud bervariasi menurut lokasi dan waktu, tetapi umumnya paling rendah pada malam hari dan akhir pekan. Dengan demikian, malam hari dan akhir pekan adalah waktu terbaik untuk menjalankan cluster preemptible VM yang besar.

Mendesain aplikasi Anda agar menjadi fault-tolerant dan preemption

Anda harus siap dengan adanya perubahan pola preemption pada waktu yang berbeda. Misalnya, jika suatu zona mengalami pemadaman sebagian, sejumlah besar preemptible VM dapat di-preempt untuk memberi ruang bagi VM standar yang perlu dipindahkan sebagai bagian dari pemulihan. Dalam jangka waktu yang singkat itu, tingkat preemption akan terlihat sangat berbeda dari hari lainnya. Jika aplikasi Anda berasumsi bahwa preemption selalu dilakukan dalam kelompok kecil, Anda mungkin tidak siap untuk peristiwa tersebut. Anda dapat menguji perilaku aplikasi dalam peristiwa preemption dengan menghentikan instance VM.

Coba buat lagi VM yang telah di-preempt

Jika instance VM Anda di-preempt, coba buat preemptible VM baru satu atau dua kali sebelum beralih kembali ke VM standar. Bergantung pada persyaratan Anda, sebaiknya gabungkan VM standar dan preemptible VM di cluster Anda untuk memastikan bahwa pekerjaan dilakukan dengan kecepatan yang memadai.

Menggunakan skrip penonaktifan

Kelola pemberitahuan penonaktifan dan preemption dengan skrip penonaktifan yang dapat menyimpan progres tugas sehingga dapat melanjutkan tugas, bukan memulai dari awal.

Apa langkah selanjutnya?