Contents
はじめに – BigQuery データライフサイクル自動化の全体像
ポイント
- テーブル・パーティションごとに 保持期限 (expiration) と タイムトラベル期間 をコードで管理すれば、ヒューマンエラーを排除しつつ CI/CD に完全統合できる。
- 法令遵守(GDPR、個人情報保護法)だけでなく、ストレージ料金やクエリスキャンコストの最適化にも直結する重要な設定です。
対象読者
- BigQuery のテーブル設計・運用を担当しているデータエンジニア
- インフラを IaC で管理したい DevOps エンジニア
- コスト削減とコンプライアンスを同時に実現したいプロダクトマネージャ
本記事の構成
| セクション | 内容 |
|---|---|
| 1. テーブル・パーティションの expiration と time‑travel の正しい設定方法 | bq CLI、REST API、Terraform それぞれでの具体例を掲載 |
| 2. Scheduled Queries(Data Transfer Service)で定期クレンジングジョブを構築 | UI 手順と bq mk --transfer_config によるコード化 |
| 3. Dataform / dbt によるモデル管理と環境別デプロイパイプライン | 環境変数活用のベストプラクティス |
| 4. Terraform と Cloud Build によるインフラ全体のコード化 | テーブル expiration、Scheduled Query、IAM ロールを一元管理 |
| 5. コスト最適化・モニタリング・最小権限設定 | GCP の公式ドキュメントに基づく推奨設定 |
1. BigQuery のテーブル・パーティションライフサイクル設定
1‑1. expiration_timestamp と time_travel_window_days の基本
| プロパティ | 説明 | 公式ドキュメント |
|---|---|---|
expiration_timestamp |
テーブルまたはパーティションが自動的に削除される UTC 時刻(RFC3339) | https://cloud.google.com/bigquery/docs/table-expiration |
time_travel_window_days |
データのスナップショットを保持できる日数。デフォルトは 7 日、最大 90 日 | https://cloud.google.com/bigquery/docs/time-travel |
ポイント:
expiration_timestampはテーブル単位でもパーティション単位でも設定可能です。一方time_travel_window_daysはテーブル全体に対してのみ適用でき、個別のパーティションには影響しません。
1‑2. 正しい bq CLI の例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# ① テーブル全体の有効期限を「現在から180日後」に設定 EXPIRATION=$(date -d "+180 days" --utc +%Y-%m-%dT%H:%M:%SZ) bq update \ --expiration_timestamp="${EXPIRATION}" \ project.dataset.my_table # ② パーティション単位で expiration を 30 日に設定(日付パーティションの場合) bq query --use_legacy_sql=false ' ALTER TABLE `project.dataset.partitioned_table` SET OPTIONS ( partition_expiration_days = 30 ); ' # ③ time‑travel の保持期間を 7 日に短縮(課金削減) bq update \ --time_travel_window_days=7 \ project.dataset.my_table |
注意:
--time_travel_window_daysが正しいオプション名です。過去のドキュメントでは--time_travel_windowと記載されていましたが、現在はサポート外です。
1‑3. Terraform における expiration の書き方
Terraform の google_bigquery_table リソースは ISO8601(RFC3339)形式の文字列 が必要です。timestamp() 関数は使用できませんので、静的文字列または formatdate で生成した文字列を渡します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
resource "google_bigquery_table" "events_partitioned" { dataset_id = google_bigquery_dataset.analytics.dataset_id table_id = "events" time_partitioning { type = "DAY" field = "event_date" expiration_ms = 2592000000 # 30 days (ミリ秒) } # テーブル全体の有効期限を「作成日から60日後」に設定 # 例: 2026-06-22T00:00:00Z(ISO8601) expiration_time = "2026-06-22T00:00:00Z" # time travel の保持期間はデフォルト7日。変更したい場合は下記を有効化 # time_travel_window_days = 7 } |
ポイント:
expiration_timeに直接文字列を書かず、変数で管理すると環境ごとの差分が出にくくなります(例:var.table_expiration_iso8601)。
2. Scheduled Queries と Data Transfer Service を使ったクレンジングジョブ
2‑1. コンソール UI での作成手順(概要)
| 手順 | 内容 |
|---|---|
| ① | BigQuery コンソール → 「クエリエディタ」へ移動し、削除クエリを記述 |
| ② | 「保存」→「スケジュール実行」を選択 |
| ③ | スケジュール設定(例: 毎日 02:00)・リージョン us-central1 を指定 |
| ④ | サービス アカウント に roles/bigquery.dataEditor と roles/bigquery.jobUser のみを付与(最小権限) |
削除クエリ例
|
1 2 3 |
DELETE FROM `project.dataset.partitioned_table` WHERE _PARTITIONDATE < DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY); |
ポイント:
_PARTITIONDATEは自動的にパーティションプルーニングを行うため、スキャンコストが最小化されます。
2‑2. bq mk --transfer_config によるコード化(CLI)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
cat > scheduled_query.json <<'EOF' { "name": "daily_partition_cleanup", "destination_dataset_id": "analytics", "display_name": "Daily Partition Cleanup", "schedule": "every 24 hours", "params": { "query": "DELETE FROM `project.analytics.events` WHERE _PARTITIONDATE < DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);", "writeDisposition": "WRITE_TRUNCATE" }, "data_source_id": "scheduled_query" } EOF bq mk --transfer_config \ --project_id=project \ --data_source=scheduled_query \ scheduled_query.json |
--data_source=scheduled_queryは Data Transfer Service が提供する Scheduled Query データソースです。- JSON 内のクエリ文字列はシングルクオートで囲み、内部のバックティック(
`)はエスケープ不要ですが、全体を'EOF'で囲むことでシェル展開を防ぎます。
3. Dataform / dbt によるモデル管理と環境別デプロイパイプライン
3‑1. 環境変数でテーブルスキーマ・名前を抽象化
dataform.sqlx(Dataform)または models/*.sql(dbt)のテンプレート例です。ここでは 環境変数 BQ_SCHEMA を使用しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-- dataform/sqlx ファイル(例: models/partitioned_events.sqlx) config { type: "incremental", uniqueKey: "id", schema: "{{ env_var('BQ_SCHEMA') }}", -- dev / staging / prod 用スキーマを切り替える tags: ["partitioned"] } /* 前日分だけ増分処理 */ SELECT id, user_id, event_date, metric FROM {{ ref("raw_events") }} WHERE _PARTITIONDATE = DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY) |
env_var('BQ_SCHEMA')は Cloud Build の置換変数や GitHub Actions のシークレットから注入できます。- incremental モードとパーティションキー
_PARTITIONDATEにより、毎日増分だけが処理されます。
3‑2. CI/CD パイプライン例(Cloud Build)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# cloudbuild.yaml steps: - name: 'gcr.io/cloud-builders/gcloud' entrypoint: 'bash' args: - '-c' - | # ブランチ名から環境を判定(main → prod, それ以外 → dev) if [[ "$BRANCH_NAME" =~ ^(main|master)$ ]]; then export BQ_SCHEMA=prod_schema else export BQ_SCHEMA=dev_schema fi # npm パッケージのインストールと Dataform 実行 npm install -g @dataform/cli dataform run --vars "{'BQ_SCHEMA':'$BQ_SCHEMA'}" images: - 'gcr.io/cloud-builders/gcloud' timeout: 1200s |
--varsオプションで環境変数をテンプレートに渡すことで、同一コードベースが dev / prod 両方で安全に動作します。- 同様のロジックは GitHub Actions の
if:条件式でも実装可能です。
4. CI/CD フローとインフラコード化 – Terraform / gcloud と Cloud Scheduler の連携
4‑1. 完全な Terraform 定義(テーブル、Scheduled Query、IAM)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
provider "google" { project = var.project_id region = "us-central1" } /* ---------- データセット ---------- */ resource "google_bigquery_dataset" "analytics" { dataset_id = "analytics" location = "US" } /* ---------- テーブル(expiration とパーティション) ---------- */ resource "google_bigquery_table" "events_partitioned" { dataset_id = google_bigquery_dataset.analytics.dataset_id table_id = "events" time_partitioning { type = "DAY" field = "event_date" expiration_ms = 2592000000 # 30 days } # ISO8601 形式でテーブル全体の有効期限を設定(例: 2026-06-22T00:00:00Z) expiration_time = "2026-06-22T00:00:00Z" } /* ---------- IAM:Data Transfer Service が使用する最小権限 ---------- */ resource "google_project_iam_member" "bq_data_editor" { project = var.project_id role = "roles/bigquery.dataEditor" member = "serviceAccount:${google_service_account.bq_transfer.email}" } resource "google_project_iam_member" "bq_job_user" { project = var.project_id role = "roles/bigquery.jobUser" member = "serviceAccount:${google_service_account.bq_transfer.email}" } /* ---------- Data Transfer(Scheduled Query) ---------- */ resource "google_bigquery_data_transfer_config" "daily_cleanup" { display_name = "Daily Partition Cleanup" data_source_id = "scheduled_query" destination_dataset_id = google_bigquery_dataset.analytics.dataset_id schedule = "every 24 hours" params = { query = <<EOT DELETE FROM `${var.project_id}.analytics.events` WHERE _PARTITIONDATE < DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY); EOT writeDisposition = "WRITE_TRUNCATE" } } /* ---------- Service Account for Data Transfer ---------- */ resource "google_service_account" "bq_transfer" { account_id = "bq-data-transfer" display_name = "BigQuery Data Transfer Service SA" } |
- 重要:
roles/bigquery.dataEditorとroles/bigquery.jobUserのみを付与することで、最小権限の原則(Principle of Least Privilege)を満たします。 expiration_timeは文字列で記述し、Terraform が自動的に RFC3339 形式として扱います。
4‑2. Cloud Build で Terraform を実行するパイプライン
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# cloudbuild.yaml(Terraform の CI/CD パイプライン) steps: - name: 'hashicorp/terraform:1.5.0' entrypoint: 'sh' args: - '-c' - | terraform init -input=false terraform fmt -check terraform validate terraform plan -out=tfplan # 自動承認で適用(本番環境では手動レビューを推奨) terraform apply -auto-approve tfplan timeout: 900s options: substitutionOption: ALLOW_LOOSE |
- コードレビュー:
terraform fmt -checkとterraform validateが必ず通過すれば、コードフォーマットと構文エラーは事前に防げます。 - ドリフト検知:
cloudscheduler jobs create httpで 1 週間ごとにterraform planを走らせ、差分があれば Slack 等へ通知する仕組みも簡単に追加可能です。
5. コスト最適化・モニタリングと実装時の注意点
5‑1. ストレージコスト削減テクニック
| 手法 | 効果 |
|---|---|
| Expiration をパーティション単位で設定 | 古いデータだけが自動削除され、無駄な長期保存を防止 |
| time‑travel window の短縮(例: 7 日) | スナップショット保持にかかるストレージ課金が最大 90% 削減 |
| オフピークでの Scheduled Query 実行 (02:00) | クエリ実行コスト自体は変わらないが、他ジョブとの競合が減り全体スループット向上 |
| Long‑Term Storage への自動移行 | 90 日以上更新の無いテーブルは自動的に Long‑Term Storage に格納され、1 TB あたり $10(標準) → $5 程度に削減。公式情報: https://cloud.google.com/bigquery/docs/long-term-storage |
5‑2. Cloud Monitoring アラート例
|
1 2 3 4 5 6 7 8 |
gcloud monitoring policies create \ --notification-channels=projects/${PROJECT_ID}/notificationChannels/${CHANNEL_ID} \ --condition-display-name="Scheduled Query Failures" \ --condition-filter='metric.type="scheduler.googleapis.com/job/execution_count" AND resource.type="cloud_scheduler_job"' \ --condition-comparison="COMPARISON_GT" \ --condition-threshold-value=0 \ --condition-duration="60s" |
| メトリック | 推奨閾値 | アクション |
|---|---|---|
bigquery.googleapis.com/query/count(失敗) |
5 分間に 3 回以上 | Slack 通知 + Cloud Build の自動停止 |
bigquery.googleapis.com/storage/bytes_used |
ストレージ上限の 80% 超過 | PagerDuty アラート |
scheduler.googleapis.com/job/execution_count(失敗) |
1回失敗で即時通知 | Cloud Function によるリトライ実装 |
5‑3. IAM ロールとリージョン制約
- 最小権限:Scheduled Query と Data Transfer Service が必要なロールは以下の2つだけです。
hcl
roles/bigquery.dataEditor # テーブルデータの書き換え(DELETE/INSERT)
roles/bigquery.jobUser # ジョブ実行権限 - リージョン:Data Transfer Service は同一ロケーション内でしか動作しません。マルチリージョン環境の場合は、バックアップ先も同じロケーション(例:
US)に統一してください。
5‑4. 法令遵守と証跡管理
- 保持期間:GDPR 等の要件に合わせてテーブルごとに
expiration_timestampを設定し、削除前に必ずバックアップ(Cloud Storage に暗号化保存)を取得。 - 監査ログ:BigQuery の Data Access Logs (
bigquery.googleapis.com/activity) と Cloud Audit Logs を有効化し、削除・更新操作の証跡を Stackdriver Logging で保持します。
公式情報参照
- テーブル期限設定: https://cloud.google.com/bigquery/docs/table-expiration
- Time‑travel の詳細: https://cloud.google.com/bigquery/docs/time-travel
- IAM ベストプラクティス: https://cloud.google.com/iam/docs/understanding-roles
まとめ
| 項目 | 実装ポイント |
|---|---|
| テーブル・パーティションの保持 | expiration_timestamp(ISO8601)と partition_expiration_days を Terraform で宣言的に管理 |
| タイムトラベルコスト抑制 | --time_travel_window_days=7(CLI)または time_travel_window_days = 7(Terraform)を設定 |
| 定期クレンジング | Scheduled Queries を UI または bq mk --transfer_config で IaC 化し、最小権限ロールだけ付与 |
| モデル管理 | Dataform / dbt の環境変数化と Cloud Build による自動デプロイ |
| CI/CD とインフラコード化 | Terraform + Cloud Build で全リソースを一元管理、ドリフト検知は Cloud Scheduler 経由で定期 plan 実行 |
| コスト・モニタリング | パーティション単位の expiration、time‑travel 縮小、Monitoring アラートで異常早期検知 |
| コンプライアンス | 法的保持期間に合わせた expiration 設定とバックアップ/監査ログの取得 |
この手順を踏むことで、BigQuery のデータライフサイクルがコードとして完全に管理できるだけでなく、コスト削減・コンプライアンス遵守・運用リスク低減という 3 つの大きなメリットを同時に実現できます。ぜひ自組織の CI/CD パイプラインへ取り入れてみてください。