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

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

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

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

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

במסמך הזה מוצגות דוגמאות והוראות מפורטות לניהול העלויות באופן אוטומטי באמצעות התראות לגבי תקציב באמצעות פונקציות Cloud Run.

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

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

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

  • נושא Pub/Sub: זו נקודת הקצה של התראות לגבי תקציב.
  • מזהה התקציב: זהו המזהה הייחודי של התקציב, שמצוין בכל ההתראות. אפשר למצוא את מזהה התקציב בהגדרות התקציב, בקטע Manage notifications. מזהה התקציב מוצג אחרי שבוחרים באפשרות Connect a Pub/Sub topic to this budget.

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

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

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

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

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

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

  1. נכנסים לדף Cloud Run functions במסוף Google Cloud .

    כניסה לדף Cloud Run functions

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

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

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

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

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

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

תיאור פונקציית Cloud Run

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

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

הצגת האירועים של פונקציות Cloud Run

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

‫תמונה שבה רואים איפה נמצאת האפשרות View Logs במסך, ואת רשימת האירועים של פונקציית Cloud Run במסוף ‎ Google Cloud ‎.

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

ההתראות נשלחות ל-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 Run שמפרסמת הודעה בערוץ Slack באמצעות בוט, בסביבת העבודה של הבוט.

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

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

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

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

  1. תוכלו להיעזר בהוראות שבמאמר יצירה של פונקציית Cloud Run כדי ליצור פונקציות חדשות. חשוב לוודא שהגדרתם את הטריגר כך שישתמש באותו נושא 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": ">=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": "^143.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^10.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^18.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

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

    slackclient==2.9.4
    google-api-python-client==2.131.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 Run כדי לראות את ההודעה שמופיעה ב-Slack.

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

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

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

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

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

‫הגדרת מגבלת תקציב במסוף Google Cloud .

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

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

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

‫בתמונה מוצגת רשימה של התראות חיוב ב-Cloud במסוף‎ Google Cloud ‎.

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

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

  1. תוכלו להיעזר בהוראות שבמאמר יצירה של פונקציית Cloud Run כדי ליצור פונקציות חדשות. חשוב לוודא שהגדרתם את הטריגר כך שישתמש באותו נושא 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": ">=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": "^143.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^10.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^18.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

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

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

  3. מעתיקים את הקוד הבא לפונקציית Cloud Run.

  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 Run מופעלת בתור חשבון שירות שנוצר באופן אוטומטי. כדי שחשבון השירות יוכל להשבית את החיוב, צריך לתת לו את ההרשאות הדרושות כמו אדמין לענייני חיוב.

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

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

אפשר לנהל את ההרשאות של האדמין לענייני חיוב בדף Billing במסוף Google Cloud .

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

‫תמונה שממחישה איפה בוחרים את השם של חשבון השירות ואת התפקיד &quot;אדמין של חשבון לחיוב&quot; בקטע Permissions במסוף ‎ Google Cloud ‎.

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

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

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

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

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

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

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

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

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

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

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

  3. מעתיקים את הקוד הבא לפונקציית Cloud Run.

  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 Run מופעלת בתור חשבון שירות שנוצר באופן אוטומטי. כדי לשלוט בשימוש, תצטרכו לתת לחשבון השירות הרשאות לכל השירותים בפרויקט שצריכים לשנות.
  2. כדי לזהות את חשבון השירות הנכון, תוכלו לבדוק את הפרטים של פונקציית Cloud Run. חשבון השירות מופיע בחלק התחתון של הדף.
  3. כדי להגדיר את ההרשאות המתאימות, במסוף Google Cloud , נכנסים לדף IAM.
    כניסה לדף IAM
     
    ‫מסך IAM במסוף Google Cloud , שבו אפשר להגדיר את ההרשאות המתאימות לחשבון השירות שמפעיל את הפונקציה ב-Cloud Functions.

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

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

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