CloudBuild から BQ の Drive 外部テーブルにクエリする

2024-06-28

データプラットフォームチームの kobori です。 6 月は流行遅れのコロナに罹っておりました。

罹患中は安静のために netflix で Harry Potter を見ていたのですが、あの分厚い本をランドセルに入れて通学していたことを思い出し、懐かしい気持ちになりました。

さて、弊社では dbt-core を使って BigQuery 上に DWH を構築しています。 近々移行予定ではあるものの、今時点では Cloud Build 上で Job の実行をしております。

データソースとして Google Drive 外部テーブルも使用しているのですが、Cloud Build 上からこの外部テーブルにクエリを実行した際に下記のエラーが発生しました。

Permission denied while getting Drive credential

予想外に解決に手こずったため、対応方法を記事にまとめてみました。

起きた事象

繰り返しになりますが、弊社の環境では Cloud Build 上で dbt の build Job を実行しております。 Google Drive 外部テーブルをデータソースに持つ View に対して dbt test を実行した際に、下記のエラーが発生しました。

Permission denied while getting Drive credential

Google Drive 外部テーブルを用いている方はこのエラーを目にしたことがあると思います。 [dbt の document][dbt-access-gdrive-credential] にも対応方法の記載があり、通常は dbt Job の実行で使用している SA を Spread Sheet の共有ユーザに追加することで解消できます。 しかし今回はこれでは解消にいたりませんでした。

外部テーブルのデータへのクエリがエラーになるようで、外部テーブルをソースとする View  の作成はできるのですが、test や CREATE TABLEdbt_utils.get_column_values などが実行されるときにエラーが発生します。

同一の SA を用いて、GitHub Actions 上でも同じ Job を実行してみたのですが、こちらは問題なく実行することができます。

dbt-bigquery のソースコードを見ると、下記の scope がデフォルトで指定されています。

  • https://www.googleapis.com/auth/bigquery
  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/drive

しかし、Cloud Build 上で scope を確認すると https://www.googleapis.com/auth/drive は scope に含まれていませんでした。

debug に利用した yaml 定義
- name: badouralix/curl-jq
  entrypoint: sh
  args:
    - '-c'
    - |
      ACCESS_TOKEN=$(curl -s -H 'Metadata-Flavor: Google' http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | jq -r .access_token)
      curl -s -H "Authorization: Bearer $$ACCESS_TOKEN" https://oauth2.googleapis.com/tokeninfo
出力結果
{
  "azp": "110337692496226799078",
  "aud": "110337692496226799078",
  "scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-platform",
  "exp": "1719457142",
  "expires_in": "1849",
  "email": "dbt@dev.iam.gserviceaccount.com",
  "email_verified": "true",
  "access_type": "online"
}

解決方法

調査の末、dbt の profiles.yml にて impersonate_service_account を指定することで解決することができました。 impersonate_service_account に dbt の build Job を実行する SA を指定し、Cloud Build に attach する SA には roles/iam.serviceAccountUser を bind します。

なぜこれで解決できたのでしょうか?

  • ADC の仕組み
  • Compute Engine のアクセススコープの仕組み

が関係していそうです。

ADC の仕組み

Google Cloud の ACD のデフォルト認証の仕組み を見ると、ADC は次の 3 つの方法で認証情報を検索するそうです。

  1. GOOGLE_APPLICATION_CREDENTIALS 環境変数
  2. gcloud CLI で提供される認証情報
  3. Google Cloud リソースに接続された SA

GOOGLE_APPLICATION_CREDENTIALS 環境変数には認証情報を持つファイルのパスを指定するのですが、Workload Identity を利用する場合か SA キーを指定する場合に利用されます。 今回の環境では、Workload Identity Federation を利用している GitHub Actions での実行では、この方法で認証が行われていそうです。

ローカルで ADC を使った認証をする場合は gcloud CLI で提供される認証情報が利用されます。 gcloud auth application-default login コマンドを実行する際に --scopes を指定することで、アクセススコープの指定ができます。 dbt でもローカルで実行する際には、下記コマンドを実行することで scope の不足によるエラーは解消することができます (参考)。

gcloud auth login --enable-gdrive-access --update-adc

Compute Engine や Cloud Build などのリソースに接続された SA により認証を行う場合、メタデータサーバ という仕組みが利用されるようです。 GOOGLE_APPLICATION_CREDENTIALS 環境変数が設定されていない場合にメタデータサーバから認証情報の取得が行われます。 このことから、Cloud Build 上で dbt を実行する場合も、認証に関する情報がメタデータサーバから取得されていると考えられます。

各実行環境における ADC の認証情報の取得方法をまとめます。

実行環境認証情報の取得
GHAGOOGLE_APPLICATION_CREDENTIALS に指定されたファイルから取得される認証情報
Localgcloud CLI により提供される認証情報
Cloud Buildメタデータサーバから取得される認証情報

ではなぜ、メタデータサーバから認証情報を取得する場合は Google Drive へのアクセスに失敗するのでしょうか? Compute Engine のアクセススコープの仕組みがヒントになりそうです。

Compute Engine のアクセススコープの仕組み

SA の認可の仕組み を見ると、Compute Engine では VM の SA とアクセススコープにより認可の制限が行われるそうです。 つまり、SA に十分な権限を与えている場合でも、VM のアクセススコープが制限されている状態では利用できる API が制限されてしまいます。 今回の Cloud Build 上で実行した dbt のジョブも、このアクセススコープの影響を受けている可能性が考えられます。

また、Compute Engine のアクセススコープは VM を再起動するまで更新されないそうです (参考。 このことから、メタデータサーバから取得される認証情報が VM を再起動するまで更新されないことが予想されます。 Cloud Build で取得する認証も、動作している VM の再起動を行わないと変更することができず、ADC を利用する場合にはアクセススコープの変更ができない可能性が考えられます。

impersonate_sercie_account を利用する場合、ADC は impersonate 時に使用され、Drive API の利用時は別の credential を利用するため Compute Engine のアクセススコープの制約を受けないのだと思います。

最後に

ADC やメタデータサーバ、SA の Impersonate について知識があれば、今回の事象に悩まされることはなかったかもしれません。 今回は、Cloud Build 上から Drive API 利用の検証ログをまとめられている方がおり非常に助けられました。 また、メタデータサーバや ADC 周りの理解では、CP の Compute Metadata Credentials に関する zenn 記事が役立ちました。

弊社ではエンジニア採用を行なっております。 是非 Entrance Book を覗いてみてください!

参照