4. 基础设施即代码入门

在持续交付体系中,工作区、集群、作业和权限如果依赖人工在 UI 上点击创建,就会出现“环境漂移”:开发环境能跑、生产环境缺一个权限或集群配置不同,导致发布失败。基础设施即代码(Infrastructure as Code,IaC)的核心思想是:把这些资源写成版本化的声明文件,由工具负责计算差异并执行变更。本节围绕 Databricks 场景,讲清 Terraform 与 Databricks Asset Bundles(DAB)两种主流方式的定位、写法与协作关系。

一、两种 IaC 工具的分工

Terraform 由 HashiCorp 提供,通过 Databricks Provider 管理“平台侧资源”:工作区、集群策略、实例池、用户与组、服务主体、权限授权(如 databricks_permissions)、Unity Catalog 的 catalog/schema 等。它的状态文件(state)记录了资源与真实环境的映射,适合管理生命周期长、跨云、跨服务的底层设施。

Databricks Asset Bundles 是 Databricks 官方推出的打包与部署工具,基于 databricks.yml 描述“数据与 AI 项目侧资源”:作业(job)、流水线(pipeline)、模型服务、以及作业所依赖的 notebook、Python wheel、配置文件。它天然支持多目标(dev/staging/prod)、变量覆盖和 databricks bundle deploy 一键部署,更贴近数据工程师的日常交付。

实践中常见的组合是:Terraform 负责工作区、集群策略、Unity Catalog 与权限等“地基”,DAB 负责作业与流水线等“上层应用”。两者通过集群策略 ID、服务主体、catalog 名称等参数衔接。

Terraform 与 DAB 的职责分层与衔接 Terraform(平台地基) 工作区 / 网络 / 存储 集群策略 / 实例池 用户 / 组 / 服务主体 Unity Catalog 对象 databricks_permissions:目录 / 表 / 作业授权 DAB(项目应用) databricks.yml 作业 / 流水线定义 notebook / wheel 源码 targets: dev / prod bundle deploy / run / destroy 策略ID 服务主体 远程 State(S3 / ADLS + 锁) 记录资源映射,支持 plan 差异对比 多人协作需后端锁,避免并发写坏状态 Git 仓库(单一事实来源) .tf 与 databricks.yml 同仓版本化 PR 触发 plan,合并触发 apply / deploy CI/CD 流水线 terraform plan → 人工审批 → terraform apply → databricks bundle deploy -t prod 变更可审计、可回滚,环境一致

二、Terraform 声明式管理示例

下面示例用 Terraform 创建一个集群策略、一个服务主体,并把作业运行权限授予该服务主体。示例参数为教学假设值,实际使用时需替换为你的工作区地址与主体 ID。

terraform {
  required_providers {
    databricks = { source = "databricks/databricks", version = "~> 1.50" }
  }
  backend "s3" {
    bucket = "corp-tfstate"
    key    = "databricks/prod/terraform.tfstate"
    region = "cn-north-1"
  }
}

provider "databricks" {
  host = var.workspace_url
}

resource "databricks_cluster_policy" "etl_policy" {
  name = "etl-standard-policy"
  definition = jsonencode({
    "spark_version"    = { "type" = "fixed", "value" = "14.3.x-scala2.12" },
    "node_type_id"     = { "type" = "allowlist", "values" = ["i3.xlarge", "i3.2xlarge"] },
    "autoscale.min_workers" = { "type" = "range", "minValue" = 2, "maxValue" = 8 },
    "custom_tags.team" = { "type" = "fixed", "value" = "data-eng" }
  })
}

resource "databricks_service_principal" "etl_sp" {
  display_name = "sp-etl-prod"
}

resource "databricks_permissions" "job_run" {
  job_id = var.etl_job_id
  access_control {
    service_principal_name = databricks_service_principal.etl_sp.application_id
    permission_level       = "CAN_MANAGE_RUN"
  }
}

执行流程为:terraform init 初始化后端与 Provider,terraform plan 输出将要新增/修改/销毁的资源清单,确认无误后 terraform apply 落地。若把 autoscale.max_workers 的 maxValue 从 8 改为 12,plan 会显示该策略的原地更新(update in-place),apply 后工作区策略即时生效,无需重建集群。

三、DAB 声明式管理示例

DAB 用一份 databricks.yml 描述作业及其多环境差异。下面示例定义一个每日 ETL 作业,dev 使用单节点、prod 使用自动伸缩集群,并引用 Terraform 创建的策略。

bundle:
  name: daily-etl

variables:
  cluster_policy_id:
    description: 由 Terraform 输出的集群策略 ID

targets:
  dev:
    workspace:
      host: https://dev-workspace.cloud.databricks.com
    variables:
      cluster_policy_id: "D0A1B2C3D4E5F6"
  prod:
    workspace:
      host: https://prod-workspace.cloud.databricks.com
    variables:
      cluster_policy_id: "F6E5D4C3B2A1D0"

resources:
  jobs:
    daily_etl_job:
      name: "daily-etl-${bundle.target}"
      schedule:
        quartz_cron_expression: "0 0 2 * * ?"
        timezone_id: "Asia/Shanghai"
      job_clusters:
        - job_cluster_key: etl_cluster
          new_cluster:
            policy_id: ${var.cluster_policy_id}
            spark_version: "14.3.x-scala2.12"
            num_workers: 2
      tasks:
        - task_key: ingest
          job_cluster_key: etl_cluster
          notebook_task:
            notebook_path: ./src/ingest.py

部署命令为 databricks bundle validate 校验语法与变量,databricks bundle deploy -t prod 上传源码并创建/更新作业,databricks bundle run daily_etl_job -t prod 触发一次运行。DAB 会在工作区中维护部署状态,重复 deploy 是幂等的。

四、权限与状态管理要点

权限声明要遵循最小权限原则:作业执行用服务主体而非个人账号,避免人员离职导致作业中断;目录与表权限通过 databricks_grants 或 databricks_permissions 显式声明,不要依赖 UI 手工授权。Terraform state 必须放远程后端并开启锁,禁止把 state 提交到 Git;DAB 的部署状态由工作区托管,但源码与 databricks.yml 必须进 Git。

推荐协作顺序:先用 Terraform 建好工作区、策略、服务主体与 Unity Catalog,把策略 ID、服务主体 application_id 作为变量传给 DAB;DAB 只负责作业与流水线。这样职责清晰,回滚时也能分层处理。
常见坑:Terraform 与 DAB 同时管理同一个作业会导致状态冲突;修改集群策略后未重新 deploy DAB,作业仍引用旧策略;state 文件被多人同时 apply 且无锁,造成资源丢失。建议在 CI 中固定执行顺序并加审批门禁。

掌握本节内容后,你应能判断哪些资源交给 Terraform、哪些交给 DAB,并写出可版本化、可评审、可回滚的声明式配置,为后续的持续交付流水线打下基础。