Service Usage API でカスタム割り当てを設定して、ユーザー単位の1日の BigQuery 処理データ量の上限値を指定してみた

2020.07.02

こんにちは、みかみです。

BigQuery を利用する場合、やはりクエリ実行にかかる料金は心配です。。

GCP では、プロジェクトやユーザー単位で1日当たりに Google API のクエリ実行により処理されるデータ量を、カスタム割り当てとして制限することが可能です。

カスタム割り当てでクエリ実行量の上限を設定しておけば、上限以上の処理データ量がかかるクエリは実行エラーとなり、意図せず高額な課金が発生するのをふせぐことができます。

やりたいこと

  • カスタム割り当てで、1日当たりのBigQuery クエリ処理データ量の上限を設定したい
  • クエリ処理データ量の上限を設定した後で、上限以上のクエリを実行した場合の挙動を確認したい

GCP 管理コンソールからカスタム割り当てを設定

ドキュメントには GCP 管理コンソール「IAM と管理」の「割り当て」から上限設定を変更できるとの記載がありました。

しかし、左端のチェックボックス(と思われるもの)をクリックしても、一向にチェックが付かず、「割り当てを編集」をクリックして設定を変更しようとしても、やはり「編集する割り当てを1つ以上選択してください」と表示され変更できず。。(無料トライアルアカウントなのでまだ上限緩和申請できないことが原因でしょうか?

他のインターフェースで設定変更できないか調べたところ、Service Usage API で割り当ての管理ができるようです。

Service Usage API の準備

以下のクイックスタートを参照し、Service Usage API のセットアップを行いました。

Service Usage API を有効化し

「Service Usage 管理者」のロールを付与したサービスアカウントを作成して、アカウントキーファイルを取得しました。

続いて oauth2l をインストールして、初期設定を行います。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ pip install google-oauth2l --upgrade
Collecting google-oauth2l
  Downloading https://files.pythonhosted.org/packages/4a/a2/270bf3e7755a394bd724dd21f65775b9f002bd2459f5767887935564ff35/google_oauth2l-1.0.2-py2.py3-none-any.whl
(省略)
Installing collected packages: monotonic, fasteners, pycparser, cffi, cryptography, pyopenssl, google-oauth2l
Successfully installed cffi-1.14.0 cryptography-2.9.2 fasteners-0.15 google-oauth2l-1.0.2 monotonic-1.5 pycparser-2.20 pyopenssl-19.1.0
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ oauth2l header --json ~/service-usage.json cloud-platform
Authorization: Bearer ya29.c.KoxxxWl8
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ alias gcurl='curl -H "$(oauth2l header --json ~/service-usage.json cloud-platform userinfo.email)" -H "Content-Type: application/json"'

※一部伏字に変更しています。

サービス一覧を取得してみます。セットアップが正常に完了していれば、利用可能なサービスのリストが表示されるはずです。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ PROJECT_NUMBER=`gcloud projects list --filter="cm-da-mikami-yuki-258308" --format="value(PROJECT_NUMBER)"`
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1/projects/${PROJECT_NUMBER}/services
{
  "services": [
(省略)
    {
      "name": "projects/797147019523/services/bigquery.googleapis.com",
      "config": {
        "name": "bigquery.googleapis.com",
        "title": "BigQuery API",
        "documentation": {
          "summary": "A data platform for customers to create, manage, share and query data."
        },
        "quota": {},
        "authentication": {},
        "usage": {
          "requirements": [
            "serviceusage.googleapis.com/tos/cloud"
          ]
        }
      },
      "state": "ENABLED",
      "parent": "projects/797147019523"
    },
    {
      "name": "projects/797147019523/services/bigqueryconnection.googleapis.com",
      "config": {
        "name": "bigqueryconnection.googleapis.com",
        "title": "BigQuery Connection API",
        "documentation": {
          "summary": "Allows users to manage BigQuery connections to external data sources."
        },
        "quota": {},
        "authentication": {},
        "usage": {
          "requirements": [
            "serviceusage.googleapis.com/tos/cloud"
          ]
        }
      },
      "state": "DISABLED",
      "parent": "projects/797147019523"
    },
    {
      "name": "projects/797147019523/services/bigquerydatatransfer.googleapis.com",
      "config": {
        "name": "bigquerydatatransfer.googleapis.com",
        "title": "BigQuery Data Transfer API",
        "documentation": {
          "summary": "Schedule queries or transfer external data from SaaS applications to Google BigQuery on a regular basis."
        },
        "quota": {},
        "authentication": {},
        "usage": {
          "requirements": [
            "serviceusage.googleapis.com/tos/cloud"
          ]
        }
      },
      "state": "ENABLED",
      "parent": "projects/797147019523"
    },
    {
      "name": "projects/797147019523/services/bigqueryreservation.googleapis.com",
      "config": {
        "name": "bigqueryreservation.googleapis.com",
        "title": "BigQuery Reservation API",
        "documentation": {
          "summary": "A service to modify your BigQuery flat-rate reservations."
        },
        "quota": {},
        "authentication": {},
        "usage": {
          "requirements": [
            "serviceusage.googleapis.com/tos/cloud"
          ]
        }
      },
      "state": "DISABLED",
      "parent": "projects/797147019523"
    },
    {
      "name": "projects/797147019523/services/bigquerystorage.googleapis.com",
      "config": {
        "name": "bigquerystorage.googleapis.com",
        "title": "BigQuery Storage API",
        "documentation": {},
        "quota": {},
        "authentication": {},
        "usage": {
          "requirements": [
            "serviceusage.googleapis.com/tos/cloud"
          ]
        }
      },
      "state": "ENABLED",
      "parent": "projects/797147019523"
    },
(省略)
  ],
  "nextPageToken": "EkkKR3R5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmFwaS5zZXJ2aWNlbWFuYWdlbWVudC52MS5MaXN0U2VydmljZXNSZXF1ZXN0GqQBQ25jS1ZaeWVrNXVlaWRHWWtKQ1lrNXFlajVhTTBaeVFrdjhBX3ZfLTk4ZVJhdl9fX3liUWpKcU5pWmFjbXBLZWtaNlltcEtha1l2UmpKcU5pWmFjbW96Rm5KNlRtNTZKMFppUWtKaVRtcDZQbG96Um5KQ1NfXzRRTWlGVVhXXzVLNkVCTzFBQVdnc0p5dUpIR1hRX3l1a1FBMkNEMVpqb0J3PT0="
}

Service Usage API が使えるようになりました。

Service Usage API でカスタム割り当てを設定

以下のドキュメントを参考に、カスタム割り当てを設定します。

Service Usage API で BigQuery API 関連の割り当てのリソース名を取得(gcurl https://serviceusage.googleapis.com/v1beta1/projects/${PROJECT_NUMBER}/services/bigquery.googleapis.com/consumerQuotaMetrics)して、「Query usage」の name フィールドを確認した上で、割り当て情報を取得しました。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ METRIC_RESOURCE_NAME="projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1beta1/${METRIC_RESOURCE_NAME}
{
  "name": "projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage",
  "displayName": "Query usage",
  "consumerQuotaLimits": [
    {
      "name": "projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject",
      "unit": "1/d/{project}",
      "metric": "bigquery.googleapis.com/quota/query/usage",
      "quotaBuckets": [
        {
          "effectiveLimit": "9223372036854775807",
          "defaultLimit": "9223372036854775807"
        }
      ]
    },
    {
      "name": "projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser",
      "unit": "1/d/{project}/{user}",
      "metric": "bigquery.googleapis.com/quota/query/usage",
      "quotaBuckets": [
        {
          "effectiveLimit": "9223372036854775807",
          "defaultLimit": "9223372036854775807"
        }
      ]
    }
  ],
  "metric": "bigquery.googleapis.com/quota/query/usage",
  "unit": "MiBy"
}

ユーザー単位の日次のクエリデータ量( projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser )の上限を変更してみます。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ LIMIT_RESOURCE_NAME="projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1beta1/${LIMIT_RESOURCE_NAME}/consumerOverrides?force=true -d '{"overrideValue": "1"}'
{
  "name": "operations/quf.db67baa7-ea16-4be8-a9f2-3f19dc9bf752"
}

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ OPERATION_NAME="operations/quf.db67baa7-ea16-4be8-a9f2-3f19dc9bf752"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1/${OPERATION_NAME}
{
  "name": "operations/quf.db67baa7-ea16-4be8-a9f2-3f19dc9bf752",
  "metadata": {
    "@type": "type.googleapis.com/google.protobuf.Empty"
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.api.serviceusage.v1beta1.QuotaOverride",
    "name": "projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser/consumerOverrides/Cg1RdW90YU92ZXJyaWRl",
    "overrideValue": "1"
  }
}

正常に実行できたようです。

なお、変更前の上限値から 10% 以上の変更を実行する場合、API のパラメータに force=true を指定しないとエラーになります。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ LIMIT_RESOURCE_NAME="projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1beta1/${LIMIT_RESOURCE_NAME}/consumerOverrides -d '{"overrideValue": "1"}'
{
  "error": {
    "code": 400,
    "message": "The quota override on metric bigquery.googleapis.com/quota/query/usage with limit id /d/project/user decreases effective quota for more than 10.0 percent.\nPrevious effective limit is -1, and new effective limit is 1.",
    "status": "FAILED_PRECONDITION"
  }
}

GCP 管理コンソールから上限設定が変更されたか確認してみます。

設定変更できているようです。

では、制限サイズを超えるデータ量を処理するクエリの場合、本当にクエリ実行エラーになるのか確認してみます。

--dry_run で処理データ量が上限に設定した 1MiB を超えていることを確認してから実行してみました。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ bq query --dry_run --nouse_legacy_sql 'SELECT * FROM `cm-da-mikami-yuki-258308`.billing.gcp_billing_export_v1_01A74C_7C2053_85DC25'
/home/ec2-user/test_update/google-cloud-sdk/platform/bq/bq.py:45: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
Query successfully validated. Assuming the tables are not modified, running this query will process 4938313 bytes of data.
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ bq query --nouse_legacy_sql 'SELECT * FROM `cm-da-mikami-yuki-258308`.billing.gcp_billing_export_v1_01A74C_7C2053_85DC25'
/home/ec2-user/test_update/google-cloud-sdk/platform/bq/bq.py:45: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
Waiting on bqjob_r3a3f2c294af18be5_000001730a76635d_1 ... (0s) Current status: DONE
BigQuery error in query operation: Error processing job 'cm-da-mikami-yuki-258308:bqjob_r3a3f2c294af18be5_000001730a76635d_1': Custom quota exceeded: Your
usage exceeded the custom quota for QueryUsagePerUserPerDay, which is set by your administrator. For more information, see
https://cloud.google.com/bigquery/cost-controls

usage exceeded the custom quota for QueryUsagePerUserPerDay とのことで、カスタム割り当て通り、上限を超えたクエリの実行がエラーになることが確認できました。

カスタム割り当てを削除

上限値を変更したい場合、そのまま上限設定 API を再度実行するとエラーになります。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1beta1/${LIMIT_RESOURCE_NAME}/consumerOverrides?force=true -d '{"overrideValue": "953674"}'
{
  "error": {
    "code": 409,
    "message": "Cannot create consumer override. The override specified in CreateConsumerOverrideRequest already exists. Please call UpdateConsumerOverride instead with resource name 'projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser/consumerOverrides/Cg1RdW90YU92ZXJyaWRl'.",
    "status": "ALREADY_EXISTS"
  }
}

以下で設定済みのカスタム割り当てを削除してから、再度上限値を設定し直す必要があります。

(test_bq) [ec2-user@ip-10-0-43-239 ~]$ OVERRIDE_RESOURCE_NAME="projects/797147019523/services/bigquery.googleapis.com/consumerQuotaMetrics/bigquery.googleapis.com%2Fquota%2Fquery%2Fusage/limits/%2Fd%2Fproject%2Fuser/consumerOverrides/Cg1RdW90YU92ZXJyaWRl"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1beta1/${OVERRIDE_RESOURCE_NAME} -X DELETE
{
  "name": "operations/quf.f3ba1206-26c4-4a0c-a885-174366fc2f53"
}
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ OPERATION_NAME="operations/quf.f3ba1206-26c4-4a0c-a885-174366fc2f53"
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcurl https://serviceusage.googleapis.com/v1/${OPERATION_NAME}
{
  "name": "operations/quf.f3ba1206-26c4-4a0c-a885-174366fc2f53",
  "metadata": {
    "@type": "type.googleapis.com/google.protobuf.Empty"
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.protobuf.Empty"
  }
}

まとめ(所感)

カスタム割り当てでクエリ処理データ量の上限を設定しておけば、大量データが格納されているテーブルと知らずにクエリを実行して高額な課金が発生するような事故を防止することができ、安心して BigQuery を利用することができます。

特に1アカウントで複数のプロジェクトがある場合、プロジェクトごと、ユーザー(サービスアカウント)ごとに状況に合わせて上限設定できるカスタムコスト管理は非常に便利だと思いました。

参考