GCP 版 Dataform がついに GA になりましたね。同時に定期実行の仕組みも出て、一通りの機能が揃った感がある。いまこそ買収以前の SaaS 版(Legacy 版)から GCP 版に移行する時!!
しかし GitHub リポジトリと連携する場合、登場人物が多くて難しくなっていると思う。 特に GCP に馴染みがなかったりデータ分析がメインの人は困りそう。公式ドキュメントには step by step で書いてあるものの、なぜ必要なのか分からないまま設定することになる。
なので全体像を図にしたり補足するという趣旨のエントリです。
Dataform とは
Dataform とは...という話はしません。公式ドキュメントや世間のブログ記事を読もう。
Dataform を使うと、テーブル同士の依存に基づいて順番に SQL を実行してデータパイプラインを作ったり、依存関係を可視化したり、データセット名に suffix を付与してステージング環境を作ったりできる。
現状の Dataform でカバーしきれない複雑な要件、具体的には更新や変換にリアルタイム性が要求されたり、外部の依存やトリガーに基づくパイプラインなどでは Cloud Dataflow や Clodu Composer が必要になるけど、データウェアハウス実装のかなりの範囲をカバーするサービスです。
Google Cloud のリソースとの関係
このあたりのドキュメントで登場する登場人物をまとめた。
- Quickstart: Create and execute a SQL workflow | Dataform | Google Cloud
- Create a Dataform repository | Google Cloud
- Connect to a third-party Git repository | Dataform | Google Cloud
① まず Dataform API を有効にして、コンソールから Dataform リポジトリを作る。
すると ② Dataform サービスアカウントが Cloud IAM に生えてくる。
service-{PROJECT_NUMBER}@gcp-sa-dataform.iam.gserviceaccount.com
の形式。
IAM 一覧では、ユーザが作ったもの以外はデフォルトでは非表示なので、"Google 提供のロール付与を含める" をチェックするとよい。
重要なのは、Dataform はこのサービスアカウントの権限で動作するということ。
なのでこいつに一通りの権限を付与してあげる必要がある。
③ GitHub に行き、Dataform のコードを保存するリポジトリを作る。
④このリポジトリを GCP 側から操作するため Personal access token (PAT) を払い出す
今 GitHub には 2 種類の PAT がある。
- personal access tokens (classic)
- fine-grained personal access token
classic は以前からあるが、リポジトリ単位でのアクセス制御ができない。新しい fine-grained を使い ③ で作ったリポジトリのみ許可するのが良い。classic では、scope に repo
を、fine-grained では Repository permissions で Contents: read and write
を与えればよい。
⑤ 再び GCP 側に戻り、Secret Manager でシークレットを作成し、④ で作成した PAT をバージョンとして保存する。
⑥ シークレットの "権限" タブで、② の Dataform サービスアカウントに roles/secretmanager.secretAccessor
ロールを付与して Dataform がこのトークンを利用できるようにする。
補足すると、ここでは個別のシークレットへのアクセス権を付与している。IAM コンソールの目立つボタンからロールを付与すると、プロジェクトレベルのアクセス権となり、一通りのシークレットにアクセスできる権限を付与することになるので注意。
これで Dataform コンソールでコードを書いて GitHub へ push できるようになった。
⑦ Dataform を使う目的は BigQuery のテーブルを作成したり更新したりすることなので、IAM で Dataform サービスアカウントに BigQuery を操作する以下の権限を付与する。
- BigQuery ジョブユーザー (
roles/bigquery.jobUser
) - BigQuery データ編集者 (
roles/bigquery.dataEditor
)
その他、別のプロジェクトのデータを参照したい場合は、そちらでも BigQuery データ閲覧者(roles/bigquery.dataViewer
) を付与する。更新したいならデータ編集者ね。Dataform 実行プロジェクトと BigQuery リソースのプロジェクトは異なっていても構わない。
Google Spreadsheet など Google ドライブのデータを外部テーブルとして扱いたい場合は、該当のファイルを Dataform サービスアカウントに共有すればよい。
(オプション) CI やその他のワークフローから実行する可能性があるなら、それ用のサービスアカウントを作っておくとよい。
② のサービスアカウントは、GCP が管理するもので Service Agentとも呼ばれる。追加のロールを付与することはできるけど、ユーザーが成り代わったり認証情報を入手したりはできないので、Dataform コンソールの外から利用できない。
IAM での権限は Google グループに対しても付与できるので、Dataform サービスエージェントと、CI 用のサービスアカウントを所属させておいて、そのグループに対して BigQuery の権限を与えるようにしておくと、2 つのサービスアカウントそれぞれ与えなくて済むし権限が食い違うこともなくなる。
GitHub リポジトリと連携しなくても使える
ここまで GitHub リポジトリと連携するつもりで書いていたけど、連携しなくても Dataform を使うことはできる。
ただし、差分を見たり特定の revision に戻したりする UI はないので、git リポジトリとしての便利さはほぼ得られない。2023-05-14 現在、HEAD にコミットするか戻すかどうか、Workspace の変更を main に push する操作だけできる。
ちょっと触ってみたいだけなら、連携部分は気にしないでいいと思う。必要になったら GitHub リポジトリと接続すればいい。Legacy 版と違って既にファイルのある GitHub リポジトリと連携することもできるし。
CI
GitHub 連携をしたら GitHub Actions で CI を整備したいですね。
上のオプション部分で作ったサービスアカウントを OIDC 連携で利用して、
- push したら compile に成功するかチェック & dry-run する
- main にマージしたら Dataform を実行する
などするようにしています。
CI で色々やる際の注意点として、ルートの package.json
の依存は @dataform/core
のみか、最小限にする必要がある。Dataform コンソールで SQLX をコンパイルする際に、バックグラウンドで npm install
が走っているようだけど、依存が増えるとタイムアウトしてまともに使えなくなる。@dataform/cli
が増えただけでダメ。
CI の中でだけインストールしたり $ npx -p @dataform/cli@latest dataform compile
などしています。
Can't install certain devDependencies [264598563] - Visible to Public - Issue Tracker
おまけ
Dataform 関連だけ抜き出した Terraform、google-beta が要ります。
resources.tf
variable "project" { type = string } variable "region" { type = string default = "asia-northeast1" } variable "dataform_repository_url" { type = string } variable "dataform_default_branch" { type = string default = "main" } data "google_project" "this" { project_id = var.project } resource "google_secret_manager_secret" "dataform_repository_token" { secret_id = "dataform_thirdparty_repository_token" replication { automatic = true } } resource "google_secret_manager_secret_version" "dataform_repository_token" { secret = google_secret_manager_secret.dataform_repository_token.id secret_data = "******" } # secret が state に書かれてしまうので、普段は手でコンソールから secret と version を作って # secret は terraform import、 version は data リソースで参照、みたいにしている # # data "google_secret_manager_secret" "dataform_repository_token" { # secret = google_secret_manager_secret.dataform_repository_token.id # } resource "google_dataform_repository" "dataform" { provider = google-beta name = "my-dataform-repository" region = var.region git_remote_settings { url = var.dataform_repository_url default_branch = var.dataform_default_branch authentication_token_secret_version = google_secret_manager_secret_version.dataform_repository_token.id } } # Dataform Service Agent へのロール付与 locals { dataform_agent_member = "serviceAccount:service-${data.google_project.this.number}@gcp-sa-dataform.iam.gserviceaccount.com" } resource "google_project_iam_member" "dataform_agent_roles" { for_each = toset([ "roles/bigquery.dataEditor", "roles/bigquery.jobUser", "roles/dataform.editor", "roles/dataform.serviceAgent", ]) project = var.project role = each.value member = local.dataform_agent_member depends_on = [ # Dataform Service Agent は最初の repository が作成された後に作られる google_dataform_repository.dataform ] } resource "google_secret_manager_secret_iam_member" "dataform_repository_token" { secret_id = google_secret_manager_secret.dataform_repository_token.secret_id role = "roles/secretmanager.secretAccessor" member = local.dataform_agent_member depends_on = [ google_dataform_repository.dataform ] } # CI などから Dataform を実行するためのサービスアカウント resource "google_service_account" "dataform" { account_id = "dataform" display_name = "dataform" description = "Dataform 実行用サービスアカウント" } resource "google_project_iam_member" "dataform_sa_roles" { for_each = toset([ "roles/bigquery.dataEditor", "roles/bigquery.jobUser", "roles/dataform.editor", ]) project = var.project role = each.value member = google_service_account.dataform.member }
おわりに
Dataform が買収されて進化が止まっている間にライバルの dbt が普及してしまった感がありますが、巻き返してほしいですね。
Google Cloud 組み込みだし、BigQuery を DWH として使っていたらすぐ使い始められるし、無料だし dbt Cloud のようにユーザー数課金でもない、第一の選択肢になると思います。
気が向いたら、定期実行とその通知について書こうと思います。