メインコンテンツへジャンプ

Unityカタログの分散・非集中管理に向けた自動化ガイド

Terraformで、さまざまなガバナンスモデルに対応したUnity Catalogの大規模デプロイを実現するには?
ヴオン・グエン
ジーシャン・パパ
Mattia Zeni
Share this post

Original : An Automated Guide to Distributed and Decentralized Management of Unity Catalog

翻訳: junichi.maruyama 

Unity Catalogは、あらゆるクラウド上のレイクハウスにあるすべてのデータとAI資産に対して、統一されたガバナンスソリューションを提供します。顧客がUnity Catalogを採用する際、コードアプローチとしてのインフラストラクチャを使用して、これをプログラム的かつ自動的に行いたいと考えています。Unity Catalogでは、Unity Catalogのオブジェクトの最上位コンテナであるメタストアがリージョンごとに1つ存在します。このメタストアには、データ資産(テーブルとビュー)と、アクセスを制御する権限が格納されています。

このことは、Unity Catalogの管理機能を担うプラットフォーム/ガバナンスチームを一元化していない組織にとって、新たな課題となる。具体的には、これらの組織内のチームは、単一のメタストアについて協力し、連携しなければならなくなりました。つまり、互いに完全に分離した状態でアクセスを管理し、監査を実行する方法です。

このブログポストでは、お客様がDatabricks Terraform providerにおけるUnity Catalogオブジェクトのサポートを活用して、レイクハウス上の分散ガバナンスパターンを効果的に管理する方法について説明します。

私たちは2つの解決策を提示します:

  • Unity Catalogでアセットを作成する際に、チームに責任を完全に委ねるもの。
  • チームがUnity Catalogで作成できるリソースを制限するもの

Unity Catalogメタストアの作成

1回限りのブートストラップアクティビティとして、お客様は、運用する地域ごとにUnity Catalogメタストアを作成する必要があります。これは、ブレイクグラスシナリオでのみアクセス可能な高度な特権であるアカウント管理者、すなわち、自動パイプラインで使用するために承認ワークフローを必要とするsecret vaultに保存されたユーザー名とパスワードが必要です。

アカウント管理者は、AWS上でユーザー名&パスワードを使って認証する必要があります:

provider "databricks" {
  host       = "https://accounts.cloud.databricks.com"
  account_id = var.databricks_account_id
  username   = var.databricks_account_username
  password   = var.databricks_account_password
}

または、Azure上で彼らのAADトークンを使用する:

provider "databricks" {
  host       = "https://accounts.azuredatabricks.net"
  account_id = var.databricks_account_id
  auth_type  = "azure-cli" # or azure-client-secret or azure-msi
}

Databricks Account Adminが提供する必要があります:

  1. 管理テーブルのデータを保存するデフォルトの場所となる単一のクラウドストレージ(S3/ADLS)
  2. Unity Catalogが(1)のクラウドストレージにアクセスするために使用する、単一のIAMロール/マネージドID

Terraformのコードは以下のようなものになります(AWSの例)

resource "databricks_metastore" "this" {
  name          = "primary"
  storage_root  = var.central_bucket
  owner         = var.unity_admin_group
  force_destroy = true
}

resource "databricks_metastore_data_access" "this" {
  metastore_id = databricks_metastore.this.id
  name         = aws_iam_role.metastore_data_access.name
  aws_iam_role {
    role_arn = aws_iam_role.metastore_data_access.arn
  }
  is_default = true
}

チームでは、個々のカタログごとに管理対象テーブルのロケーションとアイデンティティを設定したり、スキーマレベルでさらに細かく設定することで、テーブルにこのデフォルトのロケーションとアイデンティティを使用しないことを選択できます。管理対象テーブルが作成されると、データはスキーマの場所(存在する場合)を使用してカタログの場所(存在する場合)にフォールバックし、前の2つの場所が設定されていない場合にのみメタストアの場所にフォールバックして保存されます。

メタストア管理者を指名する

メタストアを作成する際に、メタストア管理者としてunity_admin_groupを指名しています。メタストア内のすべてのオブジェクトをリストアップし、アクセスを管理できる中央当局を持つことを避けるために、このグループを空にしておくことにします。

resource "databricks_group" "admin_group" {
  display_name = var.unity_admin_group
}

高い管理能力を必要とする例外的なブレークグラス・シナリオ(初期アクセスの設定、カタログ所有者が組織を離れた場合のカタログ所有者の変更など)のために、ユーザーをグループに追加することができます。

resource "databricks_user" "break_glass" {
  for_each  = toset(var.break_glass_users)
  user_name = each.key
  force     = true
}

resource "databricks_group_member" "admin_group_member" {
  for_each  = toset(var.break_glass_users)
  group_id  = databricks_group.admin_group.id
  member_id = databricks_user.break_glass[each.value].id
}

チームへの責任委譲

各チームは、独自のカタログを作成し、そのデータへのアクセスを管理する責任があります。各新規チームが独立して活動するために必要な権限を取得するために、最初のブートストラップ活動が必要です。

その後、アカウント管理者は次のことを実行する必要があります:

  • team-adminsというグループを作成します。
  • CREATE CATALOG、CREATE EXTERNAL LOCATION、およびDelta Sharingを使用している場合はGRANT CREATE SHARE、PROVIDER、RECIPIENTをこのチームに付与する(オプションとして)
resource "databricks_group" "team_admins" {
  display_name = "team-admins"
}

resource "databricks_grants" "sandbox" {
  metastore = databricks_metastore.this.id
  grant {
    principal  = databricks_group.team_admins.display_name
    privileges = ["CREATE_CATALOG", "CREATE_EXTERNAL_LOCATION", "CREATE SHARE", "CREATE PROVIDER", "CREATE RECIPIENT"]
  }
}

新しいチームがオンボードで参加する場合、信頼できるチーム管理者を team-admins グループに配置します。

resource "databricks_user" "team_admins" {
  for_each  = toset(var.team_admins)
  user_name = each.key
  force     = true
}

resource "databricks_group_member" "team_admin_group_member" {
  for_each  = toset(var.team_admins)
  group_id  = databricks_group.team_admins.id
  member_id = databricks_user.team_admins[each.value].id
}

team-adminsグループのメンバーは、アカウント管理者やメタストア管理者が操作しなくても、チームのために新しいカタログや外部ロケーションを簡単に作成できるようになりました。

新しいチームのオンボーディング

Databricksに新しいチームを追加するプロセスでは、新しいチームがワークスペースやデータ資産を自由に設定できるように、アカウント管理者による初期作業が必要です:

  • 新しいワークスペースは、チーム X の管理者(Azure)またはアカウント管理者(AWS)により作成されます。
  • アカウント管理者は、既存のメタストアをワークスペースにアタッチします。
  • アカウント管理者は、このチーム専用のグループ「team_X_admin」を作成し、オンボーディングされるチームの管理者を含めます。
resource "databricks_group" "team_X_admins" {
  display_name = "team_X_admins"
}

resource "databricks_user" "team_X_admins" {
  for_each  = toset(var.team_X_admins)
  user_name = each.key
  force     = true
}

resource "databricks_group_member" "team_X_admin_group_member" {
  for_each  = toset(var.team_X_admins)
  group_id  = databricks_group.team_X_admins.id
  member_id = databricks_user.team_X_admins[each.value].id
}
  • アカウント管理者は、ストレージクレデンシャルを作成し、所有者を「team_X_admin」グループに変更して使用する。チーム管理者がクラウドテナントで信頼されている場合、クレデンシャルがアクセスできるストレージを制御できます(例:自分のS3バケットやADLSストレージアカウントのいずれか)
resource "databricks_storage_credential" "external" {
  name = "team_X_credential"
  azure_managed_identity {
    access_connector_id = azurerm_databricks_access_connector.ext_access_connector.id
  }
  comment = "Managed by TF"
  owner   = databricks_group.team_X_admins.display_name
}
  • その後、アカウント管理者は、新しく作成されたワークスペースをUCメタストアに割り当てます。
resource "databricks_metastore_assignment" "this" {
  workspace_id         = var.databricks_workspace_id
  metastore_id         = databricks_metastore.this.id
  default_catalog_name = "hive_metastore"
}
  • チームXの管理者は、必要な数のカタログと外部ロケーションを作成します。
    • チーム管理者はメタストア所有者やアカウント管理者ではないので、自分が所有していないエンティティ(カタログ/スキーマ/テーブルなど)、つまり他のチームのものとやり取りすることはできません。

チームへの責任委譲が限定的

組織によっては、中央のメタストアにアセットを作成する際に、チームを自律させたくない場合があります。実際、複数のチームにそのような資産を作成する能力を与えることは、統治が難しく、命名規則を強制することができず、環境をきれいに保つことが困難です。

このようなシナリオでは、各チームが管理者に作成してもらいたいアセットのリストを添えてリクエストを提出するモデルを提案します。そのチームはアセットのオーナーとなり、他者への権限付与を自律的に行うことができるようになります。

このようなリクエストをできるだけ自動化するために、CI/CDを使った方法を紹介します。管理者チームは、自分たちが好むバージョン管理システムの中央リポジトリを所有し、そこに組織内のDatabricksをデプロイするすべてのスクリプトを格納します。各チームはこのリポジトリにブランチを作成し、定義済みのテンプレート(Terraform Module)を使って自分たちの環境のTerraform設定ファイルを追加することができます。チームの準備が整ったら、プルリクエストを作成します。この時点で、中央管理者はプルリクエストをレビューし(適切なチェックで自動化することも可能)、メインブランチにマージする必要があります。

このアプローチでは、個々のチームが行うことをよりコントロールすることができますが、セントラルアドミニストレータのチームではいくつかの(限定的で自動化可能な)活動が必要となります。

このシナリオでは、以下のTerraformスクリプトは、アカウントadminにしたService Principal (00000000-0000-0000-00000000) を使ってCI/CDパイプラインから自動的に実行されます。このようなサービスプリンシパルをアカウント管理者にする単発の操作は、例えば既存のアカウント管理者が手動で実行する必要があります:

resource "databricks_service_principal" "sp" {
  application_id = "00000000-0000-0000-0000-000000000000"
}

resource "databricks_service_principal_role" "sp_account_admin" {
  service_principal_id = databricks_service_principal.sp.id
  role                 = "account admin"
}

新しいチームのオンボーディング

新しいチームがオンボーディングを希望する場合、以下のオブジェクトを作成するリクエストを提出する必要があります(Azureの例):

  • Team_X_admins というグループを作成し、Account Admin Service Principal (将来的にアセットの修正を可能にするため) とグループのメンバーを含む。
resource "databricks_group" "team_X_admins" {
  display_name = "team_X_admins"
}

resource "databricks_user" "team_X_admins" {
  for_each  = toset(var.team_X_admins)
  user_name = each.key
  force     = true
}

resource "databricks_group_member" "team_X_admin_group_member" {
  for_each  = toset(var.team_X_admins)
  group_id  = databricks_group.team_X_admins.id
  member_id = databricks_user.team_X_admins[each.value].id
}

data "databricks_service_principal" "service_principal_admin" {
  application_id = "00000000-0000-0000-0000-000000000000"
}

resource "databricks_group_member" "service_principal_admin_member" {   
  group_id  = databricks_group.team_X_admins.id
  member_id = databricks_service_principal.service_principal_admin.id
}
  • 新しいリソースグループを作成するか、既存のリソースグループを指定する
resource "azurerm_resource_group" "this" {
  name     = var.resource_group_name
  location = var.resource_group_region
}
  • プレミアムDatabricksのワークスペース
resource "azurerm_databricks_workspace" "this" {
  name                        = var.databricks_workspace_name
  resource_group_name         = azurerm_resource_group.this.name
  location                    = azurerm_resource_group.this.location
  sku                         = "premium"
}
  • 新しいストレージアカウント、または既存のストレージアカウントを提供する
resource "azurerm_storage_account" "this" {
  name                     = var.storage_account_name
  resource_group_name      = azurerm_resource_group.this.name
  location                 = azurerm_resource_group.this.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  account_kind             = "StorageV2"
  is_hns_enabled           = "true"
}
  • ストレージアカウントに新規にコンテナを作成する、または既存のコンテナを提供する
resource "azurerm_storage_container" "container" {
  name                  = "container"
  storage_account_name  = azurerm_storage_account.this.name
  container_access_type = "private"
}
  • A Databricks Access Connector
resource "azurerm_databricks_access_connector" "this" {
  name                = var.databricks_access_connector_name
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location
  identity {
    type = "SystemAssigned"
  }
}
  • Access Connectorに「Storage blob Data Contributor」ロールを割り当てる
resource "azurerm_role_assignment" "this" {
  scope                = azurerm_storage_account.this.id
  role_definition_name = "Storage Blob Data Contributor"
  principal_id         = azurerm_databricks_access_connector.metastore.identity[0].principal_id
}
  • 新しく作成したワークスペースにセントラルメタストアを割り当てる
resource "databricks_metastore_assignment" "this" {
  metastore_id = databricks_metastore.this.id
  workspace_id = azurerm_databricks_workspace.this.workspace_id
}
  • ストレージクレデンシャルの作成
resource "databricks_storage_credential" "storage_credential" {
  name            = "mi_credential"
  azure_managed_identity {
    access_connector_id = azurerm_databricks_access_connector.this.id
  }
  comment         = "Managed identity credential managed by TF"
  owner           = databricks_group.team_X_admins
}
  • 外部ロケーションを作成する
resource "databricks_external_location" "external_location" {
  name            = "external"
  url             = format("abfss://%s@%s.dfs.core.windows.net/",
                    "container",
                    "storageaccountname"
  )
  credential_name = databricks_storage_credential.storage_credential.id
  comment         = "Managed by TF"
  owner           = databricks_group.team_X_admins
  depends_on      = [
    databricks_metastore_assignment.this, databricks_storage_credential.storage_credential
  ]
}
  • カタログを作成する
resource "databricks_catalog" "this" {
  metastore_id = databricks_metastore.this.id
  name         = var.databricks_catalog_name
  comment      = "This catalog is managed by terraform"
  owner        = databricks_group.team_X_admins
  storage_root = format("abfss://%s@%s.dfs.core.windows.net/managed_catalog",
                    "container",
                    "storageaccountname"
  )
}

これらのオブジェクトが作成されると、チームは自律的にプロジェクトを開発し、必要に応じて他のチームメンバーやパートナーにアクセスできるようになります。

既存チーム用のアセットを修正

チームはUnityカタログで自律的にアセットを修正することもできません。これを行うには、彼らが作成したファイルを修正し、新しいプルリクエストを作成することによって、中央チームに新しいリクエストを提出することができます。

これは、新しいストレージ認証情報、外部ロケーション、カタログなどの新しいアセットを作成する必要がある場合にも当てはまります。

Unity Catalog + Terraform = 管理されたレイクハウス

上記では、Unity Catalogの有効化と継続的な管理のハードルを処理するために、組み込みの製品機能を活用するためのいくつかのガイドラインと推奨されるベストプラクティスを説明しました。

Unity Catalogのドキュメント[AWS, Azure]、Unity Catalog Terraformガイド[AWS, Azure]で詳細をご覧ください。

Databricks 無料トライアル

関連記事

プラットフォームブログ一覧へ