דוגמאות לתשובות אוטומטיות של בקרת עלות

דוגמה לארכיטקטורה

תרשים שמדגים איך משתמשים בהתראות פרוגרמטיות לגבי תקציב כדי ליצור תשובות אוטומטיות של בקרת עלות.
איור 1: המחשה לשימוש בהתראות תקציב לשליחת תשובות אוטומטיות של בקרת עלות, באמצעות התראות פרוגרמטיות של Pub/Sub ופונקציות של Cloud Functions.

אם אתם מודעים לעלויות ורוצים לנהל את הסביבה ביחס לתקציב שלכם, תוכלו להשתמש בהתראות פרוגרמטיות לגבי תקציב כדי לשלוח תשובות אוטומטיות של בקרת עלות על סמך התראת התקציב.

התראות התקציב מספקות סטטוס בזמן אמת של התקציב בחשבון לחיוב ב-Cloud באמצעות נושאי Pub/Sub, שמשתמשים ביכולת ההתאמה, בגמישות ובאמינות של תוכנה ארגונית מכוונת-הודעות לענן.

בדף הזה מוצגות דוגמאות והוראות מפורטות לניהול העלויות באופן אוטומטי באמצעות התראות תקציב עם פונקציות של Cloud Functions.

הגדרה של התראות תקציב

בשלב הראשון מפעילים נושא Pub/Sub לתקציב. הסבר מפורט זמין במאמר ניהול התראות פרוגרמטיות לגבי תקציב.

אחרי שמפעילים את התראות התקציב, חשוב לזכור:

  • נושא Pub/Sub: זו נקודת הקצה שהוגדרה להתראות התקציב.
  • מזהה התקציב: זהו המזהה הייחודי של התקציב, שמצוין בכל ההתראות. אפשר למצוא את מזהה התקציב בהגדרות התקציב, תחת ניהול התראות. מזהה התקציב מוצג אחרי שבוחרים באפשרות קשר נושא Pub/Sub לתקציב זה.

הקטע Manage notifications במסוף Google Cloud, שבו אפשר לשייך נושא Pub/Sub לתקציב. הקטע כולל את מזהה התקציב, שם הפרויקט ונושא ה-Pub/Sub.

האזנה להתראות

בשלב הבא מאזינים להודעות על ידי הרשמה לנושא ה-Pub/Sub. אם אין אף רישום, מערכת Pub/Sub לא תמסור את ההודעות שפורסמו ולא ניתן יהיה לשחזר אותן מאוחר יותר.

על אף שיש דרכים רבות להירשם לנושא, בדוגמאות הבאות נשתמש בטריגרים לפונקציות של Cloud Functions.

יצירת פונקציה של Cloud Function

כך יוצרים פונקציה חדשה:

  1. ב Google Cloud Console, עליך להיכנס לדף Cloud Functions.

    כניסה לדף Cloud Functions

  2. לוחצים על CREATE FUNCTION ונותנים לפונקציה שם שיש לו משמעות ביחס לתקציב שלכם.

  3. בקטע Trigger בוחרים באפשרות Pub/Sub topic.

  4. בוחרים את הנושא שהגדרתם לתקציב.

  5. מספקים את קוד המקור ואת יחסי התלות שדרושים להרצת הפונקציה.

  6. חשוב לוודא שהגדרתם את Function to execute לשם הנכון של הפונקציה.

צור דף פונקציות בקטע Cloud Functions במסוף של Google Cloud. הדף כולל את שם הפונקציה, את הקצאת הזיכרון, את סוג הטריגר ואת נושא Pub/Sub שהגדרתם לתקציב.

תיאור הפונקציה של Cloud Function

כדי להנחות את הפונקציה של Cloud Functions אילו צעדים לנקוט כשמתקבלת התראה, צריך לכתוב קוד בעורך המוטבע או להעלות קובץ. לפרטים לגבי ההתראות שהקוד יכול לקבל, קראו את המאמר פורמט של התראות.

דוגמה: כשפונקציה מופעלת על ידי התראת תקציב, היא עשויה לתעד את ההתראות שהתקבלו ב-Pub/Sub, יחד עם המאפיינים והנתונים שלהן. למידע נוסף קראו את המאמר טריגרים ב-Pub/Sub.

הצגת האירועים ב-Cloud Function

אחרי ששומרים את הפונקציה ב-Cloud Functions, אפשר ללחוץ על VIEW LOGS כדי להציג את התראות התקציב שנרשמו ביומן. פעולה זו תציג את היומנים מההפעלות של הפונקציה.

תמונה שממחישה את המיקום של האפשרות View Logsבמסך, ואת רשימת האירועים של פונקציית Cloud Functions Console ב.

בדיקת פונקציה של Cloud Function

ההתראות נשלחות ל-Pub/Sub, והאפליקציות הרשומות מקבלות את ההודעות. כדי לבדוק התראה לדוגמה ולוודא שהפונקציה פועלת כצפוי, צריך לפרסם הודעה ב-Pub/Sub ולהשתמש באובייקט הבא כגוף ההודעה:

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

אפשר גם להוסיף מאפיינים להודעה, כמו מספר החשבון לחיוב. למידע נוסף, ניתן לעיין במסמכי התיעוד המלאים בנושא פורמט של התראות.

שליחת התראות ל-Slack

אימייל הוא לא תמיד הדרך הטובה ביותר להתעדכן בעלויות בענן, במיוחד אם התקציב שלכם קריטי ותלוי זמן. בעזרת התראות, אתם יכולים להעביר הודעות לגבי התקציב דרך אמצעי תקשורת אחרים.

בדוגמה הזו מוסבר איך להעביר התראות לגבי תקציב ל-Slack. כך, בכל פעם שמתפרסמת התראה לגבי תקציב בחשבון לחיוב ב-Cloud, יש פונקציה ב-Cloud Functions שמפרסמת באמצעות בוט הודעה בערוץ Slack, בסביבת העבודה של הבוט.

הגדרת ערוץ והרשאות ב-Slack

בשלב הראשון יוצרים את סביבת העבודה ב-Slack ואת אסימוני המשתמשים שמשמשים את הבוטים כדי לקרוא ל-Slack API. אסימוני ה-API מנוהלים בכתובת https://api.slack.com/apps. הוראות מפורטות זמינות במאמר Bot Users באתר Slack.

הגדרת ההתראות ב-Slack.

כתיבת פונקציה של Cloud Function

  1. יוצרים פונקציה חדשה לפי ההוראות במאמר יצירת פונקציה ב-Cloud Functions. חשוב לוודא שהגדרתם את הטריגר כך שישתמש באותו נושא Pub/Sub שהוגדר לשימוש בתקציב.

  2. מוסיפים יחסי תלות:

    Node.js

    מעתיקים את הפרטים הבאים אל package.json:

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=12.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha test/periodic.test.js --timeout=600000",
        "test": "c8 mocha 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": "^3.1.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^123.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": "^15.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    מעתיקים את הפרטים הבאים אל requirements.txt:

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

  3. כותבים את הקוד הדרוש או משתמשים בדוגמה שלמטה כדי לפרסם התראות לגבי התקציב בערוץ הצ'אט של Slack באמצעות Slack API.

  4. מוודאים שהפרמטרים הבאים של postMessage ב-Slack API מוגדרים בצורה נכונה:

    • אסימון הגישה ל-OAuth של משתמש הבוט
    • שם הערוץ

קוד לדוגמה

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")

עכשיו אתם יכולים לבדוק את הפונקציה ב-Cloud Functions כדי לראות את ההודעה שמופיעה ב-Slack.

הגבלה (השבתה) של החיוב לצורך הפסקת השימוש

הדוגמה הזו ממחישה איך להגביל את העלויות ולהפסיק את השימוש בפרויקט, על ידי השבתת החיוב ב-Cloud. השבתת החיוב בפרויקט תגרום לכך שכל שירותי Google Cloud בפרויקט יופסקו, כולל שירותים בתוכנית ללא תשלום.

למה להשבית את החיוב?

יכול להיות שתצטרכו להגביל את העלויות אם יש מגבלה קשיחה על רמת ההוצאה שלכם ב-Google Cloud. המצב הזה אופייני לסטודנטים, לחוקרים ולמפתחים שעובדים בסביבות ארגז חול. במקרים כאלה כדאי להפסיק לצבור הוצאות, וכשתגיעו למגבלת התקציב תשביתו את כל שירותי Google Cloud ואת השימוש בהם.

לצורך הדוגמה שלנו, אנחנו משתמשים בפרויקט acme-backend-dev שאינו בסביבת ייצור, ועבורו אפשר להשבית את החיוב חיוב ב-Cloud באופן בטוח.

.Google Cloud Console-הגדר את מגבלת התקציב ב

לפני שמתחילים ליישם את הדוגמה הזו, חשוב לוודא שביצעתם את הפעולות הבאות:

  • הפעלת Cloud Billing API. כדי להשבית את החיוב ב-Cloud בפרויקט, הפונקציה ב-Cloud Functions צריכה לקרוא ל-Cloud Billing API.

  • הגדרת תקציב כדי לעקוב אחרי העלויות בפרויקט ולהפעיל התראות לגבי תקציב.

 Google Cloud Console-ב Cloud-הצג את רשימת התראות החיוב ב

כתיבת פונקציה של Cloud Function

בשלב הבא צריך להגדיר את הפונקציה ב-Cloud Functions כך שתקרא ל-Cloud Billing API. באופן כזה, הפונקציה ב-Cloud Functions יכולה להשבית את החיוב ב-Cloud בפרויקט לדוגמה acme-backend-dev:

  1. יוצרים פונקציה חדשה לפי ההוראות במאמר יצירת פונקציה ב-Cloud Functions. חשוב לוודא שהגדרתם את הטריגר כך שישתמש באותו נושא Pub/Sub שהוגדר לשימוש בתקציב.

  2. מוסיפים את יחסי התלות הבאים:

    Node.js

    מעתיקים את הפרטים הבאים אל package.json:

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=12.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha test/periodic.test.js --timeout=600000",
        "test": "c8 mocha 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": "^3.1.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^123.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": "^15.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    מעתיקים את הפרטים הבאים אל requirements.txt:

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

  3. מעתיקים את הקוד שבהמשך לפונקציה ב-Cloud Functions.

  4. מגדירים את הפונקציה לביצוע "stopBilling" (ב-Node) או "stop_billing" (ב-Python).

  5. בהתאם לזמן הריצה, יכול להיות שמשתנה הסביבה GOOGLE_CLOUD_PROJECT יוגדר באופן אוטומטי. עליכם לבדוק את רשימת משתני הסביבה שמוגדרים באופן אוטומטי כדי לקבוע אם אתם צריכים להגדיר באופן ידני את המשתנה GOOGLE_CLOUD_PROJECT כך שיכיל את הפרויקט שעבורו אתם רוצים להגביל (להשבית) את החיוב חיוב ב-Cloud.

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")

הגדרת הרשאות לחשבון שירות

הפונקציה שלכם ב-Cloud Functions מופעלת בתור חשבון שירות שנוצר באופן אוטומטי. כדי שחשבון השירות יוכל להשבית את החיוב, עליכם לתת לו את ההרשאות הדרושות, כמו אדמין לענייני חיוב.

כדי לאתר את חשבון השירות הנכון, עליכם להציג את פרטי הפונקציה ב-Cloud Functions. חשבון השירות מופיע בחלק התחתון של הדף.

תמונה שממחישה איפה אפשר לראות את פרטי חשבון השירות בקטע של פונקציה של Cloud Functions במסוף Google Cloud.

ניתן לך לנהל את ההרשאות של האדמין לענייני חיוב בדף חיוב‬ ב Google Cloud Console.

כדי לתת לחשבון השירות הרשאות של אדמין של חשבון לחיוב, בוחרים את השם של חשבון השירות.

תמונה שממחישה איפה בוחרים את השם של חשבון השירות ואת התפקיד 'אדמין של חשבון לחיוב' בקטע Permissions במסוף.

בדיקה אם החיוב חיוב ב-Cloud מושבת

כשתישלח התראה לגבי התקציב, לפרויקט שצוין לא יהיה מוגדר יותר חשבון לחיוב ב-Cloud. אם אתם רוצים לבדוק את הפונקציה, פרסמו הודעה לדוגמה עם ההודעה לבדיקה שלמעלה. הפרויקט לא יופיע יותר בחשבון לחיוב ב-Cloud והמשאבים שכלולים בפרויקט יושבתו, כולל הפונקציה ב-Cloud Functions אם היא משויכת לאותו הפרויקט.

תמונה שממחישה שהפרויקט לדוגמה לא מופיע יותר ברשימת הפרויקטים שמקושרים לחשבון לחיוב ב-Cloud. זה מאמת שהחיוב ב-חיוב ב-Cloud מושבת בפרויקט.

אפשרלהפעיל מחדש באופן ידני את החיוב ב-Cloud עבור הפרויקט דרך Google Cloud Console

שליטה סלקטיבית בשימוש

ההגבלה (השבתה) של החיוב ב-Cloud כפי שמתואר בדוגמה הקודמת היא בינארית וסופית. הפרויקט שלכם יכול להיות באחד משני מצבים: מופעל או מושבת. כשהפרויקט מושבת, כל השירותים מופסקים ובסופו של דבר כל המשאבים נמחקים.

אם אתם צריכים להגיב בצורה פחות גורפת, תוכלו לשלוט באופן סלקטיבי במשאבים. לדוגמה, אם אתם רוצים להפסיק כמה משאבים של Compute Engine אבל להשאיר את Cloud Storage ללא שינוי, תוכלו לקבוע את השימוש באופן סלקטיבי. כך תוכלו להפחית את העלות לשעה בלי להשבית לחלוטין את הסביבה.

אתם יכולים לכתוב מדיניות מפורטת ככל שתצטרכו. עם זאת, לצורך הדוגמה, הפרויקט שלנו מריץ מחקר על כמה מכונות וירטואליות של Compute Engine, והתוצאות מאוחסנות ב-Cloud Storage. הפונקציה לדוגמה ב-Cloud Functions תשבית את כל המכונות של Compute Engine, אבל לא תשפיע על התוצאות המאוחסנות אחרי חריגה מהתקציב.

כתיבת פונקציה של Cloud Function

  1. יוצרים פונקציה חדשה לפי ההוראות במאמר יצירת פונקציה ב-Cloud Functions. חשוב לוודא שהגדרתם את הטריגר כך שישתמש באותו נושא Pub/Sub שהוגדר לשימוש בתקציב.

  2. מוודאים שהוספתם את יחסי התלות המתוארים בהגבלה (השבתה) של החיוב לצורך הפסקת השימוש.

  3. מעתיקים את הקוד שבהמשך לפונקציה ב-Cloud Functions.

  4. מגדירים את הפונקציה לביצוע "limitUse" (ב-Node) או "limit_use" (ב-Python).

  5. בהתאם לזמן הריצה, יכול להיות שמשתנה הסביבה GCP_PROJECT יוגדר באופן אוטומטי. עליכם לבדוק את רשימת משתני הסביבה שמוגדרים באופן אוטומטי כדי לקבוע אם אתם צריכים להגדיר באופן ידני את המשתנה GCP_PROJECT כך שיכיל את הפרויקט שבו פועלות המכונות הווירטואליות.

  6. מגדירים את הפרמטר ZONE. זהו התחום (zone) שבו יופסקו המכונות בדוגמה הזו.

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}")

הגדרת הרשאות לחשבון שירות

  1. את הפונקציה ב-Cloud Functions מריצים כחשבון שירות שנוצר באופן אוטומטי. כדי לאפשר שליטה בשימוש, עליכם לתת לחשבון השירות הרשאות לכל השירותים בפרויקט שהשירות אמור לשנות.
  2. כדי לאתר את חשבון השירות הנכון, מציגים את פרטי הפונקציה ב-Cloud Functions. חשבון השירות מופיע בחלק התחתון של הדף.
  3. ב-Google Cloud Console, היכנסו לדף IAM כדי להגדיר את ההרשאות המתאימות.
    כניסה לדף IAM
     
    מסך IAM במסוף, שבו אפשר להגדיר את ההרשאות המתאימות לחשבון השירות שמפעיל את הפונקציה ב-Cloud Functions.

בדיקה שהמכונות הופסקו

כשתישלח התראה לגבי התקציב, המכונות הווירטואליות של Compute Engine יופסקו.

כדי לבדוק את הפונקציה, פרסמו הודעה לדוגמה עם ההודעה לבדיקה שלמעלה. כדי לוודא שהפונקציה פועלת כמו שצריך, בדקו את המכונות הווירטואליות של Compute Engine ב- Google Cloud Console