BigQuery のテーブルメタデータを Cloud Run Jobs で自動更新する最小構成

この記事は個人ブログと同じ内容です

www.ritolab.com


「BigQuery の各テーブルおよびカラムの description が空のままになっているものを、できるだけシンプルに補完できる方法はないか?」

「メタ情報はメタ情報だけで管理したい」

「テーブルを洗い替えてメタ情報が消えても、フックや定期実行など任意のタイミングで補完したい」

これらの実現を模索し色々と試してみた中で、Google Cloud SDK を使ったスクリプトを Cloud Run Job で実行する構成がもっともシンプルでミニマムに実現できたため、その内容をご紹介します。

全体構成

全体の構成は以下になっています。各テーブルごとにメタデータyaml ファイルで管理し、コンテナ環境の中で python スクリプトを実行していくものです。

.
├── Dockerfile
├── docker-compose.yml
├── main.py
├── metadata
│      ├── <dataset_name_1>
│      │        ├── table_name_1-1.yml
│      │        └── table_name_1-2.yml
│      └── <dataset_name_2>
│          ├── table_name_2-1.yml
│          .
│          .
└── requirements.txt
# requirements.txt
google-cloud-bigquery==3.34.0
PyYAML==6.0.2

テーブルメタデータは簡単に管理したい

規模によっては 1 つのデータセットに多くのテーブル、 1 つのテーブルに多くのカラムがある場合も多分に想定されるため、メタデータの管理はとにかくシンプルに簡単にしたいという考えが大前提でした。

そのため、metadata/ に、データセットごとにディレクトリを切り、それぞれテーブルごとに yaml ファイルを作成しています。

name: players
description: "プレイヤー一覧"
columns:
  - name: id
    description: "プレイヤーを一意に識別するID"
  - name: name
    description: "プレイヤー名"

なお、yaml ファイルの構成はどんな分割の仕方であっても次のスクリプト側で調整すればよく、例えばテーブル単位ではなくもっと大まかにもできます。

メイン処理

main.py では、metadata/ にある yaml ファイルを全て処理しつつ、テーブルとカラムの description を更新するものです。

メタデータを読み込み、Google Cloud SDK を使って更新するだけなので、処理内容は非常にシンプルです。

import os
import yaml
from google.cloud import bigquery

PROJECT_ID = "your-project-id"
METADATA_DIR = "metadata"

def update_table_metadata(dataset_name, table_config, client):
    table_id = f"{PROJECT_ID}.{dataset_name}.{table_config['name']}"
    print(f"  > テーブル '{table_id}' のメタデータを更新中...")

    try:
        table = client.get_table(table_id)
    except Exception as e:
        print(f"  > スキップ: テーブル '{table_id}' が見つかりません。{e}")
        return

    if 'description' in table_config:
        table.description = table_config['description']

    # カラムの説明を更新
    new_schema = []
    for schema_field in table.schema:
        desc = next((c['description'] for c in table_config.get('columns', []) if c['name'] == schema_field.name), None)
        new_schema.append(
            bigquery.SchemaField(
                schema_field.name,
                schema_field.field_type,
                mode=schema_field.mode,
                description=desc if desc else schema_field.description
            )
        )

    table.schema = new_schema
    client.update_table(table, ["schema", "description"])
    print(f"  > 更新完了: '{table_id}'")


client = bigquery.Client(project=PROJECT_ID)

for dataset_name in os.listdir(METADATA_DIR):
    dataset_path = os.path.join(METADATA_DIR, dataset_name)
    if not os.path.isdir(dataset_path):
        continue

    for file in os.listdir(dataset_path):
        if file.endswith((".yaml", ".yml")):
            table_path = os.path.join(dataset_path, file)
            with open(table_path, 'r', encoding='utf-8') as f:
                table_config = yaml.safe_load(f)
                update_table_metadata(dataset_name, table_config, client)

print("すべてのメタデータ更新が完了しました。")

コンテナ環境の用意

Cloud Run Job で実行したいため、コンテナ環境を用意します。ここの環境もミニマムで作成しています。(ローカルでも使用)

Dockerfile

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["python", "main.py"]

ローカルから実行できるようにするなら、docker-compose.yml も準備します。

このコンテナから Google Cloud の API(例:BigQuery)にアクセスできるようにするため、認証情報(Application Default Credentials) を環境変数 GOOGLE_APPLICATION_CREDENTIALS に設定しています。

docker-compose.yml

services:
  bigquery-metadata:
    build: .
    volumes:
      - .:/app
      - ${HOME}/.config/gcloud:/root/.config/gcloud:ro
    environment:
      - GOOGLE_APPLICATION_CREDENTIALS=/root/.config/gcloud/application_default_credentials.json
    working_dir: /app

ローカルで実行

ローカルで実行します。

まずは使用するプロジェクトに切り替えます。

# 使用する Google Cloud プロジェクトを設定
gcloud config set project your-project-id

# Application Default Credentials を設定(ローカルの認証情報を取得)
gcloud auth application-default login

以下のコマンドで実行されます。

docker compose up --build

ログは以下が出力され、BigQuery テーブルのメタデータが更新されました。

bigquery-metadata-1  | プロジェクト 'your-project-id' のメタデータを更新中...
bigquery-metadata-1  |   > テーブル 'your-project-id.esports_sample.esports_play_sessions' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.esports_sample.esports_play_sessions'
bigquery-metadata-1  |   > テーブル 'your-project-id.esports_sample.players' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.esports_sample.players'
bigquery-metadata-1  |   > テーブル 'your-project-id.blog_sample.blog_posts' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.blog_sample.blog_posts'
bigquery-metadata-1  |   > テーブル 'your-project-id.blog_sample.comments' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.blog_sample.comments'
bigquery-metadata-1  |   > テーブル 'your-project-id.blog_sample.likes' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.blog_sample.likes'
bigquery-metadata-1  |   > テーブル 'your-project-id.blog_sample.users' のメタデータを更新中...
bigquery-metadata-1  |   > 更新完了: 'your-project-id.blog_sample.users'
bigquery-metadata-1  | すべてのメタデータ更新が完了しました。

Cloud Run Job で実行

クラウド上で実行させるため、以下を準備します。

サービスアカウント作成

Cloud Run Job でこのスクリプトを実行するサービスアカウントを作成しておきます。

サービスアカウント作成します。

gcloud iam service-accounts create bigquery-metadata-sa

作成したサービスアカウントに、「BigQuery ジョブユーザー」と「BigQuery データ編集者」の権限を付与します。

gcloud projects add-iam-policy-binding your-project-id \
    --member="serviceAccount:bigquery-metadata-sa@your-project-id.iam.gserviceaccount.com" \
    --role="roles/bigquery.dataEditor"

gcloud projects add-iam-policy-binding your-project-id \
    --member="serviceAccount:bigquery-metadata-sa@your-project-id.iam.gserviceaccount.com" \
    --role="roles/bigquery.jobUser"

ビルド&デプロイ

コンテナイメージをビルドします。

gcloud builds submit --tag gcr.io/your-project-id/bigquery-metadata .

Cloud Run Job にデプロイします。

# 初回デプロイ
gcloud run jobs create bigquery-metadata-job \
    --image=gcr.io/your-project-id/bigquery-metadata \
    --service-account=bigquery-metadata-sa@your-project-id.iam.gserviceaccount.com \
    --region=asia-northeast1

# 再デプロイするなら
gcloud run jobs update bigquery-metadata-job \
    --image=gcr.io/your-project-id/bigquery-metadata \
    --service-account=bigquery-metadata-sa@your-project-id.iam.gserviceaccount.com \
    --region=asia-northeast1

実行

下記コマンドで実行します。

gcloud run jobs execute bigquery-metadata-job --region=asia-northeast1

Cloud Run Job のコンソール画面でログを確認することで、正常に実行されたことが確認できます。BigQuery テーブルのメタデータも更新されています。

2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.esports_sample.esports_play_sessions' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.esports_sample.esports_play_sessions'
2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.esports_sample.players' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.esports_sample.players'
2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.blog_sample.blog_posts' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.blog_sample.blog_posts'
2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.blog_sample.comments' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.blog_sample.comments'
2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.blog_sample.likes' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.blog_sample.likes'
2025-07-21 10:03:48.647 JST > テーブル 'your-project-id.blog_sample.users' のメタデータを更新中...
2025-07-21 10:03:48.647 JST > 更新完了: 'your-project-id.blog_sample.users'
2025-07-21 10:03:48.647 JST すべてのメタデータ更新が完了しました。

フック実行と定期実行について

Cloud Run Job を任意のタイミングで実行する方法としては、Cloud Scheduler でスケジュール実行を設定するか、Cloud Functions を設定して、既存の ELT ツールでの連携時に実行するなどができます。

まとめ

スクリプト 1 つで実現できて、メタデータの管理方法にも yaml ファイルの切り方で柔軟性を持たせられる。

ご紹介の通り、BigQuery テーブルのメタデータを更新するだけであれば、Cloud Run Jobs と SDK の構成が最もシンプルでした。

(内容は割愛しますが)Dataform も試してみたのですが、transform ではなくメタデータだけを更新したいというケースでは今回の構成よりも手間がかかりました。

最後に。

BigQuery テーブルのメタデータを更新する方法について模索し始めたのは、もう一つ動機があります。

前回の記事で BigQuery の自動メタデータ生成(Automated Metadata Curation)を試しました。確かに便利でしたが、同時に、詳細にそのテーブルやカラムのことを伝えるなら、どこまで自動生成に頼れるのかという問いも感じていました。

例えば、「このデータは3日後に自動で削除される(=日時範囲が限定的なレコードしか存在しないため、集計には使えないと判断できる)」など、アプリケーションや業務の文脈に強く依存する情報が存在します。こうしたメタデータは、現時点では人間が手で書くしかありません。(もちろん、どこまで伝えるのかはありますが)

今後、AI によるメタデータ補完や対話的なデータ探索が進化していくにしても、こうした背景を伴う説明は、手動での記述が求められる場面がまだ一定あるのではないかと感じています。

(まだプレビュー版ではありますが)BigQueryの自動メタデータ生成は英語ですし、人が読みやすいことを考えると、メタデータは日本語で管理したい部分もあります。

こうした背景も踏まえると、実務において今回のような仕組みも、現実的な選択肢のひとつとして持っておいて良いといえそうです。