Cloud Functions、Cloud Scheduler、Cloud Monitoring を使用したコスト最適化の自動化


このドキュメントでは、Cloud Functions を使用して、無駄なクラウド リソースを特定してクリーンアップし、Cloud Scheduler で実行するように関数をスケジュールし、Cloud Monitoring アラート ポリシーを使用して、観測された使用状況に基づいてそれらを実行する方法を示します。このチュートリアルは、無駄なクラウド支出を特定して削減するための、体系的で自動化されたアプローチを求めるデベロッパー、SRE、クラウド アーキテクト、およびクラウド インフラストラクチャ管理者を対象としています。

このチュートリアルは、読者が次の内容を理解していることを前提としています。

目標

  • 未使用の IP アドレスを削除する: Google Cloud では、静的 IP アドレスがロードバランサや仮想マシン(VM)インスタンスに接続されている場合、その静的 IP アドレスは無料のリソースになります。静的 IP アドレスが予約されている場合、それが使用されていなくても、1 時間ごとに課金されます。静的 IP アドレスと大規模な動的プロビジョニングが不可欠なアプリでは、このコストが時間の経過とともに極めて大きくなる可能性があります。
  • 孤立ディスクまたは未使用の永続ディスクを削除する: 永続ディスクが、VM にアタッチされずに作成された場合、またはマシンに複数のディスクがあり、1 つ以上のディスクが切断された場合、その永続ディスクは未使用になるか孤立します。
  • 低価格なストレージ クラスへ移行する: Google Cloud では、複数のクラスのオブジェクト ストレージを提供しています。ニーズに合ったクラスを使用してください。

アーキテクチャ

次の図は、デプロイの最初の部分を示しています。ここでは、未使用の IP アドレスを特定してクリーンアップするために Cloud Functions の関数のスケジュールを設定します。

未使用の IP アドレスを特定してクリーンアップする Cloud Functions の関数のアーキテクチャ。

最初の例では、次の内容について説明します。

  • 静的外部 IP アドレスと使用されていない個別の静的外部 IP アドレスを使用した Compute Engine VM を作成する。
  • 未使用のアドレスを特定するための Cloud Function をデプロイする。
  • HTTP トリガーを使用して実行する関数をスケジュールする Cloud Scheduler ジョブを作成する。

次の図では、Cloud Functions の関数をスケジュールし、アタッチされていない孤立した永続ディスクを特定してクリーンアップします。

未使用の永続ディスクを特定してクリーンアップする Cloud Functions の関数のアーキテクチャ。

2 番目の例では、次の内容について説明します。

  • 2 つの永続ディスクと接続されていない個別の永続ディスクを使用した Compute Engine VM を作成する。ディスクの 1 つは、VM から接続解除されることにより孤立します。
  • Cloud Function をデプロイし、接続されていない孤立した永続ディスクを特定する。
  • HTTP トリガーを使用して Cloud Function の実行をスケジュールする Cloud Scheduler ジョブを作成する。

次の図では、Monitoring アラート ポリシーから Cloud Functions の関数をトリガーし、ストレージ バケットをより低コストなストレージ クラスに移行しています。

ストレージ バケットを移行する Cloud Functions の関数のアーキテクチャ。

3 番目の例では、次の内容について説明します。

  • 2 つのストレージ バケットを作成し、ファイルをサービング バケットに追加して、トラフィックを生成する。
  • バケット使用率を可視化するモニタリング ダッシュボードを作成する。
  • Cloud Functions の関数をデプロイし、アイドル状態のバケットをより低価格なストレージ クラスに移行する。
  • Monitoring アラート ポリシーから受信した通知をシミュレートすることを目的としたペイロードを使用して、関数をトリガーします。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Compute Engine, Cloud Functions, and Cloud Storage API を有効にします。

    API を有効にする

  5. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  6. Google Cloud プロジェクトで課金が有効になっていることを確認します

  7. Compute Engine, Cloud Functions, and Cloud Storage API を有効にします。

    API を有効にする

  8. Google Cloud コンソールで、「Cloud Shell をアクティブにする」をクリックします。

    Cloud Shell をアクティブにする

    Google Cloud コンソールの下部で Cloud Shell セッションが開始し、コマンドライン プロンプトが表示されます。Cloud Shell はシェル環境です。Google Cloud CLI がすでにインストールされており、現在のプロジェクトの値もすでに設定されています。セッションが初期化されるまで数秒かかることがあります。

  9. このドキュメントのすべてのコマンドは、Cloud Shell から実行します。

環境設定

このセクションでは、このアーキテクチャに必要なインフラストラクチャと ID を構成します。

  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 アドレスのクリーンアップ

このセクションでは、次の手順を行います。

  • 2 つの静的 IP アドレスを作成する。
  • 静的 IP アドレスを使用する VM を作成する。
  • 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. 2 つの静的 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. 2 つのアドレスが作成されたことを確認します。

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

VM の作成

  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 アドレスの 1 つが現在使用中であることを確認します。

    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. トリガー URL を環境変数として設定します。

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

Cloud Functions の関数のスケジュール設定とテスト

  1. Cloud Shell で、毎日午前 2 時に Cloud Function を実行する Cloud Scheduler タスクを作成します。

    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
    

未使用および孤立した永続ディスクのクリーンアップ

このセクションでは、次の手順を行います。

  • 2 つの永続ディスクを作成する。
  • ディスクの 1 つを使用する VM を作成する。
  • ディスクを VM から切断する。
  • 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. 2 つのディスクを作成します。

    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. 2 つのディスクが作成されたことを確認します。

    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
    

VM の作成とディスクの検査

  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. VM に接続されたディスクを検査します。

    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 は、ディスクが接続されている VM を示します。
    • lastAttachTimestamp は、ディスクが VM に最後に接続された日時を示します。
  3. VM に接続されていないディスクを検査します。

    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 が表示されていないのは、現在 VM によって使用されていないためです。
    • ディスクに lastAttachedTimestamp が表示されていないのは、使用されたことがないためです。
  4. 孤立した永続ディスクを VM から接続解除します。

    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 が表示されていない場合は、現在使用されていないことを示しています。
    • 今度は、ディスクが VM から最後に切り離された日時(つまり、ディスクが最後に使用された日時)を示す 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 の関数のトリガー URL を環境変数として設定します。

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

Cloud Functions の関数のスケジュール設定とテスト

  1. Cloud Shell で、毎日午前 2 時に Cloud Function を実行する Cloud Scheduler タスクを作成します。

    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 Storage に移動することもできます。

このセクションでは、次の手順を行います。

  • 2 つの Cloud Storage バケットを作成する。
  • バケットの 1 つにオブジェクトを追加する。
  • バケット オブジェクト アクセスを監視するように Monitoring を構成する。
  • オブジェクトを Regional Storage バケットから Nearline Storage バケットに移行する 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 という名前の 2 番目のバケットを作成します。

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

Cloud Monitoring ワークスペースを設定する

このセクションでは、バケットの使用状況を監視して、バケット オブジェクトが使用されていないときを把握できるよう Cloud Monitoring を構成します。提供バケットが使用されていない場合、Cloud Functions の関数はバケットを Regional Storage クラスから Nearline Storage クラスに移行します。

  1. Google Cloud コンソールで [Monitoring] に移動します。

    Cloud Monitoring に移動

  2. [New Workspace] を選択し、[Add] をクリックします。

    初期構成が完了するまで待ちます。

Cloud 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. ダッシュボードにアクセスできることを確認するには、ダッシュボード上にポインタを置き、バケットの使用状況が表示されることを確認します。

    バケット内のオブジェクト アクセスを監視できるよう、Monitoring を構成しました。Cloud Storage バケットへのトラフィックがないため、グラフにはデータが表示されません。

提供バケットの負荷の生成

Monitoring が構成されたので、Apache Bench を使用してトラフィックを提供バケットに送信します。

  1. Cloud Shell で、提供バケット内のオブジェクトにリクエストを送信します。

    ab -n 10000 \
        http://storage.googleapis.com/$PROJECT_ID-serving-bucket/testfile.txt
    
  2. Google Cloud コンソールで [Monitoring] に移動します。

    Cloud Monitoring に移動

  3. バケットの使用状況ダッシュボードを選択するには、ダッシュボード上にポインタを置いて [バケットの使用状況] を選択します。提供バケットにのみトラフィックがあることを確認します。アイドル状態のバケットにはトラフィックがないため、request_count metric 時系列は提供バケットに対してのみ表示されます。

Cloud Functions の関数の確認とデプロイ

  1. Cloud Shell で、Cloud Functions の関数を使用してストレージ バケットを Nearline Storage クラスに移行するコードを出力します。

    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 Storage に変更します。

  2. Cloud Functions の関数をデプロイします。

    gcloud functions deploy migrate_storage --trigger-http --runtime=python37
    
  3. 次のセクションで使用する環境変数としてトリガー URL を設定します。

    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 Storage に移行されたことを確認します。

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

    出力は次のとおりです。

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

本番環境に関する考慮事項

独自の Google Cloud 環境でコストの最適化を自動化する場合は、次の点を考慮してください。

  • 一般的な考慮事項: Google Cloud リソースを変更または削除する権限を持つ Cloud Functions のセキュリティを強化する必要があります。
  • 無駄の特定: このチュートリアルでは、無駄な支出に関するいくつかの例について説明しています。一般的に、3 つのカテゴリのいずれかに分類される例がこの他にも多くあります。
    • オーバー プロビジョニングされたリソース: 必要以上の CPU パワーとメモリを搭載した VM など、特定のワークロードに必要以上に大きくプロビジョニングされたリソース。
    • アイドル状態のリソース: 完全に未使用のリソース。
    • パートタイムでアイドル状態になるリソース: 営業時間中にのみ使用されるリソース。
  • クリーンアップの自動化: このチュートリアルでは、ディスクのスナップショットを作成して削除するために、複数の非同期操作を含む複数のステップによるプロセスが必要でした。未使用の IP アドレスなどの他の Google Cloud リソースは、同期オペレーションを使用できます。
  • 大規模なデプロイ: このチュートリアルでは、Google Cloud プロジェクト ID は Cloud Functions の関数コードで定義されます。このようなソリューションを大規模にデプロイするには、Cloud Billing または Cloud Resource Manager API を使用して、請求先アカウントまたは組織を持つプロジェクトのリストを取得することを検討してください。次に、これらの Google Cloud プロジェクト ID を変数として関数に渡します。このような構成では、リソースをクリーンアップまたは削除できるプロジェクトに Cloud Functions の関数のサービス アカウントを追加する必要があります。Cloud Deployment ManagerTerraform などの自動デプロイ フレームワークを使用することをおすすめします。
  • アラートの自動化: このドキュメントでは、Monitoring アラートからのモック ペイロードを使用して、ストレージ クラスの移行をトリガーする方法について説明します。Monitoring アラート ポリシーは、最大 23 時間 59 分間にわたって評価できます。本番環境では、この制限はストレージ クラスを移行する前に、バケットがアイドル状態であると見なすほど長くない場合があります。Cloud Storage バケットでデータアクセスの監査ログを有効にし、これらの監査ログを使用するパイプラインを作成して、過去 30 日間にサービス提供にバケットが使用されたかどうかを評価することを検討してください。詳細については、監査ログについてでご確認ください。また、集約シンクを作成してログを Pub/Sub と Dataflow パイプラインに送信することをおすすめします。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

プロジェクトの削除

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ