程序化预算通知示例

如果您想要节省费用且需要根据预算控制环境,则可以使用程序化预算通知来自动执行与预算有关的操作。

预算通知使用 Cloud Pub/Sub 通知来提供实时预算状态,同时将企业消息传递中间件所具备的可伸缩性、灵活性和可靠性带到了到云端。

本页面包含一些示例和分步说明,介绍了如何结合使用预算通知与 Cloud Functions 来自动执行费用管理。

设置预算通知

第一步是为您的预算启用 Cloud Pub/Sub 通知。管理通知中介绍了此步骤。

启用预算通知后,请注意以下事项:

  • Pub/Sub 主题 - 为预算配置的通知端点。
  • 预算 ID - 包含在所有通知中的预算的唯一 ID。您可以在管理通知下的预算中找到该预算的 ID。在您选中将一个 Pub/Sub 主题关联到此预算后,系统即会显示该 ID。

在 GCP Console 中管理结算通知

监听通知

下一步是通过订阅 Pub/Sub 主题来监听通知。如果您没有订阅者帐号,Pub/Sub 将丢弃已发布的消息,您以后将无法再检索到它们。

虽然您可以通过多种方式订阅感兴趣的主题,但在以下示例中,我们将使用 Cloud Functions 函数触发器

创建 Cloud Functions 函数

要创建新的 Cloud Functions 函数,请执行以下操作:

  1. 转到 Google Cloud Platform Console 的 Cloud Functions 页面。创建一个新函数,为其指定一个对您的预算有意义的名称。
  2. 触发器下,选择 Cloud Pub/Sub 主题
  3. 选择您对预算配置的主题。
  4. 为要运行的函数提供源代码和依赖项。

在 GCP Console 中创建新函数

描述 Cloud Functions 函数

要告知 Cloud Functions 函数您希望它如何处理通知,您可以使用內嵌编辑器编写代码,也可以上传文件。如需详细了解您的代码将收到的通知,请参阅通知格式

例如,在被预算通知触发后,函数可能会记录收到的 Pub/Sub 通知、属性和数据。要了解详情,请参阅 Google Cloud Pub/Sub 触发器

查看 Cloud Functions 函数事件

保存 Cloud Functions 函数后,您可以点击查看日志以查看记录的预算通知。下图显示了有关函数调用的日志。

在 GCP Console 中查看 Cloud Functions 函数事件

向 Slack 发送通知

电子邮件并非在任何时候都是您及时了解云费用的最佳方式,尤其是在您的预算非常重要且具有高时效性的情况下。利用通知,您可以将预算消息转发给其他媒介。

在以下示例中,我们介绍了如何将预算通知转发给 Slack。通过这种转发,每次 Cloud Billing 发布预算通知时,Cloud Functions 函数都会使用一个聊天机器人将消息发布到聊天机器人工作区的 Slack 频道。

设置 Slack 频道和权限

第一步是创建 Slack 工作区以及用于调用 Slack API 的聊天机器人用户令牌。API 令牌可以通过 https://api.slack.com/apps 进行管理。如需查看详细说明,请参阅 Slack 网站上的聊天机器人用户

配置 Slack 通知

编写 Cloud Functions 函数

  1. 按照创建 Cloud Functions 函数中的步骤创建一个新函数。

  2. 添加以下依赖项:

    Python

    slackclient==1.3.0 添加到函数的 requirements.txt 文件:
    slackclient==1.3.0

  3. 编写代码,以使用 Slack API 将预算通知发布到 Slack 聊天频道。

  4. 在代码中设置以下 Slack API postMessage 参数:

    • 聊天机器人用户 OAuth 访问令牌
    • 频道
    • 文本

例如:

Python

from slackclient import SlackClient

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

CHANNEL_ID = 'C0XXXXXX'

slack_client = SlackClient(BOT_ACCESS_TOKEN)

def notify_slack(data, context):
    pubsub_message = data

    notification_attrs = json.dumps(pubsub_message['attributes'])
    notification_data = base64.b64decode(data['data']).decode('utf-8')
    budget_notification_text = f'{notification_attrs}, {notification_data}'

    slack_client.api_call(
      'chat.postMessage',
      channel=CHANNEL_ID,
      text=budget_notification_text)

通过限制(停用)结算功能来停止使用

下面的示例展示了如何设置费用上限,还通过停用结算功能停止了对 Google Cloud 项目的使用。这将导致所有 Google Cloud 服务都终止项目的非免费层级服务。

您可能会因可以花在 Google Cloud Platform 上的金额存在硬性限制而需要设置费用上限。这对于在沙盒环境中执行操作的学生、研究人员或开发者来说非常常见。在这些情况下,您希望停止支出,而且当达到预算上限时,您可能想要关停所有 Google Cloud Platform 服务并停止对它们的使用。

在我们的示例中,我们将“acme-backend-dev”用作非生产项目,该项目的结算功能可以安全停用。

在 GCP Console 中配置预算上限

在开始之前,您需要设置预算来监控项目费用,还需要启用预算通知。

在 GCP Console 中管理结算提醒

配置服务帐号权限

您的 Cloud Functions 函数作为自动创建的服务帐号运行。因此,该服务帐号可以停用结算功能,而您需要为其授予结算管理员权限。

要确定正确的服务帐号,请查看您的 Cloud Functions 函数详细信息。服务帐号显示在页面底部。

在 GCP Console 中确定服务帐号

您可以在 Google Cloud Platform Console 的“结算”页面上管理结算管理员权限

要向服务帐号授予结算帐号管理员权限,请选择服务帐号名称。

在 GCP Console 中管理权限

编写 Cloud Functions 函数

接下来,您需要配置 Cloud Functions 函数,以调用 Cloud Billing API。这样一来,Cloud Functions 函数便能够停用我们的示例项目“acme-backend-dev”的结算功能。

  1. 按照创建 Cloud Functions 函数中的步骤创建一个新函数。

  2. 添加以下依赖项:

    Python

    oauth2client==4.1.3google-api-python-client==1.7.4 添加到函数的 requirements.txt 文件:
    oauth2client==4.1.3
    google-api-python-client==1.7.4
    

  3. 编写代码,通过将项目从结算帐号中移除来停用其结算功能。

  4. 设置 PROJECT_NAME 参数。这是您要为其设置结算上限(停用结算功能)的那个项目。

Python

import base64
import json
import os
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
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,
        credentials=GoogleCredentials.get_application_default()
    )
    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}')

验证结算功能是否停用

当该函数被触发后,您可以验证您的 Cloud Functions 函数是否成功运行。如果成功,结算帐号下将不再显示该项目,而项目中的资源也将被停用。

验证结算功能是否停用

您可以在 GCP Console 中为项目手动重新启用结算功能

有选择地控制使用量

上面的示例中所述的设置上限(停用结算功能)操作只有两种最终结果。您的项目要么启用,要么停用。如果项目停用,所有服务都将停止,且所有资源最终会被删除。

如果您需要更精细的响应,则可以有选择地控制资源。例如,如果您希望停止某些计算资源,但存储资源保持不变,则可以有选择地控制使用量。这样可以降低每小时的费用,而不会完全停用您的环境。

您可以根据自己的需要编写细致入微的政策。但是,在我们的示例中,我们的项目正在使用一些计算虚拟机进行研究,还会将结果存储在 Cloud Storage 中。下面的 Cloud Functions 函数示例将关停所有计算实例,但在超出预算后不会影响我们存储的结果。

配置服务帐号权限

  1. 您的 Cloud Functions 函数作为自动创建的服务帐号运行。要控制使用量,您需要将服务帐号权限授予项目中需要进行更改的任何服务。
  2. 要确定正确的服务帐号,请查看您的 Cloud Functions 函数详细信息。服务帐号显示在页面底部。
  3. 转到 GCP Console 的“IAM”页面以设置适当的权限。
     
    在 GCP Console 中管理结算提醒

编写 Cloud Functions 函数

  1. 按照创建 Cloud Functions 函数中的步骤创建一个新函数。

  2. 确保您已添加了通过限制(停用)结算功能来停止使用中描述的依赖项。

  3. 编写代码以关停项目中的资源。

  4. 设置 PROJECT_NAME 参数。这是您要配置其费用上限的项目。

Python

import base64
import json
import os
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
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

    billing = discovery.build(
        'cloudbilling',
        'v1',
        cache_discovery=False,
        credentials=GoogleCredentials.get_application_default()
    )

    projects = billing.projects()

    if __is_billing_enabled(PROJECT_NAME, projects):
        print(__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
    """
    res = projects.getBillingInfo(name=project_name).execute()
    return res['billingEnabled']

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
    @return {string} Text containing response from disabling billing
    """
    body = {'billingAccountName': ''}  # Disable billing
    res = projects.updateBillingInfo(name=project_name, body=body).execute()
    print(f'Billing disabled: {json.dumps(res)}')

验证结算功能是否停用

您可以检查计算虚拟机是否已在 GCP Console 中停止,从而验证该函数是否成功运行。

此页内容是否有用?请给出您的反馈和评价:

发送以下问题的反馈:

此网页
Cloud Billing 文档
需要帮助?请访问我们的支持页面