Beispiele für automatisierte Antworten zur Kostenkontrolle

Beispielreferenzarchitektur

Diagramm eines Beispiels mit programmatischen Benachrichtigungen für Budgetbenachrichtigungen zur Automatisierung einer Kostenkontrollenantwort
Abbildung 1: Ein Beispiel für die Verwendung von Budgetbenachrichtigungen zur Automatisierung von Kostenkontrollantworten mit Pub/Sub für programmatische Benachrichtigungen und Cloud Functions zur Automatisierung einer Antwort.

Wenn Sie kostenbewusst sind und Ihre Umgebung relativ zu Ihrem Budget steuern müssen, können Sie programmatische Budgetbenachrichtigungen verwenden, um die Kostenkontrollenantwort auf Basis der Budgetbenachrichtigung zu automatisieren.

Budgetbenachrichtigungen verwenden Pub/Sub-Themen, um den Status des Cloud Billing-Budgets unter Verwendung der Skalierbarkeit, Flexibilität und Zuverlässigkeit der unternehmensbezogenen nachrichtenorientierten Middleware für die Cloud in Echtzeit darzustellen.

Auf dieser Seite finden Sie Beispiele und detaillierte Anleitungen dazu, wie Sie mithilfe von Cloud Functions Budgetbenachrichtigungen zur Automatisierung der Kostenverwaltung nutzen können.

Budgetbenachrichtigungen einrichten

Der erste Schritt besteht darin, ein Pub/Sub-Thema für Ihr Budget zu aktivieren. Dies wird unter Benachrichtigungen zu programmatischen Budgetbenachrichtigungen verwalten ausführlich beschrieben.

Beachten Sie Folgendes, nachdem Sie die Budgetbenachrichtigungen aktiviert haben:

  • Pub/Sub-Thema: Dies ist der konfigurierte Benachrichtigungsendpunkt für das Budget.
  • Budget-ID: Dies ist eine eindeutige ID für das Budget, die Bestandteil aller Benachrichtigungen ist. Sie finden die Budget-ID im Budget unter Benachrichtigungen verwalten. Die ID wird angezeigt, wenn Sie Pub/Sub-Thema mit diesem Budget verbinden auswählen.

Im Bereich "Benachrichtigungen verwalten" der Google Cloud Console können Sie ein Pub/Sub-Thema mit einem Budget verknüpfen. Er enthält die Budget-ID, den Projektnamen und das Pub/Sub-Thema.

Benachrichtigungen beobachten

Der nächste Schritt besteht darin, Ihre Benachrichtigungen zu beobachten. Abonnieren Sie dazu das Pub/Sub-Thema. Wenn Sie kein Abo haben, werden veröffentlichte Nachrichten durch Pub/Sub gelöscht und Sie können diese später nicht mehr abrufen.

Es gibt verschiedene Möglichkeiten, ein Thema zu abonnieren. Für die folgenden Beispiele verwenden wir Cloud Functions-Funktionstrigger.

Cloud Functions-Funktion erstellen

So erstellen Sie eine neue Cloud Functions-Funktion:

  1. Wechseln Sie in der Google Cloud Console zur Seite Cloud Functions.

    Zur Seite "Google Cloud Functions"

  2. Klicken Sie auf  FUNKTION ERSTELLEN und geben Sie der Funktion einen aussagekräftigen Namen, der Ihr Budget beschreibt.

  3. Wählen Sie unter Trigger die Option Pub/Sub-Thema aus.

  4. Wählen Sie das Thema aus, das Sie für das Budget konfiguriert haben.

  5. Stellen Sie den Quellcode und die Abhängigkeiten für die auszuführende Funktion bereit.

  6. Achten Sie darauf, dass für die Auszuführende Funktion der richtige Funktionsname festgelegt ist.

Die Seite "Funktion erstellen" im Bereich "Cloud Functions" der Google Cloud Console. Sie enthält den Funktionsnamen, die Menge des zugewiesenen Speichers, die Art des Triggers und das Pub/Sub-Thema, das Sie für Ihr Budget konfiguriert haben.

Cloud Functions-Funktion beschreiben

Damit die Cloud Functions-Funktion weiß, was mit der Benachrichtigung geschehen soll, können Sie entweder Code mit dem Inline-Editor schreiben oder eine Datei hochladen. Weitere Informationen zu den Benachrichtigungen, die der Code erhalten wird, finden Sie unter Benachrichtigungsformat.

Eine Funktion kann etwa empfangene Pub/Sub-Benachrichtigungen, Attribute und Daten in ein Log schreiben, wenn sie durch eine Budgetbenachrichtigung ausgelöst wird. Weitere Informationen finden Sie unter Pub/Sub-Trigger.

Cloud Functions-Ereignisse ansehen

Nachdem Sie die Cloud Functions-Funktion gespeichert haben, können Sie auf LOGS ANSEHEN klicken, um die protokollierten Budgetbenachrichtigungen aufzurufen. Es werden die Logs der Funktionsaufrufe angezeigt.

Die Abbildung zeigt die Stelle, an der Sie "Logs ansehen" auf dem Bildschirm finden, sowie die Liste der Ereignisse der Cloud Functions-Funktion in der Google Cloud Console.

Cloud Functions-Funktion testen

Benachrichtigungen werden an Pub/Sub gesendet und Abonnenten erhalten die Nachrichten. Wenn Sie eine Beispielbenachrichtigung testen möchten, um zu prüfen, ob sich Ihre Funktion wie erwartet verhält, veröffentlichen Sie eine Nachricht in Pub/Sub. Verwenden Sie dazu dieses Objekt als Nachrichtentext:

{
    "budgetDisplayName": "name-of-budget",
    "alertThresholdExceeded": 1.0,
    "costAmount": 100.01,
    "costIntervalStart": "2019-01-01T00:00:00Z",
    "budgetAmount": 100.00,
    "budgetAmountType": "SPECIFIED_AMOUNT",
    "currencyCode": "USD"
}

Sie können auch Nachrichtenattribute wie die billing-konto-ID hinzufügen. Weitere Informationen finden Sie in der vollständigen Benachrichtigungsformat dokumentation.

Benachrichtigungen an Slack senden

E-Mails sind nicht immer der beste Weg, um hinsichtlich der Cloud-Kosten auf dem Laufenden zu bleiben, insbesondere wenn ein bestimmtes Budget unbedingt eingehalten werden muss und zeitkritisch ist. Mit Benachrichtigungen können Sie Budgetnachrichten an andere Medien weiterleiten.

In diesem Beispiel wird beschrieben, wie Budgetbenachrichtigungen an Slack weitergeleitet werden. Hierbei nutzt eine Cloud Functions-Funktion jedes Mal, wenn von Cloud Billing eine Budgetbenachrichtigung veröffentlicht wird, einen Bot, um eine Nachricht an einen Slack-Kanal des Bot-Arbeitsbereichs zu senden.

Slack-Kanal und Berechtigungen einrichten

Der erste Schritt besteht darin, den Slack-Arbeitsbereich und die Bot-Nutzertokens zu erstellen, die zum Aufrufen der Slack API verwendet werden. API-Tokens können unter https://api.slack.com/apps verwaltet werden. Weitere Informationen finden Sie unter Bot Users auf der Slack-Website.

Slack-Benachrichtigungen konfigurieren

Cloud Functions-Funktion erstellen

  1. Erstellen Sie eine neue Funktion. Folgen Sie dazu den Schritten unter Cloud Functions-Funktion erstellen. Achten Sie darauf, dass der Trigger auf dasselbe Pub/Sub-Thema gesetzt ist, das vom Budget verwendet wird.

  2. Fügen Sie Abhängigkeiten hinzu:

    Node.js

    Kopieren Sie Folgendes in Ihre package.json-Datei:

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha -p -j 2 test/periodic.test.js --timeout=600000",
        "test": "c8 mocha -p -j 2 test/index.test.js --timeout=5000 --exit"
      },
      "author": "Ace Nassri <anassri@google.com>",
      "license": "Apache-2.0",
      "dependencies": {
        "@google-cloud/billing": "^4.0.0",
        "@google-cloud/compute": "^4.0.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^126.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^8.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^16.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    Kopieren Sie Folgendes in Ihre requirements.txt-Datei:

    slackclient==2.9.4
    google-api-python-client==2.87.0
    

  3. Schreiben Sie Code oder verwenden Sie das folgende Beispiel, um Budgetbenachrichtigungen mithilfe der Slack API in einem Slack-Chat-Kanal zu posten.

  4. Prüfen Sie, ob die folgenden postMessage-Parameter für Slack API korrekt festgelegt sind:

    • OAuth-Zugriffstoken des Bot-Nutzers
    • Name des Kanals

Beispielcode:

Node.js

const slack = require('slack');

// TODO(developer) replace these with your own values
const BOT_ACCESS_TOKEN =
  process.env.BOT_ACCESS_TOKEN || 'xxxx-111111111111-abcdefghidklmnopq';
const CHANNEL = process.env.SLACK_CHANNEL || 'general';

exports.notifySlack = async pubsubEvent => {
  const pubsubAttrs = pubsubEvent.attributes;
  const pubsubData = Buffer.from(pubsubEvent.data, 'base64').toString();
  const budgetNotificationText = `${JSON.stringify(
    pubsubAttrs
  )}, ${pubsubData}`;

  await slack.chat.postMessage({
    token: BOT_ACCESS_TOKEN,
    channel: CHANNEL,
    text: budgetNotificationText,
  });

  return 'Slack notification sent successfully';
};

Python

import base64
import json
import os

import slack
from slack.errors import SlackApiError

# See https://api.slack.com/docs/token-types#bot for more info
BOT_ACCESS_TOKEN = "xxxx-111111111111-abcdefghidklmnopq"
CHANNEL = "C0XXXXXX"

slack_client = slack.WebClient(token=BOT_ACCESS_TOKEN)

def notify_slack(data, context):
    pubsub_message = data

    # For more information, see
    # https://cloud.google.com/billing/docs/how-to/budgets-programmatic-notifications#notification_format
    try:
        notification_attr = json.dumps(pubsub_message["attributes"])
    except KeyError:
        notification_attr = "No attributes passed in"

    try:
        notification_data = base64.b64decode(data["data"]).decode("utf-8")
    except KeyError:
        notification_data = "No data passed in"

    # This is just a quick dump of the budget data (or an empty string)
    # You can modify and format the message to meet your needs
    budget_notification_text = f"{notification_attr}, {notification_data}"

    try:
        slack_client.api_call(
            "chat.postMessage",
            json={"channel": CHANNEL, "text": budget_notification_text},
        )
    except SlackApiError:
        print("Error posting to Slack")

Jetzt können Sie Ihre Cloud Functions-Funktion testen, um in Slack eine Nachricht angezeigt zu sehen.

Kosten deckeln (Abrechnung deaktivieren) und Nutzung stoppen

In diesem Beispiel erfahren Sie, wie Sie Kosten deckeln und die Nutzung eines Projekts stoppen können, indem Sie Cloud Billing deaktivieren. Wenn Sie die Abrechnung für ein Projekt deaktivieren, werden alle Google Cloud-Dienste im Projekt beendet, einschließlich der kostenlosen Dienste.

Warum die Abrechnung deaktivieren?

Möglicherweise sind Sie gezwungen, die Kosten zu deckeln, weil Sie bei Ihren Ausgaben für Google Cloud eine feste Obergrenze einhalten müssen. Dies trifft üblicherweise auf Studenten, Forscher oder Entwickler zu, die in einer Sandbox-Umgebung arbeiten. In diesen Fällen möchten Sie die Ausgaben begrenzen und sind möglicherweise bereit, alle Ihre Google Cloud-Dienste und deren Nutzung zu beenden, wenn das Budgetlimit erreicht ist.

In unserem Beispiel verwenden wir acme-backend-dev als Nicht-Produktionsprojekt, für das Cloud Billing ohne Bedenken deaktiviert werden kann.

Budgetdeckelung in der Google Cloud Console konfigurieren

Bevor Sie dieses Beispiel verwenden, sollten Sie Folgendes tun:

Die Abbildung zeigt die Liste der Cloud Billing-Benachrichtigungen in der Google Cloud Console.

Cloud Functions-Funktion erstellen

Als Nächstes müssen Sie Ihre Cloud Functions-Funktion so konfigurieren, dass sie die Cloud Billing API aufruft. Dadurch kann Cloud Functions das Cloud Billing-Tool für unser Beispielprojekt acme-backend-dev deaktivieren.

  1. Erstellen Sie eine neue Funktion. Folgen Sie dazu den Schritten unter Cloud Functions-Funktion erstellen. Achten Sie darauf, dass der Trigger auf dasselbe Pub/Sub-Thema gesetzt ist, das vom Budget verwendet wird.

  2. Fügen Sie die folgenden Abhängigkeiten hinzu:

    Node.js

    Kopieren Sie Folgendes in Ihre package.json-Datei:

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha -p -j 2 test/periodic.test.js --timeout=600000",
        "test": "c8 mocha -p -j 2 test/index.test.js --timeout=5000 --exit"
      },
      "author": "Ace Nassri <anassri@google.com>",
      "license": "Apache-2.0",
      "dependencies": {
        "@google-cloud/billing": "^4.0.0",
        "@google-cloud/compute": "^4.0.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^126.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^8.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^16.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    Kopieren Sie Folgendes in Ihre requirements.txt-Datei:

    slackclient==2.9.4
    google-api-python-client==2.87.0
    

  3. Kopieren Sie den unten stehenden Code in die Cloud Functions-Funktion.

  4. Stellen Sie die auszuführende Funktion auf "stopBilling" (Node) oder "stop_billing" (Python) ein.

  5. Abhängig von Ihrer Laufzeit wird die Umgebungsvariable GOOGLE_CLOUD_PROJECT möglicherweise automatisch festgelegt. Überprüfen Sie die Liste der automatisch festgelegten Umgebungsvariablen und bestimmen Sie, ob die Variable GOOGLE_CLOUD_PROJECT manuell auf das Projekt festgelegt werden soll, für das Sie Cloud Billing deckeln (deaktivieren) möchten.

Node.js

const {CloudBillingClient} = require('@google-cloud/billing');
const {InstancesClient} = require('@google-cloud/compute');

const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
const PROJECT_NAME = `projects/${PROJECT_ID}`;
const billing = new CloudBillingClient();

exports.stopBilling = async pubsubEvent => {
  const pubsubData = JSON.parse(
    Buffer.from(pubsubEvent.data, 'base64').toString()
  );
  if (pubsubData.costAmount <= pubsubData.budgetAmount) {
    return `No action necessary. (Current cost: ${pubsubData.costAmount})`;
  }

  if (!PROJECT_ID) {
    return 'No project specified';
  }

  const billingEnabled = await _isBillingEnabled(PROJECT_NAME);
  if (billingEnabled) {
    return _disableBillingForProject(PROJECT_NAME);
  } else {
    return 'Billing already disabled';
  }
};

/**
 * Determine whether billing is enabled for a project
 * @param {string} projectName Name of project to check if billing is enabled
 * @return {bool} Whether project has billing enabled or not
 */
const _isBillingEnabled = async projectName => {
  try {
    const [res] = await billing.getProjectBillingInfo({name: projectName});
    return res.billingEnabled;
  } catch (e) {
    console.log(
      'Unable to determine if billing is enabled on specified project, assuming billing is enabled'
    );
    return true;
  }
};

/**
 * Disable billing for a project by removing its billing account
 * @param {string} projectName Name of project disable billing on
 * @return {string} Text containing response from disabling billing
 */
const _disableBillingForProject = async projectName => {
  const [res] = await billing.updateProjectBillingInfo({
    name: projectName,
    resource: {billingAccountName: ''}, // Disable billing
  });
  return `Billing disabled: ${JSON.stringify(res)}`;
};

Python

import base64
import json
import os

from googleapiclient import discovery

PROJECT_ID = os.getenv("GCP_PROJECT")
PROJECT_NAME = f"projects/{PROJECT_ID}"
def stop_billing(data, context):
    pubsub_data = base64.b64decode(data["data"]).decode("utf-8")
    pubsub_json = json.loads(pubsub_data)
    cost_amount = pubsub_json["costAmount"]
    budget_amount = pubsub_json["budgetAmount"]
    if cost_amount <= budget_amount:
        print(f"No action necessary. (Current cost: {cost_amount})")
        return

    if PROJECT_ID is None:
        print("No project specified with environment variable")
        return

    billing = discovery.build(
        "cloudbilling",
        "v1",
        cache_discovery=False,
    )

    projects = billing.projects()

    billing_enabled = __is_billing_enabled(PROJECT_NAME, projects)

    if billing_enabled:
        __disable_billing_for_project(PROJECT_NAME, projects)
    else:
        print("Billing already disabled")

def __is_billing_enabled(project_name, projects):
    """
    Determine whether billing is enabled for a project
    @param {string} project_name Name of project to check if billing is enabled
    @return {bool} Whether project has billing enabled or not
    """
    try:
        res = projects.getBillingInfo(name=project_name).execute()
        return res["billingEnabled"]
    except KeyError:
        # If billingEnabled isn't part of the return, billing is not enabled
        return False
    except Exception:
        print(
            "Unable to determine if billing is enabled on specified project, assuming billing is enabled"
        )
        return True

def __disable_billing_for_project(project_name, projects):
    """
    Disable billing for a project by removing its billing account
    @param {string} project_name Name of project disable billing on
    """
    body = {"billingAccountName": ""}  # Disable billing
    try:
        res = projects.updateBillingInfo(name=project_name, body=body).execute()
        print(f"Billing disabled: {json.dumps(res)}")
    except Exception:
        print("Failed to disable billing, possibly check permissions")

Dienstkontoberechtigungen konfigurieren

Die Cloud Functions-Funktion wird als automatisch erstelltes Dienstkonto ausgeführt. Damit die Abrechnung durch das Dienstkonto deaktiviert werden kann, müssen Sie dem Konto die entsprechenden Berechtigungen erteilen, z. B. Abrechnungsadministrator.

Sehen Sie sich die Details der Cloud Functions-Funktion an, um das richtige Dienstkonto zu ermitteln. Das Dienstkonto ist am Ende der Seite aufgeführt.

Die Abbildung zeigt, wo die Dienstkontoinformationen im Bereich "Cloud Functions" der Google Cloud Console zu finden sind.

Sie können die Rechnungskontoadministrator-Berechtigungen in der Google Cloud Console auf der Seite Abrechnung verwalten.

Wählen Sie den Namen des Dienstkontos aus, um dem Konto Rechnungskonto-Administratorberechtigungen zu erteilen.

Die Abbildung zeigt, wo im Bereich "Berechtigungen" der Google Cloud Console der Name des Dienstkontos und die Rolle "Rechnungskontoadministrator" ausgewählt werden.

Deaktivierung von Cloud Billing prüfen

Wenn das Budget eine Benachrichtigung sendet, hat das angegebene Projekt kein Cloud-Rechnungskonto mehr. Wenn Sie die Funktion testen möchten, veröffentlichen Sie eine Beispielnachricht mit der obigen Testnachricht. Das Projekt wird nicht mehr unter dem Cloud-Rechnungskonto angezeigt und die Ressourcen im Projekt sind deaktiviert. Dies gilt auch für die Cloud Functions-Funktion, wenn sie sich im selben Projekt befindet.

Die Abbildung zeigt, dass das Beispielprojekt nicht mehr in der Liste der Projekte angezeigt wird, die mit dem Cloud-Rechnungskonto verknüpft sind. Dadurch wird bestätigt, dass Cloud Billing für das Projekt deaktiviert ist.

Sie können in der Google Cloud Console Cloud Billing für Ihr Projekt manuell wieder aktivieren.

Nutzung selektiv steuern

Die oben anhand des Beispiels beschriebene Kostendeckelung (Deaktivierung von Cloud Billing) ist binär und endgültig. Das Projekt ist entweder aktiviert oder deaktiviert. Wenn es deaktiviert ist, werden keine Dienste mehr ausgeführt und alle Ressourcen werden schließlich gelöscht.

Wenn Sie eine differenziertere Rückmeldung benötigen, können Sie Ressourcen selektiv steuern. Wenn Sie beispielsweise einige Compute Engine-Ressourcen beenden, Cloud Storage aber intakt lassen möchten, können Sie die Nutzung selektiv steuern. Dadurch werden Ihre Kosten pro Stunde gesenkt, ohne Ihre Umgebung vollständig zu deaktivieren.

Sie können je nach Anforderungen eine individuelle Differenzierung vornehmen. In diesem Beispiel dient unser Projekt einer Untersuchung mithilfe einer Reihe von virtuellen Compute Engine-Maschinen. Die Ergebnisse werden in Cloud Storage gespeichert. In unserem Beispiel einer Cloud Functions-Funktion werden alle Compute Engine-Instanzen heruntergefahren. Dies hat aber keinen Einfluss auf unsere gespeicherten Ergebnisse, nachdem das Budget überschritten wurde.

Cloud Functions-Funktion erstellen

  1. Erstellen Sie eine neue Funktion. Folgen Sie dazu den Schritten unter Cloud Functions-Funktion erstellen. Achten Sie darauf, dass der Trigger auf dasselbe Pub/Sub-Thema gesetzt ist, das vom Budget verwendet wird.

  2. Prüfen Sie noch einmal, ob Sie die unter Kosten deckeln (Abrechnung deaktivieren) und Nutzung stoppen beschriebenen Abhängigkeiten hinzugefügt haben.

  3. Kopieren Sie den unten stehenden Code in die Cloud Functions-Funktion.

  4. Stellen Sie die auszuführende Funktion auf "limitUse" (Node) oder "limit_use" (Python) ein.

  5. Abhängig von Ihrer Laufzeit wird die Umgebungsvariable GCP_PROJECT möglicherweise automatisch festgelegt. Prüfen Sie die Liste der automatisch festgelegten Umgebungsvariablen und legen Sie fest, ob die Variable GCP_PROJECT manuell für das Projekt festgelegt werden muss, auf dem die virtuellen Maschinen ausgeführt werden.

  6. Legen Sie den Parameter ZONE fest. Dies ist die Zone, in der Instanzen für dieses Beispiel beendet werden.

Node.js

const {CloudBillingClient} = require('@google-cloud/billing');
const {InstancesClient} = require('@google-cloud/compute');

const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
const PROJECT_NAME = `projects/${PROJECT_ID}`;
const instancesClient = new InstancesClient();
const ZONE = 'us-central1-a';

exports.limitUse = async pubsubEvent => {
  const pubsubData = JSON.parse(
    Buffer.from(pubsubEvent.data, 'base64').toString()
  );
  if (pubsubData.costAmount <= pubsubData.budgetAmount) {
    return `No action necessary. (Current cost: ${pubsubData.costAmount})`;
  }

  const instanceNames = await _listRunningInstances(PROJECT_ID, ZONE);
  if (!instanceNames.length) {
    return 'No running instances were found.';
  }

  await _stopInstances(PROJECT_ID, ZONE, instanceNames);
  return `${instanceNames.length} instance(s) stopped successfully.`;
};

/**
 * @return {Promise} Array of names of running instances
 */
const _listRunningInstances = async (projectId, zone) => {
  const [instances] = await instancesClient.list({
    project: projectId,
    zone: zone,
  });
  return instances
    .filter(item => item.status === 'RUNNING')
    .map(item => item.name);
};

/**
 * @param {Array} instanceNames Names of instance to stop
 * @return {Promise} Response from stopping instances
 */
const _stopInstances = async (projectId, zone, instanceNames) => {
  await Promise.all(
    instanceNames.map(instanceName => {
      return instancesClient
        .stop({
          project: projectId,
          zone: zone,
          instance: instanceName,
        })
        .then(() => {
          console.log(`Instance stopped successfully: ${instanceName}`);
        });
    })
  );
};

Python

import base64
import json
import os

from googleapiclient import discovery

PROJECT_ID = os.getenv("GCP_PROJECT")
PROJECT_NAME = f"projects/{PROJECT_ID}"
ZONE = "us-west1-b"

def limit_use(data, context):
    pubsub_data = base64.b64decode(data["data"]).decode("utf-8")
    pubsub_json = json.loads(pubsub_data)
    cost_amount = pubsub_json["costAmount"]
    budget_amount = pubsub_json["budgetAmount"]
    if cost_amount <= budget_amount:
        print(f"No action necessary. (Current cost: {cost_amount})")
        return

    compute = discovery.build(
        "compute",
        "v1",
        cache_discovery=False,
    )
    instances = compute.instances()

    instance_names = __list_running_instances(PROJECT_ID, ZONE, instances)
    __stop_instances(PROJECT_ID, ZONE, instance_names, instances)

def __list_running_instances(project_id, zone, instances):
    """
    @param {string} project_id ID of project that contains instances to stop
    @param {string} zone Zone that contains instances to stop
    @return {Promise} Array of names of running instances
    """
    res = instances.list(project=project_id, zone=zone).execute()

    if "items" not in res:
        return []

    items = res["items"]
    running_names = [i["name"] for i in items if i["status"] == "RUNNING"]
    return running_names

def __stop_instances(project_id, zone, instance_names, instances):
    """
    @param {string} project_id ID of project that contains instances to stop
    @param {string} zone Zone that contains instances to stop
    @param {Array} instance_names Names of instance to stop
    @return {Promise} Response from stopping instances
    """
    if not len(instance_names):
        print("No running instances were found.")
        return

    for name in instance_names:
        instances.stop(project=project_id, zone=zone, instance=name).execute()
        print(f"Instance stopped successfully: {name}")

Dienstkontoberechtigungen konfigurieren

  1. Die Cloud-Funktion wird als automatisch erstelltes Dienstkonto ausgeführt. Zum Steuern der Nutzung müssen Sie dem Dienstkonto für alle Dienste im Projekt die Berechtigungen erteilen, die benötigt werden, um Änderungen vorzunehmen.
  2. Sehen Sie sich die Details der Cloud Functions-Funktion an, um das richtige Dienstkonto zu ermitteln. Das Dienstkonto ist am Ende der Seite aufgeführt.
  3. Rufen Sie in der Google Cloud Console die Seite IAM auf, um geeignete Berechtigungen festzulegen.
    Zur Seite "IAM"
     
    Zeigt den IAM-Bildschirm in der Google Cloud Console an, auf dem Sie die entsprechenden Berechtigungen für das Dienstkonto festlegen können, mit dem die Cloud Functions-Funktion ausgeführt wird.

Prüfen, ob Instanzen beendet wurden

Wenn das Budget eine Benachrichtigung sendet, wurden die virtuellen Compute Engine-Maschinen beendet.

Wenn Sie die Funktion testen möchten, veröffentlichen Sie eine Beispielnachricht mit der obigen Testnachricht. Um zu bestätigen, dass die Funktion erfolgreich ausgeführt wurde, prüfen Sie Ihre virtuellen Compute Engine-Maschinen in der Google Cloud Console.