4. 基础设施即代码入门
在 Azure 数据平台上,数据工厂、Synapse 工作区和 Databricks 工作区往往不是孤立存在的:数据工厂负责编排与调度,Synapse 承担数仓与 SQL 计算,Databricks 负责 Spark 作业与笔记本。如果这些资源全部靠门户点击创建,环境之间就会出现配置漂移,测试环境能跑通的管道到了生产可能因为缺少某个托管私有终结点或角色分配而失败。基础设施即代码(IaC)要解决的核心问题,就是把这些资源的“期望状态”写成可版本化、可评审、可重复执行的声明式文件。
本节不讨论所有 Azure 资源的完整建模,而是聚焦三类数据工作负载的 IaC 定义方式,并对比 Bicep 与 Terraform 在表达这些资源时的差异。理解这些差异,才能在实际项目中选择合适的工具链。
一、三类资源的 IaC 建模要点
先看 Azure 数据工厂。在 Bicep 中,数据工厂本身是一个 Microsoft.DataFactory/factories 资源,但真正需要版本化的是它内部的管道、数据集、链接服务和触发器。这些子资源在 ARM/Bicep 中通常以嵌套资源或独立资源的形式声明,例如一个链接服务需要引用数据工厂名称,并通过 properties.typeProperties 描述连接信息。关键点是:连接字符串、账户密钥等敏感值不应硬编码在模板里,而应通过 Key Vault 引用或参数传入。
Synapse 工作区的建模更复杂。一个可用的 Synapse 工作区至少涉及:工作区资源本身、ADLS Gen2 主存储账户、SQL 池(专用或按需)、Spark 池,以及可选的托管私有终结点。Bicep 中 Microsoft.Synapse/workspaces 需要指定 defaultDataLakeStorage,并依赖存储账户先创建。SQL 池和 Spark 池是工作区的子资源,创建顺序和命名规则必须与工作区一致。如果使用托管虚拟网络,还需要额外声明 managedVirtualNetwork 和相关防火墙规则。
Databricks 工作区的 IaC 定义相对独立,核心资源是 Microsoft.Databricks/workspaces,需要指定 sku(如 premium)、managedResourceGroupId 以及是否启用 enableNoPublicIp。但工作区内部的集群、笔记本、作业并不直接由 ARM/Bicep 管理,通常需要配合 Databricks REST API、Terraform 的 Databricks Provider 或 Databricks Asset Bundles 来完成。这是三类资源中 IaC 边界最需要提前明确的一点。
二、Bicep 与 Terraform 的表达差异
Bicep 是 Azure 原生的领域特定语言,编译后生成 ARM 模板。它的优势在于对 Azure 资源类型的支持最及时,资源之间的 dependsOn 和 existing 关键字能直接表达依赖与引用。例如定义一个 Synapse 工作区时,可以直接用 resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { name: storageAccountName } 引用已有存储账户,再在工作区属性中引用其 id。Bicep 的部署作用域可以是资源组、订阅或管理组,适合与 Azure DevOps 或 GitHub Actions 的 Azure 登录任务直接配合。
Terraform 的优势在于跨云一致性和状态管理。它通过 Provider 插件与 Azure API 交互,azurerm Provider 覆盖了数据工厂、Synapse 和 Databricks 工作区资源,而 databricks Provider 可以进一步管理工作区内部的集群、作业和权限。Terraform 的 plan 命令会先计算差异再执行,这对生产环境变更评审非常有用。但 Terraform 需要维护远程 State,State 中可能包含敏感信息,必须使用加密的 Blob 存储并开启状态锁定。
一个常见的组合策略是:用 Bicep 或 Terraform 的 azurerm 层创建 Azure 资源骨架(工作区、存储、网络、Key Vault),再用 databricks Provider 或 Databricks Asset Bundles 管理工作区内部的作业与笔记本。这样既保证了基础设施的声明式管理,又不会因为工作区内部对象频繁变更而触发整个工作区的重建。
三、一个可参考的参数化结构
无论选择哪种工具,建议把环境差异收敛到参数文件或变量文件中。以下是一个 Terraform 变量定义的示例结构,用于区分开发与生产环境:
variable "environment" {
type = string
description = "环境标识:dev / test / prod"
}
variable "location" {
type = string
default = "eastasia"
}
variable "synapse_sql_pool_sku" {
type = string
default = "DW100c"
}
variable "databricks_sku" {
type = string
default = "premium"
}
variable "adf_managed_private_endpoints" {
type = list(string)
default = []
}
在 dev.tfvars 中可以把 synapse_sql_pool_sku 设为 DW100c,生产环境则设为 DW500c;adf_managed_private_endpoints 在开发环境可以为空,生产环境必须列出所有需要私网接入的服务。这样同一套模块代码可以在不同环境重复执行,差异只体现在变量文件里。
四、部署顺序与验证
三类资源之间存在明确的依赖顺序。存储账户必须最先创建,因为 Synapse 工作区需要它作为主数据湖;Key Vault 也应在数据工厂和 Synapse 之前就绪,以便链接服务引用密钥;Databricks 工作区虽然相对独立,但如果需要访问 ADLS,也需要存储账户和相应的角色分配先完成。在 Bicep 中,这些顺序由 dependsOn 或隐式引用自动推导;在 Terraform 中,则由资源之间的引用关系决定。
部署完成后,验证不应只看“部署成功”。建议至少检查三项:第一,数据工厂的链接服务能否成功测试连接;第二,Synapse 工作区的 SQL 池状态是否为 Online,Spark 池是否可用;第三,Databricks 工作区能否通过令牌访问并列出集群。这些验证可以通过 Azure CLI、PowerShell 或 Terraform 的 output 配合后续脚本完成。
最后需要明确 IaC 的边界:它管理的是资源的期望状态,而不是数据本身。数据工厂的管道定义可以版本化,但管道运行产生的日志和切片状态不在 IaC 范围内;Synapse 的 SQL 池可以声明式创建,但池中的表结构和数据加载需要额外的数据库迁移工具;Databricks 的工作区可以声明式创建,但笔记本内容通常由 Git 集成或 Asset Bundles 管理。把边界划清楚,才能避免把 IaC 当成万能工具,也才能让持续交付流水线稳定运行。