使用 Cloud Functions、Cloud Scheduler 和 Cloud Monitoring 自动进行费用优化

本教程演示如何使用 Cloud Functions 函数识别和清理浪费的云资源、使用 Cloud Scheduler 安排函数的运行时间,以及使用 Monitoring 提醒政策来根据观察到的使用情况执行这些函数。本教程适用于正在寻求一种系统性的自动方法来标识和减少云支出浪费的开发者、SRE、云架构师和云基础架构管理员。

本教程假定您熟悉以下内容:

目标

  • 删除未使用的 IP 地址:在 Google Cloud 上,附加到负载平衡器或虚拟机 (VM) 实例的静态 IP 地址属于免费资源。 如果某静态 IP 地址被预留,但未被使用,则该地址将按小时收费。在严重依赖静态 IP 地址和大规模动态预配的应用中,这种浪费可能会随时间推移变得非常显著。
  • 删除孤立或未使用的永久性磁盘:如果永久性磁盘在创建时未挂接到虚拟机,或者如果一个机器具有多个磁盘,并且一个或多个磁盘已分离,则这些永久性磁盘就是未使用或孤立的磁盘。
  • 迁移到更加经济实惠的存储类别:Google Cloud 提供了多种对象存储类别。 请使用与您的需求最相符的类别。

架构

下图描述了本教程第一部分中使用的架构,在该架构中,通过安排一个 Cloud Functions 函数来识别和清理未使用的 IP 地址。

Cloud Functions 函数的架构,该函数用于识别和清理未使用的 IP 地址。

第一个示例演示了以下操作:

  • 使用静态外部 IP 地址和单独的未使用的静态外部 IP 地址创建 Compute Engine 虚拟机。
  • 部署 Cloud Functions 函数,以标识未使用的地址。
  • 创建一个 Cloud Scheduler 作业,以使用 HTTP 触发器计划函数运行。

下图安排一个 Cloud Functions 函数来识别和清理未挂接和孤立的永久性磁盘。

Cloud Functions 函数的架构,该函数用于识别和清理未使用的永久性磁盘。

第二个示例演示了以下操作:

  • 创建一个包含两个永久性磁盘和一个单独的未挂接的永久性磁盘的 Compute Engine 虚拟机。其中一个磁盘因与虚拟机分离而成为孤立的磁盘。
  • 部署 Cloud Functions 函数,以标识未挂接的和孤立的永久性磁盘。
  • 创建一个 Cloud Scheduler 作业,以使用 HTTP 触发器计划 Cloud Functions 函数的执行。

下图触发一个 Cloud Functions 函数来将存储分区从 Monitoring 提醒政策迁移到更加经济实惠的存储类别。

Cloud Functions 函数的架构,该函数用于迁移存储分区。

第三个示例演示了以下操作:

  • 创建两个存储分区,将文件添加到服务存储分区,并针对其生成流量。
  • 创建一个 Monitoring 信息中心,以直观呈现存储分区利用率。
  • 部署 Cloud Functions 函数,以将空闲存储分区迁移到更加经济实惠的存储类别。
  • 使用载荷来触发该函数,该载荷用于模拟从 Monitoring 提醒政策接收的通知。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google Cloud 帐号。如果您是 Google Cloud 新手,请创建一个帐号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。

    转到“项目选择器”

  3. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

  4. 启用 Compute Engine, Cloud Functions, and Cloud Storage API。

    启用 API

  5. 在 Cloud Console 中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Cloud Console 的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Cloud SDK 的 Shell 环境,其中包括 gcloud 命令行工具以及已为当前项目设置的值。该会话可能需要几秒钟时间来完成初始化。

  6. 本教程中的所有命令都从 Cloud Shell 中运行。

设置环境

在本部分中,您将配置完成本教程所需的基础架构和身份。

  1. 在 Cloud Shell 中,克隆代码库并切换到 gcf-automated-resource-cleanup 目录:

    git clone https://github.com/GoogleCloudPlatform/gcf-automated-resource-cleanup.git && cd gcf-automated-resource-cleanup/
    
  2. 设置环境变量,并将代码库文件夹设为在其中运行与本教程相关的所有命令的 $WORKDIR

    export PROJECT_ID=$(gcloud config list \
        --format 'value(core.project)' 2>/dev/null)
        WORKDIR=$(pwd)
    
  3. 安装开源负载生成工具 Apache Bench

    sudo apt-get install apache2-utils
    

清理未使用的 IP 地址

在本部分中,您将完成以下步骤:

  • 创建两个静态 IP 地址。
  • 创建一个使用静态 IP 地址的虚拟机。
  • 查看 Cloud Functions 函数代码。
  • 部署 Cloud Functions 函数。
  • 使用 Cloud Scheduler 作业测试 Cloud Functions 函数。

创建 IP 地址

  1. 在 Cloud Shell 中,切换到 unused-ip 目录:

    cd $WORKDIR/unused-ip
    
  2. 将 IP 地址的名称导出为变量:

    export USED_IP=used-ip-address
    export UNUSED_IP=unused-ip-address
    
  3. 创建两个静态 IP 地址:

    gcloud compute addresses create $USED_IP \
        --project=$PROJECT_ID --region=us-central1
    gcloud compute addresses create $UNUSED_IP \
        --project=$PROJECT_ID --region=us-central1
    

    本教程使用 us-central1 区域,但您可以选择其他区域并在本教程的其余部分中始终引用该区域。

  4. 确认已创建两个地址:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    在输出中,状态 RESERVED 表示 IP 地址未使用:

    NAME               ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    unused-ip-address  35.232.144.85  EXTERNAL  us-central1          RESERVED
    used-ip-address    104.197.56.87  EXTERNAL  us-central1          RESERVED
    
  5. 将已使用的 IP 地址设置为环境变量:

    export USED_IP_ADDRESS=$(gcloud compute addresses describe $USED_IP \
        --region=us-central1 --format=json | jq -r '.address')
    

创建一个虚拟机

  1. 在 Cloud Shell 中,创建一个实例:

    gcloud compute instances create static-ip-instance \
        --zone=us-central1-a \
        --machine-type=n1-standard-1 \
        --subnet=default \
        --address=$USED_IP_ADDRESS
    
  2. 确认现在正使用其中一个 IP 地址:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    输出类似于以下内容:

    NAME               ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    unused-ip-address  35.232.144.85  EXTERNAL  us-central1          RESERVED
    used-ip-address    104.197.56.87  EXTERNAL  us-central1          IN_USE
    

查看 Cloud Functions 函数代码

  • 在 Cloud Shell 中,输出代码的主要部分:

    cat $WORKDIR/unused-ip/function.js | grep "const compute" -A 31
    

    输出如下所示:

    const compute = new Compute();
    compute.getAddresses(function(err, addresses){ // gets all addresses across regions
         if(err){
             console.log("there was an error: " + err);
         }
         if (addresses == null) {
             console.log("no addresses found");
             return;
         }
         console.log("there are " + addresses.length + " addresses");
    
         // iterate through addresses
         for (let item of addresses){
    
              // get metadata for each address
              item.getMetadata(function(err, metadata, apiResponse) {
    
                  // if the address is not used AND if it's at least ageToDelete days old:
                  if ((metadata.status=='RESERVED') & (calculateAge(metadata.creationTimestamp) >= ageToDelete)){
                      // delete address
                      item.delete(function(err, operation, apiResponse2){
                          if (err) {
                              console.log("could not delete address: " + err);
                          }
                      })
                  }
              })
          }
           // return number of addresses evaluated
          res.send("there are " + addresses.length + " total addresses");
      });
    }
    

    在前面的代码示例中,请注意以下内容:

    • compute.getAddresses(function(err, addresses){ // gets all addresses across regions
      

      使用 getAddresses 方法检索项目中所有区域的 IP 地址。

    • // get metadata for each address
      item.getMetadata(function(err, metadata, apiResponse) {
         // if the address is not used:
             if (metadata.status=='RESERVED'){
      

      获取每个 IP 地址的元数据并检查其 STATUS 字段。

    • if ((metadata.status=='RESERVED') &
      (calculateAge(metadata.creationTimestamp) >= ageToDelete)){
      

      检查 IP 地址是否正在使用中,使用辅助函数计算其存在时间,并将其存在时间与常量(在本教程中设置为 0)进行比较。

    • // delete address
      item.delete(function(err, operation, apiResponse2){
      

      删除 IP 地址。

部署 Cloud Functions 函数

  1. 在 Cloud Shell 中,部署 Cloud Functions 函数:

    gcloud functions deploy unused_ip_function --trigger-http --runtime=nodejs8
    
  2. 将触发器网址设置为环境变量:

    export FUNCTION_URL=$(gcloud functions describe unused_ip_function \
        --format=json | jq -r '.httpsTrigger.url')
    

计划并测试 Cloud Functions 函数

  1. 在 Cloud Shell 中,创建一个 Cloud Scheduler 任务,以在每天凌晨 2 点运行 Cloud Functions 函数:

    gcloud scheduler jobs create http unused-ip-job \
        --schedule="* 2 * * *" \
        --uri=$FUNCTION_URL
    
  2. 通过手动触发作业来测试该作业:

    gcloud scheduler jobs run unused-ip-job
    
  3. 确认已删除未使用的 IP 地址:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    输出类似于以下内容:

    NAME             ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    used-ip-address  104.197.56.87  EXTERNAL  us-central1          IN_USE
    

清理未使用和孤立的永久性磁盘

在本部分中,您将完成以下步骤:

  • 创建两个永久性磁盘。
  • 创建一个使用其中一个磁盘的虚拟机。
  • 从虚拟机分离磁盘。
  • 查看 Cloud Functions 函数代码。
  • 部署 Cloud Functions 函数。
  • 使用 Cloud Scheduler 作业测试 Cloud Functions 函数。

创建永久性磁盘

  1. 在 Cloud Shell 中,切换到 unattached-pd 目录:

    cd $WORKDIR/unattached-pd
    
  2. 将磁盘的名称导出为环境变量:

    export ORPHANED_DISK=orphaned-disk
    export UNUSED_DISK=unused-disk
    
  3. 创建两个磁盘:

    gcloud beta compute disks create $ORPHANED_DISK \
       --project=$PROJECT_ID \
       --type=pd-standard \
       --size=500GB \
       --zone=us-central1-a
    gcloud beta compute disks create $UNUSED_DISK \
        --project=$PROJECT_ID \
        --type=pd-standard \
        --size=500GB \
        --zone=us-central1-a
    
  4. 确认已创建两个磁盘:

    gcloud compute disks list
    

    输出如下所示:

    NAME                LOCATION       LOCATION_SCOPE SIZE_GB TYPE         STATUS
    orphaned-disk       us-central1-a  zone           500     pd-standard  READY
    static-ip-instance  us-central1-a  zone           10      pd-standard  READY
    unused-disk         us-central1-a  zone           500     pd-standard  READY
    

创建一个虚拟机并检查磁盘

  1. 在 Cloud Shell 中,创建实例:

    gcloud compute instances create disk-instance \
        --zone=us-central1-a \
        --machine-type=n1-standard-1 \
        --disk=name=$ORPHANED_DISK,device-name=$ORPHANED_DISK,mode=rw,boot=no
    
  2. 检查挂接到虚拟机的磁盘:

    gcloud compute disks describe $ORPHANED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    输出类似于以下内容:

    {
      "creationTimestamp": "2019-06-12T12:21:25.546-07:00",
      "id": "7617542552306904666",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "lastAttachTimestamp": "2019-06-12T12:24:53.989-07:00",
      "name": "orphaned-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/orphaned-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "users": [
        "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/instances/disk-instance"
      ],
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    在前面的代码示例中,请注意以下内容:

    • users 标识挂接该磁盘的虚拟机。
    • lastAttachTimestamp 标识磁盘上次挂接到虚拟机的时间。
  3. 检查未挂接到虚拟机的磁盘:

    gcloud compute disks describe $UNUSED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    输出类似于以下内容:

    {
      "creationTimestamp": "2019-06-12T12:21:30.905-07:00",
      "id": "1313096191791918677",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "name": "unused-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/unused-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    在前面的代码示例中,以下内容至关重要:

    • 该磁盘未列出 users,因为虚拟机当前未使用该磁盘。
    • 该磁盘没有 lastAttachedTimestamp,因为它从未被使用过
  4. 从虚拟机分离孤立的永久性磁盘:

    gcloud compute instances detach-disk disk-instance \
        --device-name=$ORPHANED_DISK \
        --zone=us-central1-a
    
  5. 检查孤立的磁盘:

    gcloud compute disks describe $ORPHANED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    输出类似于以下内容:

    {
      "creationTimestamp": "2019-06-12T12:21:25.546-07:00",
      "id": "7617542552306904666",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "lastAttachTimestamp": "2019-06-12T12:24:53.989-07:00",
      "lastDetachTimestamp": "2019-06-12T12:34:56.040-07:00",
      "name": "orphaned-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/orphaned-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    在前面的代码示例中,以下内容至关重要:

    • 该磁盘未列出 users,这表明该磁盘当前未被使用。
    • 现在有一个 lastDetachTimestamp 条目,指示该磁盘上次与虚拟机分离的时间以及该磁盘的最后一次使用时间。
    • lastAttachTimestamp 字段仍然存在。

查看 Cloud Functions 函数代码

  1. 在 Cloud Shell 中,输出检索项目中所有永久性磁盘的代码部分:

    cat $WORKDIR/unattached-pd/main.py | grep "(request)" -A 12
    

    输出如下所示:

    def delete_unattached_pds(request):
        # get list of disks and iterate through it:
        disksRequest = compute.disks().aggregatedList(project=project)
        while disksRequest is not None:
            diskResponse = disksRequest.execute()
            for name, disks_scoped_list in diskResponse['items'].items():
                if disks_scoped_list.get('warning') is None:
                    # got disks
                    for disk in disks_scoped_list['disks']: # iterate through disks
                        diskName = disk['name']
                        diskZone = str((disk['zone'])).rsplit('/',1)[1]
                        print (diskName)
                        print (diskZone)
    

    函数使用 aggregatedList 方法获取 Google Cloud 项目(函数在该项目中运行,并迭代所有磁盘)中的所有永久性磁盘。

  2. 输出用于检查 lastAttachTimestamp 字段并在该字段不存在的情况下删除磁盘的代码部分:

    cat $WORKDIR/unattached-pd/main.py | grep "handle never" -A 11
    

    输出如下所示:

    # handle never attached disk - delete it
    # lastAttachedTimestamp is not present
    if disk.get("lastAttachTimestamp") is None:
           print ("disk " + diskName + " was never attached - deleting")
           deleteRequest = compute.disks().delete(project=project,
                  zone=diskZone,
                  disk=diskName)
           deleteResponse = deleteRequest.execute()
           waitForZoneOperation(deleteResponse, project, diskZone)
           print ("disk " + diskName + " was deleted")
           Continue
    

    如果 lastAttachTimestamp 不存在(表示该磁盘从未被使用过),此部分将删除磁盘。

  3. 输出用于计算磁盘存在时间(如果是孤立的磁盘)、创建磁盘快照并删除磁盘的代码部分:

    cat $WORKDIR/unattached-pd/main.py | grep "handle detached" -A 32
    

    输出如下所示:

    # handle detached disk - snapshot and delete
    # lastAttachTimestamp is present AND users is not present AND it meets the age criterium
    if disk.get("users") is None \
        and disk.get("lastDetachTimestamp") is not None \
        and diskAge(disk['lastDetachTimestamp'])>=deleteAge:
    
        print ("disk " + diskName + " has no users and has been detached")
        print ("disk meets age criteria for deletion")
    
        # take a snapshot
        snapShotName = diskName + str(int(time.time()))
        print ("taking snapshot: " + snapShotName)
        snapshotBody = {
            "name": snapShotName
        }
        snapshotRequest = compute.disks().createSnapshot(project=project,
             zone=diskZone,
             disk=diskName,
             body=snapshotBody)
        snapshotResponse = snapshotRequest.execute()
        waitForZoneOperation(snapshotResponse, project, diskZone)
        print ("snapshot completed")
    
        # delete the disk
        print ("deleting disk " + diskName)
        deleteRequest = compute.disks().delete(project=project,
            zone=diskZone,
            disk=diskName)
        deleteResponse = deleteRequest.execute()
        waitForZoneOperation(deleteResponse, project, diskZone)
        print ("disk " + diskName + " was deleted")
        continue
    

    如果磁盘确实列出了 users 并且存在 lastDetachTimestamp,将使用此代码部分,这意味着磁盘当前未被使用,但在某些时候已被使用过。在此情况下,Cloud Functions 函数将创建磁盘的快照以保留数据,然后删除磁盘。

部署 Cloud Functions 函数

  1. 在 Cloud Shell 中,部署 Cloud Functions 函数:

    gcloud functions deploy delete_unattached_pds \
        --trigger-http --runtime=python37
    
  2. 将 Cloud Functions 函数的触发器网址设置为环境变量:

    export FUNCTION_URL=$(gcloud functions describe delete_unattached_pds \
        --format=json | jq -r '.httpsTrigger.url')
    

计划并测试 Cloud Functions 函数

  1. 在 Cloud Shell 中,创建一个 Cloud Scheduler 任务,以在每天凌晨 2 点运行 Cloud Functions 函数:

    gcloud scheduler jobs create http unattached-pd-job \
        --schedule="* 2 * * *" \
        --uri=$FUNCTION_URL
    
  2. 测试作业:

    gcloud scheduler jobs run unattached-pd-job
    
  3. 确认已创建孤立磁盘的快照:

    gcloud compute snapshots list
    

    输出类似于以下内容:

    NAME                     DISK_SIZE_GB  SRC_DISK                           STATUS
    orphaned-disk1560455894  500           us-central1-a/disks/orphaned-disk  READY
    
  4. 确认已删除未使用的磁盘和孤立的磁盘:

    gcloud compute disks list
    

    输出如下所示:

    NAME                LOCATION       LOCATION_SCOPE SIZE_GB  TYPE         STATUS
    disk-instance       us-central1-a  zone           10       pd-standard  READY
    static-ip-instance  us-central1-a  zone           10       pd-standard  READY
    

将存储分区迁移到费用更低的存储类别

Google Cloud 提供了存储对象生命周期规则,您可以使用这些规则根据一组属性(例如创建日期或实时状态)将对象自动移动到不同的存储类别。 但是,这些规则无法确定相关对象是否已被访问过。 有时,如果较新的对象已有一段时间未被访问过,您可能需要将其移动到 Nearline 存储空间。

在本部分中,您将完成以下步骤:

  • 创建两个 Cloud Storage 存储分区。
  • 将对象添加到其中一个存储分区。
  • 配置 Monitoring 以观察存储分区对象访问。
  • 查看将对象从 Regional 存储空间存储分区迁移到 Nearline 存储空间存储分区的 Cloud Functions 函数代码。
  • 部署 Cloud Functions 函数。
  • 使用 Monitoring 提醒测试 Cloud Functions 函数。

创建 Cloud Storage 存储分区并添加文件

  1. 在 Cloud Shell 中,切换到 migrate-storage 目录:

    cd $WORKDIR/migrate-storage
    
  2. 创建 serving-bucket Cloud Storage 存储分区,以稍后用于更改存储类别:

    export PROJECT_ID=$(gcloud config list \
        --format 'value(core.project)' 2>/dev/null)
    gsutil mb -c regional -l us-central1 gs://${PROJECT_ID}-serving-bucket
    
  3. 公开该存储分区:

    gsutil acl ch -u allUsers:R gs://${PROJECT_ID}-serving-bucket
    
  4. 将文本文件添加到存储分区:

    gsutil cp $WORKDIR/migrate-storage/testfile.txt  \
        gs://${PROJECT_ID}-serving-bucket
    
  5. 公开文件:

    gsutil acl ch -u allUsers:R gs://${PROJECT_ID}-serving-bucket/testfile.txt
    
  6. 确认您可以访问该文件:

    curl http://storage.googleapis.com/${PROJECT_ID}-serving-bucket/testfile.txt
    

    输出如下所示:

    this is a test
    
  7. 再创建一个名为 idle-bucket 的存储分区,该存储分区不提供任何数据:

    gsutil mb -c regional -l us-central1 gs://${PROJECT_ID}-idle-bucket
    

设置 Monitoring 工作区

在本部分中,您将配置 Monitoring 以观察存储分区的使用情况,了解何时不会使用存储分区对象。如果服务存储分区未被使用,则 Cloud Functions 函数会将该存储分区从 Regional 存储类别迁移到 Nearline 存储类别。

  1. 在 Cloud Console 中,转到 Monitoring

    转到“监控”

  2. 点击新建工作区 (New Workspace),然后点击添加

    等待初始配置完成。

创建 Monitoring 信息中心

  1. 在 Monitoring 中,转到信息中心,然后点击创建信息中心

  2. 点击添加图表

  3. 名称字段中,输入 Bucket Access

  4. 要查找 Cloud Storage 存储分区的请求内容指标,请在查找资源和指标 (Find resource and metric) 字段中输入 request,然后为 gcs_bucket 资源选择请求数

  5. 要按存储分区名称对指标进行分组,请在分组依据下拉列表中,点击 bucket_name

  6. 要按方法名称进行过滤,请在过滤条件字段中输入 ReadObject,然后点击应用

  7. 点击保存

  8. 在名称字段中,输入 Bucket Usage

  9. 要确认信息中心是否可访问,请将鼠标指针悬停在信息中心上,并验证是否显示存储分区使用情况 (Bucket Usage)。

    您已配置 Monitoring 以观察存储分区中的对象访问。该图表不显示任何数据,因为没有到 Cloud Storage 存储分区的流量。

为服务存储分区生成负载

配置 Monitoring 后,请使用 Apache Bench 将流量发送到服务存储分区。

  1. 在 Cloud Shell 中,将请求发送到服务存储分区中的对象:

    ab -n 10000 \
        http://storage.googleapis.com/$PROJECT_ID-serving-bucket/testfile.txt
    
  2. 在 Cloud Console 中,转到 Monitoring。

    转到“监控”

  3. 要选择存储分区使用情况 (Bucket Usage) 信息中心,请将鼠标指针悬停在信息中心上并选择存储分区使用情况 (Bucket Usage)。确认流量仅发送到服务存储分区。request_count metric 时间序列仅会针对服务存储分区显示,因为空闲存储分区没有任何流量。

查看并部署 Cloud Functions 函数

  1. 在 Cloud Shell 中,输出使用 Cloud Functions 函数将存储分区迁移到 Nearline 存储空间类别的代码:

    cat $WORKDIR/migrate-storage/main.py | grep "migrate_storage(" -A 15
    

    输出如下所示:

    def migrate_storage(request):
        # process incoming request to get the bucket to be migrated:
        request_json = request.get_json(force=True)
        # bucket names are globally unique
        bucket_name = request_json['incident']['resource_name']
    
        # create storage client
        storage_client = storage.Client()
    
        # get bucket
        bucket = storage_client.get_bucket(bucket_name)
    
        # update storage class
        bucket.storage_class = "NEARLINE"
        bucket.patch()
    

    Cloud Functions 函数使用在请求中传递的存储分区名称将其存储类别更改为 Nearline 存储空间。

  2. 部署 Cloud Functions 函数:

    gcloud functions deploy migrate_storage --trigger-http --runtime=python37
    
  3. 将触发器网址设置为在下一部分中使用的环境变量:

    export FUNCTION_URL=$(gcloud functions describe migrate_storage \
        --format=json | jq -r '.httpsTrigger.url')
    

测试并验证提醒自动化

  1. 设置空闲存储分区的名称:

    export IDLE_BUCKET_NAME=$PROJECT_ID-idle-bucket
    
  2. 使用 incident.json 文件将测试通知发送到您部署的 Cloud Functions 函数:

    envsubst < $WORKDIR/migrate-storage/incident.json | curl -X POST \
        -H "Content-Type: application/json" $FUNCTION_URL -d @-
    

    输出如下所示:

    OK
    

    输出不会以换行符终止,因此命令提示符会紧随其后。

  3. 确认空闲存储分区已迁移到 Nearline 存储空间:

    gsutil defstorageclass get gs://$PROJECT_ID-idle-bucket
    

    输出如下所示:

    gs://automating-cost-optimization-idle-bucket: NEARLINE
    

针对生产环境的注意事项

在您自己的 Google Cloud 环境中自动进行费用优化时,请考虑以下事项:

  • 一般注意事项:您应该增强可以修改或删除 Google Cloud 资源的 Cloud Functions 函数的安全性
  • 识别浪费:本教程提供了一些支出浪费示例。 此外,还有许多其他示例,这些示例通常属于以下三种类别中的其中一种:
    • 超额预配的资源:预配量超过给定工作负载所需量的资源,例如,其 CPU 性能和内存量超出需要的虚拟机。
    • 空闲资源:完全未使用的资源。
    • 部分时间空闲资源:仅在工作时间使用的资源。
  • 自动执行清理:在本教程中,需要执行一个包含多个异步操作的多步过程,以截取磁盘快照并删除磁盘。 其他 Google Cloud 资源(例如未使用的 IP 地址)可以使用同步操作。
  • 大规模部署:在本教程中,Google Cloud 项目 ID 在 Cloud Functions 函数代码中定义。要大规模部署此类解决方案,请考虑使用 Cloud Billing 或 Resource Manager API 来获取含结算帐号或组织的项目列表。然后,将这些 Google Cloud 项目 ID 作为变量传递给函数。在此类配置中,您需要将 Cloud Functions 函数的服务帐号添加到该函数可以在其中清理或删除资源的项目中。我们建议使用自动化部署框架,例如 Cloud Deployment ManagerTerraform
  • 提醒自动化:本教程演示了如何使用 Monitoring 提醒中的模拟负载来触发存储类别迁移。Monitoring 提醒政策的评估时间最长为 23 小时 59 分钟。在生产环境中,此时间限制可能不足以在迁移存储分区的存储类别之前将存储分区视为空闲。请考虑在 Cloud Storage 存储分区上启用数据访问审核日志,并创建一个使用这些审核日志的流水线,以评估存储分区在过去 30 天内是否被用于提供服务。如需了解详情,请查看了解审核日志,并考虑创建汇总接收器以将日志发送到 Pub/Sub 和 Dataflow 流水线进行处理。

清理

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤