DataBrick 现代数据应用构建指南(二)
原文:
annas-archive.org/md5/bc35a4f08676703f400a5d1765ae165d译者:飞龙
第七章:使用 Unity Catalog 查看数据血缘
本章中,我们将深入探讨数据血缘在 Databricks 数据智能平台中的关键作用。你将学习如何追踪数据来源、可视化数据集转换、识别上下游依赖关系,并使用 Catalog Explorer 的血缘图功能来记录血缘。到本章结束时,你将掌握确保数据来自可信来源、并在问题发生前识别出破坏性变化的技能。
本章将涵盖以下主要主题:
-
在 Unity Catalog 中引入数据血缘
-
使用数据血缘 REST API 跟踪数据来源
-
可视化上下游数据转换
-
确定依赖关系和影响
-
实操实验 – 记录整个组织的数据血缘
技术要求
要跟随本章提供的示例,你需要拥有 Databricks 工作区的权限,以便创建和启动一个通用集群,从而导入并执行本章配套的笔记本。所有代码示例可以从本章的 GitHub 仓库下载,地址是 github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter07。本章将使用通用集群创建并运行多个新的笔记本,预计将消耗大约 5-10 Databricks 单位(DBUs)。
在 Unity Catalog 中引入数据血缘
数据血缘是指在 Unity Catalog(UC)中追踪可安全访问对象之间关系的能力,例如表格,使用户能够查看数据资产如何从上游来源形成,并验证下游的依赖关系。
图 7.1 – 数据血缘追踪数据流动及其在内部过程中的转化
在 Databricks 中,用户可以几乎实时地追踪数据资产的血缘,这样数据管理员就能确保他们使用的是最新的资产。此外,Unity Catalog 中的数据血缘跨越多个工作区,这些工作区连接到同一个 Unity Catalog 元存储,使得数据专业人士能够获得一个完整的、全面的视图,了解数据集如何被转化并相互关联。
数据血缘可以跨越 Databricks 数据智能平台中的多种可安全访问对象进行追踪,以下是一些例子:
-
查询
-
表格
-
表格列
-
笔记本
-
工作流
-
机器学习模型
-
Delta Live Tables(DLT)管道
-
仪表板
就像 Databricks 数据智能平台中的许多对象一样,你可以通过多种机制追踪血缘信息,包括通过 Databricks UI 使用 Catalog Explorer,或者通过消费 Data Lineage REST API。实际上,数据血缘信息会自动由 Databricks 数据智能平台捕获并记录在系统表中(详见第五章)。与其他保存在 Databricks 系统表中的系统信息一样,血缘信息可能会累积不少。为了节省存储成本,默认情况下,这些信息会保留一年。对于更长期的血缘存储需求,建议建立一个替代流程,将血缘信息附加到长期归档存储中。例如,假设一个组织需要保留多年的系统审计信息,那么就需要一个长期归档的 ETL 管道,将血缘数据复制到归档存储中。
在接下来的章节中,我们将介绍在 Databricks 中查看数据资产血缘的各种方式。
使用 Data Lineage REST API 跟踪数据来源
像 Databricks 数据智能平台中的许多可安全访问的对象一样,存在多种方式可以检索与对象相关的详细血缘信息。Databricks 中检索某个特定对象血缘信息的一种常见方式是通过 Data Lineage REST API。目前,Data Lineage REST API 仅限于检索表血缘信息的只读视图以及列血缘信息。
| UC 对象 | HTTP 动词 | 端点 | 描述 |
|---|---|---|---|
| 表 | GET | / api/2.0/lineage-tracking/table-lineage | 给定一个 UC 表名称,检索上游和下游表连接的列表,以及它们相关的笔记本连接信息 |
| 列 | GET | / api/2.0/lineage-tracking/column-lineage | 给定一个 UC 表名称和列名称,检索上游和下游列连接的列表 |
表 7.1 – Data Lineage REST API 获取与 UC 表和列对象的上游和下游连接相关的信息
然而,预计 Data Lineage REST API 将随着时间的推移不断发展,增加更多的功能,使数据管理员能够检索信息,甚至操作平台内数据资产的端到端血缘信息。
让我们来看一下如何使用 Lineage Tracking API 来检索本章附带的 GitHub 仓库中,由数据集生成器笔记本创建的表的上游和下游连接信息,仓库地址为github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter07。
首先,我们将在 Databricks 工作空间中创建一个全新的笔记本,并导入 requests Python 库。我们将专门使用 Python 的 requests 库来向 Databricks REST API 发送数据溯源请求,并解析来自 Databricks 控制平面的响应:
import requests
创建并启动一个通用集群,以便将笔记本附加到该集群并运行笔记本单元格。你需要生成一个 个人访问令牌(PAT),用于与 Databricks REST 端点进行身份验证并发送数据溯源 API 请求。强烈建议将 PAT 存储在 Databricks 秘密对象中(docs.databricks.com/en/security/secrets/secrets.html),以避免不小心泄露身份验证信息到你的 Databricks 工作空间。
重要提示
以下代码片段仅用于示范。你需要更新工作空间名称以匹配你 Databricks 工作空间的名称,并设置 API 令牌的值。
让我们使用 requests 库通过指定完全合格的端点,向数据溯源 API 发送请求:
response = requests.get(
f"https://{WORKSPACE_NAME}.cloud.databricks.com/api/2.0/lineage-tracking/table-lineage",
headers={
"Authorization": f"Bearer {API_TOKEN}"
},
json={
"table_name": FULLY_QUALIFIED_TABLE_NAME,
"include_entity_lineage": "true"
}
)
print(response.json())
接下来,让我们添加一些辅助函数,用于解析来自数据溯源 API 的响应,并以易于理解的格式打印连接信息。请在你的笔记本中添加一个新的单元格,并粘贴以下辅助函数:
def print_table_info(conn_type, table_info_json):
info = table_info_json["tableInfo"]
print(f"""
+---------------------------------------------+
| {conn_type.upper()} Table Connection Info
|---------------------------------------------|
| Table name: {info['name']}
|---------------------------------------------|
| Catalog name: {info['catalog_name']}
|---------------------------------------------|
| Table type: {info['table_type']}
|---------------------------------------------|
| Lineage timestamp: {info['lineage_timestamp']}
+---------------------------------------------+
""")
if conn_type.upper() == "UPSTREAMS":
print(f"""
|
\|/
""")
def print_notebook_info(conn_type, notebook_info):
print(f"""
+---------------------------------------------+
| {conn_type.upper()} Notebook Connection Info:
|---------------------------------------------|
| Workspace id: {str(notebook_info['workspace_id'])}
|---------------------------------------------|
| Notebook id: {str(notebook_info['notebook_id'])}
|---------------------------------------------|
| Timestamp: {notebook_info['lineage_timestamp']}
+---------------------------------------------+
""")
现在,让我们更新我们之前用于获取表格溯源信息的代码片段的响应部分,但这次我们将调用这些辅助函数:
if response.status_code == 200:
connection_flows = ["upstreams", "downstreams"]
for flow in connection_flows:
if flow in response.json():
connections = response.json()[flow]
for conn in connections:
if "tableInfo" in conn:
print_table_info(flow, conn)
elif "notebookInfos" in conn:
for notebook_info in conn["notebookInfos"]:
print_notebook_info(flow, notebook_info)
输出现在应该是来自我们数据溯源 API 的更加易读的响应,允许我们清晰地查看来自 Unity Catalog 中表的上游和下游表连接。
图 7.2 – 来自 Databricks Data Lineage REST API 的表格溯源响应输出
数据溯源 API 非常适合追踪 Unity Catalog 中数据集之间的连接。然而,我们还可以检索有关我们表的 列 的更精细的溯源信息。在下一个示例中,让我们检索有关我们表的 description 列的信息。我们还将定义另一个辅助函数,以便更好地显示列连接信息:
def print_column_info(conn_type, column_info):
print(f"""
Connection flow: {conn_type.upper()}
Column name: {column_info['name']}
Catalog name: {column_info['catalog_name']}
Schema name: {column_info['schema_name']}
Table name: {column_info['table_name']}
Table type: {column_info['table_type']}
Lineage timestamp: {column_info['lineage_timestamp']}
""")
column_name = "description"
response = requests.get(
f"https://{WORKSPACE_NAME}.cloud.databricks.com/api/2.0/lineage-tracking/column-lineage",
headers={
"Authorization": f"Bearer {API_TOKEN}"
},
json={
"table_name": FULLY_QUALIFIED_TABLE_NAME,
"column_name": column_name
}
)
if response.status_code == 200:
if "upstream_cols" in response.json():
print("| Upstream cols:")
for column_info in response.json()['upstream_cols']:
print_column_info("Upstream", column_info)
if "downstream_cols" in response.json():
print("| Downstream cols:")
for column_info in response.json()['downstream_cols']:
print_column_info("Downstream", column_info)
在这种情况下,我们表中的 description 列尤其引人注目,因为它是将两个不同列的文本字符串连接起来的结果。如果你使用不同的列名更新之前的列溯源请求,你会注意到上游源的数量会发生变化,以反映该列特有的连接数量。
图 7.3 – 来自 Databricks Lineage API 的列溯源响应输出
到目前为止,你应该已经能够熟练地使用 Databricks 数据血缘 API 来追踪数据集之间的连接,甚至是更精细的数据转换,如列连接。正如你所见,Data Lineage API 的请求和响应需要有处理 JSON 数据的经验。对于一些响应,我们需要创建辅助函数,将响应解析为更易读的格式。
在下一节中,我们将探讨如何使用 Databricks 用户界面来追踪数据集之间的关系,使得非技术数据管理员也能通过点击按钮轻松查看上下游数据源。
可视化上下游转换
在本节中,我们将利用数据集生成器笔记本,在 Unity Catalog 中创建多个数据集,以便通过 Databricks 用户界面追踪数据集血缘。如果你还没有这样做,请克隆本章附带的 GitHub 仓库,仓库地址是 github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter07。接下来,启动一个现有的通用集群,或创建一个新的集群,并开始将数据生成器笔记本附加到该集群。在 Databricks 工作区的右上角点击 Run all 按钮,执行所有笔记本单元格,验证所有单元格都成功执行。如果遇到运行时错误,请验证你是否拥有正确的元存储权限,以在 Unity Catalog 元存储中创建新的目录、架构和表格。
重要提示
你需要获得创建新的目录和架构的权限,才能在你的 Unity Catalog 元存储中进行操作。如果无法获得此权限,可以重新使用现有的目录和架构来生成示例表格。你需要相应地更新 DDL 和 DML 语句,以匹配你自己 Databricks 工作区中的值。
数据生成器笔记本的结果应包含三张表格:youtube_channels、youtube_channel_artists 和 combined_table。在 Databricks 数据智能平台中,数据血缘可以通过多种方式轻松追踪。在这个示例中,让我们使用 Databricks 用户界面来追踪一个数据资产——combined_table 表的血缘。从你的 Databricks 工作区中,点击左侧导航菜单中的 Catalog Explorer 菜单选项。接下来,可以深入目录和架构以找到 combined_table 表,或者直接在 Catalog Explorer 顶部的搜索框中输入 combined_table,该框会过滤出匹配文本的所有数据资产。点击 combined_table 表,这将会在用户界面的右侧面板中打开数据资产的 概览 详情。
图 7.4 – 数据血缘可以直接从 Databricks 的 Catalog Explorer 中追溯
从 UI 面板中,点击血缘标签以显示表的血缘详情。导航到血缘标签后,你应该能看到与combined_table数据集相关的所有连接的摘要,清晰地标识出构建此表所使用的所有上游源,以及任何利用此表的下游依赖项。
图 7.5 – Catalog Explorer 中的血缘标签包含关于表的血缘信息
在这种情况下,应该有两行包含有关上游源的信息——youtube_channels父表和youtube_channel_artists表。由于我们刚刚使用数据生成器笔记本创建了这个表,所以不应该有任何带有下游依赖关系的行。正如你可以想象的,这个表将实时更新,列出所有以某种方式使用该数据集的对象,明确标识出数据的任何下游依赖项。
最后,让我们可视化我们的表格血缘关系。点击标有查看血缘图的蓝色按钮,打开血缘可视化。
现在你应该清楚地看到,两个上游表连接形成了combined_table表。
图 7.6 – 可以通过点击血缘图中的连接链接生成血缘连接信息
接下来,点击连接上游表和下游表combined_table的箭头,揭示有关血缘连接的更多详细信息。你会注意到,侧边面板会打开,显示关于血缘连接的信息,如源表和目标表,但它还会显示这些数据资产如何在 Databricks 数据智能平台的各种其他对象中被使用。例如,UI 面板将列出这些数据集当前是如何在笔记本、工作流、DLT 管道和 DBSQL 查询中被利用的。在这种情况下,我们只是通过数据生成器笔记本生成了这些表,所以它是血缘连接信息中唯一列出的对象。
图 7.7 – 可以从血缘图中查看数据集之间的连接详情
列的血缘关系也可以通过 Catalog Explorer 来追踪。在相同的血缘图中,点击 combined_table 表中的不同列以显示血缘信息。例如,通过点击 description 表列,血缘图将更新并清晰地展示 description 列的计算方式。在这个例子中,列是通过将一串文本与父表的类别列以及子表中的艺术家名称连接起来计算得出的。
图 7.8 – 可以通过点击列来追踪血缘关系,揭示上游血缘连接
如您所见,从 Catalog Explorer 生成血缘图提供了 Unity Catalog 中数据集之间最新关系的准确快照。这些关系可以帮助我们识别数据变更对下游依赖关系的影响,例如更改列的数据类型或删除数据集等。
在下一部分,我们将了解数据血缘如何帮助我们识别数据集之间的关系,发现利用这些数据集的依赖笔记本,并避免在整个组织中引入破坏性更改。
确定依赖关系和影响
在这一部分,我们将再次利用 Catalog Explorer 中的血缘图 UI,深入了解更改某一列的数据类型和数值将如何影响下游数据集和下游流程(如笔记本和工作流),并在我们的 Databricks 工作区中查看这些影响。
首先,在我们的 Databricks 工作区创建一个新的笔记本,其中包含一个新的 DLT 流水线的定义。我们 DLT 流水线中的第一个数据集将导入存储在默认Databricks 文件系统(DBFS)中的商业航空公司航班信息原始 CSV 文件,这些文件位于**/databricks-datasets**目录下。每个 Databricks 工作区都可以访问这个数据集。创建一个新的笔记本单元,并添加以下代码片段,用于在我们的数据流水线中定义一个 bronze 表:
import dlt
@dlt.table(
name="commercial_airliner_flights_bronze",
comment="The commercial airliner flight data dataset located in `/databricks-datasets/`"
)
def commercial_airliner_flights_bronze():
path = "/databricks-datasets/airlines/"
return (spark.readStream
.format("csv")
.schema(schema)
.option("header", True)
.load(path))
我们希望通过商业喷气式飞机的信息来增强航班数据。创建一个新的笔记本单元,并添加以下代码片段,定义一个静态参考表,包含有关流行商业航空公司喷气式飞机的信息,包括制造商名称、飞机型号、原产国和燃油容量等:
commercial_airliners = [
("Airbus A220", "Canada", 2, 2013, 2016, 287, 287, 5790),
("Airbus A330neo", "Multinational", 2, 2017, 2018, 123,
123, 36744 ),
("Airbus A350 XWB", "Multinational", 2, 2013, 2014, 557,
556, 44000),
("Antonov An-148/An-158", "Ukraine", 2, 2004, 2009, 37,
8, 98567 ),
("Boeing 737", "United States", 2, 1967, 1968, 11513, 7649,
6875),
("Boeing 767", "United States", 2, 1981, 1982, 1283, 764,
23980),
("Boeing 777", "United States", 2, 1994, 1995, 1713, 1483,
47890),
("Boeing 787 Dreamliner", "United States", 2, 2009, 2011,
1072, 1069, 33340),
("Embraer E-Jet family", "Brazil", 2, 2002, 2004, 1671,
1443, 3071),
("Embraer E-Jet E2 family", "Brazil", 2, 2016, 2018, 81,
23, 3071)
]
commercial_airliners_schema = "jet_model string, Country_of_Origin string, Engines int, First_Flight int, Airline_Service_Entry int, Number_Built int, Currently_In_Service int, Fuel_Capacity int"
airliners_df = spark.createDataFrame(
data=commercial_airpliners,
schema=commercial_airliners_schema
)
接下来,我们将把航空公司喷气式飞机参考表保存到之前在 Unity Catalog 中创建的模式中:
airliners_table_name = f"{catalog_name}.{schema_name}.{table_name}"
(airliners_df.write
.format("delta")
.mode("overwrite")
.option("mergeSchema", True)
.saveAsTable(airliners_table_name))
让我们向数据流水线添加另一个步骤,这将把我们的静态商业喷气机航空公司参考表与我们的航空航班数据流连接起来。在新的笔记本单元中,创建以下用户定义函数(UDF),它将为商业航空数据集中的每个条目生成一个尾号:
from pyspark.sql.types import StringType
from pyspark.sql.functions import udf
@udf(returnType=StringType())
def generate_jet_model():
import random
commercial_jets = [
"Airbus A220",
"Airbus A320",
"Airbus A330",
"Airbus A330neo",
"Airbus A350 XWB",
"Antonov An-148/An-158",
"Boeing 737",
"Boeing 767",
"Boeing 777",
"Boeing 787 Dreamliner",
"Comac ARJ21 Xiangfeng",
"Comac C919",
"Embraer E-Jet family",
"Embraer E-Jet E2 family",
"Ilyushin Il-96",
"Sukhoi Superjet SSJ100",
"Tupolev Tu-204/Tu-214"
]
random_index = random.randint(0, 16)
return commercial_jets[random_index]
最后,创建一个新的笔记本单元,并添加以下 DLT 数据集定义,用于我们的银表:
@dlt.table(
name="commercial_airliner_flights_silver",
comment="The commercial airliner flight data augmented with randomly generated jet model and used fuel amount."
)
def commercial_airliner_flights_silver():
return (dlt.read_stream(
"commercial_airliner_flights_bronze")
.withColumn("jet_model", generate_jet_model())
.join(spark.table(airliners_table_name),
["jet_model"], "left"))
当出现提示时,点击笔记本单元输出底部的蓝色按钮 创建管道 来创建一个新的 DLT 管道。为管道命名一个有意义的名字,例如 商业航班管道。选择 触发式 作为执行模式,并选择 Core 作为产品版本。接下来,选择之前代码示例中的目标目录和模式,作为我们 DLT 管道的目标数据集位置。最后,点击 开始 按钮以触发管道更新。
图 7.9 – 用于获取商业航班数据的 DLT 管道
假设有一个外部过程,旨在计算每个商业航班的碳足迹。在这个例子中,该过程是另一个 Databricks 笔记本,它读取我们银表的输出,并计算美国境内每个航班的二氧化碳排放量。
在您的 Databricks 工作区内创建另一个笔记本,并为笔记本起一个有意义的名字,比如 计算商业航班碳足迹。接下来,添加一个新的笔记本单元,读取银表并使用简单公式计算二氧化碳排放量:
碳足迹 = 燃烧的燃料量 * 系数 / 乘客人数
在这种情况下,我们只关心计算每架航班的碳足迹;因此,我们将避免除以乘客人数。将以下代码片段添加到新创建的笔记本中,该代码将为每个航班条目分配计算出的碳足迹:
# 3.1kg of CO2 is created for every 1kg of fuel used.
# So we multiply the fuel mass above by 3.1 to estimate the CO2 emitted
# Source: https://ecotree.green/en/calculate-flight-co2
# 1 gallon of jet fuel weighs approximately 3.03907 kilograms
def calc_carbon_footprint(fuel_consumed_gallons):
return (fuel_consumed_gallons * 3.03907) * 3.1
让我们再次假设我们 DLT 管道中的银表的燃料容量目前是以加仑为单位。然而,我们的欧洲业务合作伙伴希望改用升作为数据集的单位。让我们使用目录浏览器来查看银表的血缘关系图,以更好地理解将 fuel_capacity 列的度量单位转换为升会对数据集的使用者产生什么影响。通过点击左侧导航栏中的目录浏览器,按目录名称在搜索框中过滤,然后点击银表 commercial_airliner_flights_silver 来进入血缘关系图。
图 7.10 – 列的血缘关系有助于我们理解更改列将如何影响下游依赖关系 – 概览
通过生成血缘图,我们能够实时查看所有可能依赖此列的下游列。此外,我们还可以看到所有依赖此列的 Unity Catalog 对象的实时列表,例如笔记本、工作流、DLT 管道和机器学习模型。因此,实际上,我们可以快速了解更改计量单位可能对共享该数据集的组织产生的影响。
在下一节中,我们将继续这个示例,找出更新该数据集的另一种方法,以包含燃料容量、行驶距离和到达时间,使其适应欧洲标准,而不会影响任何现有的数据消费者。
实操实验 – 跨组织文档化数据血缘
在本节中,我们将查看 Databricks 中的系统表是如何自动记录数据集和其他数据资产之间关系随时间变化的情况。正如之前提到的,Unity Catalog 会在所有连接到同一 Unity Catalog 元存储的工作空间中保留数据血缘。这在组织需要对其数据资产进行强有力的端到端审计时尤其有用。
我们再次从在 Databricks 工作空间中创建一个新的笔记本开始,并为其设置一个有意义的标题,例如查看文档化的数据血缘。接下来,创建一个新的通用集群,或者将笔记本附加到一个已经运行的集群上,以开始执行笔记本单元格。
与数据血缘 API 类似,Unity Catalog 中有两个系统表提供血缘信息的只读视图——system.access.table_lineage 表和 system.access.column_lineage 表。数据血缘系统表会自动记录与 UC 表和列对象的上游和下游连接相关的信息。
| UC 对象 | 表名 | 描述 |
|---|---|---|
| 表格 | system.access.table_lineage | 包含上游和下游表连接的列表,以及与其相关的笔记本连接信息 |
| 列 | system.access.column_lineage | 包含上游和下游列连接的列表 |
表 7.2 – 数据血缘系统表捕获有关表和列的连接信息
让我们查询前面示例中的上游和下游血缘信息。在一个新的笔记本单元格中,添加以下查询并执行单元格:
SELECT *
FROM system.access.table_lineage
WHERE source_table_name LIKE '%commercial_airliners_silver%';
我们得到如下输出:
图 7.11 – 可以从系统表中查询血缘信息
从输出结果可以看出,系统表自动记录了上下游数据源的连接信息。此外,系统表还会自动捕获审计信息,包括数据集创建者的信息以及对象创建事件的时间戳。这是记录、审查甚至报告组织数据集血统的绝佳方式。
总结
在本章中,我们介绍了在 Databricks 数据智能平台中追踪数据血统的各种方法。我们看到,Data Lineage REST API 使我们能够快速查看 Unity Catalog 中特定表格或列的上下游连接。接下来,我们展示了使用 Unity Catalog 中的 Catalog Explorer 生成血统图的简便方法。血统图对于深入了解数据集的变化如何影响下游数据消费者至关重要。最后,我们介绍了如何通过 Unity Catalog 中的系统表来记录数据资产关系的演变。
在下一章中,我们将重点介绍如何使用工具(如 Terraform)自动化部署数据管道及其所有依赖项。
第三部分:持续集成、持续部署与持续监控
在本书的最后部分,我们将探讨如何使用流行的自动化工具,如 Terraform 和 Databricks Asset Bundles(DABs),自动化管道变更的部署。我们将以如何使用 Databricks 数据智能平台中的各种工具持续监控 DLT 管道的教程作为本书的总结。
本部分包含以下章节:
-
第八章,使用 Terraform 部署、维护和管理 DLT 管道
-
第九章,利用 Databricks Asset Bundles 简化数据管道的部署
-
第十章,生产环境中的数据管道监控
第八章:使用 Terraform 部署、维护和管理 DLT 数据管道
在本章中,我们将探讨如何使用像 Terraform 这样的自动化工具将数据管道以代码的形式表达,这通常被称为基础设施即代码(IAC),并应用于 Databricks。我们将学习如何使用流行的代码编辑器,如 VS Code,设置本地 Terraform 开发环境,以便我们可以尝试将不同的资源部署到 Databricks 工作区。接下来,我们将深入探讨如何使用 Terraform 表示数据管道,并如何配置 Delta Live Tables(DLT)管道的不同方面。我们还将学习如何自动化 IaC 的验证和部署到不同的 Databricks 工作区,包括生产工作区。最后,我们将讨论行业最佳实践和未来的考虑事项。
在本章中,我们将涵盖以下主要内容:
-
介绍 Terraform 的 Databricks 提供程序
-
设置本地环境
-
使用 Terraform 配置 DLT 数据管道
-
自动化 DLT 数据管道部署
-
实践操作 - 使用 VS Code 部署 DLT 数据管道
技术要求
为了跟随本章提供的示例,你需要拥有 Databricks 工作区权限,以创建并启动通用集群,这样你才能导入并执行本章附带的笔记本。所有的代码示例可以从本章的 GitHub 仓库下载,地址为:github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter08。本章将创建并运行几个新的笔记本,还将使用产品的高级版运行一个新的 DLT 数据管道,预计会消耗大约 10—15 Databricks Units(DBUs)。
介绍 Terraform 的 Databricks 提供程序
Terraform 是一个开源部署自动化工具,可以用于以可重复和可预测的方式自动化部署云基础设施。Terraform 成为如此受欢迎的部署工具的原因之一是,它支持将基础设施部署到三个主要云提供商:Amazon Web Services (AWS)、Azure 和 Google Cloud Platform (GCP) 。Terraform 的核心概念是定义基础设施即代码(IaC),即通过代码文件表达云组件(如网络对象、虚拟机或存储容器)的配置,而不是手动部署这些组件。此外,Terraform 文件是配置驱动的。云管理员专注于通过配置表达环境之间的变化,而不是表达如何部署基础设施。最后,Terraform 维护着你的架构状态,意味着该工具将跟踪云资源的状态,并在每次执行 Terraform 配置文件时相应地更新状态。
最后,Terraform 文件可以直接从本地机器执行,允许你远程与云资源进行交互。
图 8.1 – Terraform 将通过配置文件反映环境变化
Terraform 配置文件定义了在云提供商中应用的云基础设施,基础设施状态会同步回本地环境。此外,Databricks 提供了一个 Terraform 提供者,用于将 Databricks 工作区和工作区对象部署到主要云提供商中。
Terraform 提供者是 Terraform 工具的插件,使用户能够与特定的 API 进行交互。在这个案例中,Terraform 提供者与 Databricks REST API 交互,允许工作区管理员自动化部署即便是最复杂的数据处理环境。
使用 Terraform 自动化部署组织内的数据管道有许多优势,包括以下几点:
-
在主要云提供商之间部署基础设施非常容易,使得如果需要,迁移云平台变得轻而易举。
-
通过专注于定义配置而不是手动部署和维护数据管道,轻松实现数百个数据管道的扩展。
-
管道定义简洁,允许云管理员专注于表达需要更改的内容,而不是如何部署基础设施。
让我们来看看如何轻松开始定义 Databricks 资源并将其应用于目标工作区。
设置本地 Terraform 环境
在开始将数据管道对象部署到 Databricks 工作区之前,我们需要安装 Terraform 命令行界面(CLI)工具。如果你还没有安装,你需要下载 Terraform CLI,可以从 HashiCorp 网站免费获取(developer.hashicorp.com/terraform/install)。
接下来,我们需要将 Terraform 配置文件组织到一个目录中。我们可以创建一个新的目录,命名为 chp8_databricks_terraform。
在新创建的目录中,我们将创建一个全新的 Terraform 配置文件,在该文件中定义我们的数据管道和其他相关的工作区对象。创建一个新文件,命名为 main.tf。
重要提示
Terraform 配置文件使用 Terraform 语言,并以 . tf 扩展名结尾。
导入 Databricks Terraform 提供程序
使用 Terraform 部署 Databricks 工作区对象的第一步是导入 Databricks Terraform 提供程序。如果你第一次使用 Terraform 提供程序,Terraform 将自动从 Terraform 注册表下载 Databricks 提供程序。Terraform 注册表是一个公共中心,用于下载第三方提供程序、模块和安全策略,帮助开发 Terraform 配置文件以部署云基础设施。
将以下代码片段添加到新创建的 Terraform 配置文件 main.tf 的顶部:
terraform {
required_providers {
databricks = {
source = "databricks/databricks"
}
}
}
这段代码将指示 Terraform CLI 工具下载并导入一个名为 databricks 的 Terraform 提供程序,该提供程序已经由 Databricks 组织发布到 Terraform 注册表中。
现在我们已经导入了 Databricks Terraform 提供程序,可以开始将数据管道对象部署到 Databricks 工作区。但在此之前,我们必须先与 Databricks 工作区进行身份验证,以便进行更改,例如创建新的 DLT 管道。
配置工作区身份验证
如果你还记得,Databricks Terraform 提供程序将在后台与 Databricks REST API 交互。因此,用于与 Databricks REST API 进行身份验证并进行工作区更改的相同身份验证机制也可以通过 Terraform 使用。
总的来说,使用 Terraform 提供程序进行 Databricks 工作区身份验证的支持方法大约有九种(最新的列表可以在此找到:registry.terraform.io/providers/databricks/databricks/latest/docs#authentication)。其中一些常见的身份验证方法包括:
-
使用工作区管理员用户名和密码
-
使用 Databricks 个人访问 令牌(PAT)
-
使用 Azure CLI 或 Google Cloud CLI
-
如果使用 Azure 云提供商,需使用服务主体或托管服务标识
-
使用 Databricks CLI(用户对机器身份验证)
重要提示
由于我们正在进行本地开发和测试,在以下示例中,我们将使用 Databricks CLI 生成一个 OAuth 令牌,并手动登录到我们的 Databricks 工作区。然而,对于生产部署,建议将工作区凭据安全地存储在像 Azure Key Vault、AWS Secrets Manager 或 HashiCorp Vault 等秘密管理器中。
有几种选项可以存储与 Terraform 一起使用的认证令牌——直接存储在 Terraform 配置文件中,作为 Databricks 提供程序导入的一部分,或者存储在本地机器中的配置文件中。我们建议选择后者,以避免在将代码文件提交到代码库时意外暴露凭证。填充此配置文件的最简单方法是使用 Databricks CLI。
Databricks CLI 支持 Windows、Linux 或 macOS 操作系统,使其成为跨平台兼容的多功能工具。如果你的本地机器使用的是 macOS 或 Linux 操作系统,你可以通过命令行提示符使用 Homebrew 包管理器下载 Databricks CLI。或者,你也可以轻松地升级现有 Databricks CLI 安装的版本。例如,以下命令将在 Mac 上使用 Homebrew 安装或升级现有的 Databricks CLI 安装:
$ brew tap databricks/tap
$ brew install databricks
在 Windows 机器上,你可以使用流行的包管理器winget(learn.microsoft.com/windows/package-manager/winget/)安装 Databricks CLI。以下命令将使用 winget 工具下载并安装 Databricks CLI:
$ winget search databricks
$ winget install Databricks.DatabricksCLI
下载完成后,你可以通过执行 configure 命令在 Databricks CLI 中配置认证:
$ databricks configure
当将 Terraform 配置文件应用于目标环境时,Terraform CLI 会首先检查是否在配置文件中直接提供了认证信息。否则,Terraform CLI 会查找本地的 Databricks 配置文件,该文件存储在名为 .databrickscfg 的特殊隐藏文件中,位于用户的主文件夹下。
你还可以指定一个配置文件名称,这在你有多个 Databricks 工作区,并且需要在不同工作区之间部署基础设施组件时非常有用。使用配置文件,你可以单独存储认证信息,并在部署期间轻松引用它们。你可以在此了解更多关于创建/测试配置文件的信息:docs.databricks.com/dev-tools/cli/profiles.html。
定义 DLT 管道源笔记本
在下一个示例中,我们将定义一个笔记本,包含一个简单 DLT 管道的开始部分,并将该笔记本部署到目标 Databricks 工作区中的用户工作区目录。
要构建部署笔记本的工作区位置,我们需要获取你当前在 Databricks 中的用户。为了避免硬编码此值,我们可以使用 databricks_current_user 数据源,它在部署时获取当前用户的 Databricks 用户名。将以下配置块添加到 main.tf 文件中:
data "databricks_current_user" "my_user" {}
接下来,我们将使用 databricks_notebook 资源定义一个新的 Python 笔记本,使用之前的数据源构造笔记本路径。由于笔记本相当简单,仅包含一个 DLT 数据集定义,我们将在其中内联定义笔记本内容。将以下配置块添加到 main.tf 文件中:
resource "databricks_notebook" "dlt_pipeline_notebook" {
path = "${data.databricks_current_user.my_user.home}/chp_8_terraform/my_simple_dlt_pipeline.py"
language = "PYTHON"
content_base64 = base64encode(<<-EOT
import dlt
@dlt.table(
comment="The raw NYC taxi cab trip dataset located in `/databricks-datasets/`"
)
def yellow_taxi_raw():
path = "/databricks-datasets/nyctaxi/tripdata/yellow"
schema = "vendor_id string, pickup_datetime timestamp, dropoff_datetime timestamp, passenger_count integer, trip_distance float, pickup_longitude float, pickup_latitude float, rate_code integer, store_and_fwd_flag integer, dropoff_longitude float, dropoff_lattitude float, payment_type string, fare_amount float, surcharge float, mta_tax float, tip_amount float, tolls_amount float, total_amount float"
return (spark.readStream
.schema(schema)
.format("csv")
.option("header", True)
.load(path))
EOT
)
}
最后,让我们向 main.tf 配置文件添加最后一个块,打印已部署笔记本的 URL:
output "notebook_url" {
value = databricks_notebook.dlt_pipeline_notebook.url
}
点击 Save 保存配置文件。在终端窗口中,导航到包含 main.tf 配置文件的目录。
应用工作区更改
应该运行的第一个命令是 terraform init 命令,该命令执行多个初始化步骤,以准备当前工作目录以便使用 Terraform 部署云资源。从终端窗口或 shell 提示符执行以下命令:
terraform init
接下来,Terraform CLI 提供了一种方法,让我们在应用更改之前验证 Terraform 配置文件的效果。执行 validate 命令:
terraform validate
最后,我们可以通过列出 Terraform 计划中的所有预定更改来查看拟议的基础设施更改。执行以下命令查看拟议的 Terraform 计划:
terraform plan
你会注意到计划中将定义一个单一的资源。在这种情况下,它将是包含我们 DLT 数据集定义的新 Databricks 笔记本。
一旦我们验证计划看起来没有问题,就可以将更改应用到目标 Databricks 工作区。通过执行 apply 命令应用 Terraform 计划:
terraform apply
输出将是新创建笔记本的完整 URL。复制输出的 URL 并粘贴到浏览器窗口中。验证是否有一个新的笔记本,Python 被设置为默认编程语言,且包含一个包含单一 DLT 数据集定义的笔记本单元,yellow_taxi_raw。
恭喜!你已经编写了第一个 Terraform 配置文件,并且在自动化部署 Databricks 资产到各个环境的道路上迈出了坚实的步伐。在下一节中,我们将基于前面的示例展开,看看 Databricks Terraform 提供者如何用于将 DLT 管道部署到工作区。
使用 Terraform 配置 DLT 管道
我们将使用 Databricks Terraform 提供程序中的 databricks_pipeline 资源,将 DLT 管道部署到目标 Databricks 工作区。databricks_pipeline 资源是我们 Terraform 配置文件的主要构建块。在此 Terraform 资源中,我们可以指定许多不同的配置选项,这些选项将影响 DLT 管道的部署。例如,我们可以配置 DLT 生产版、目标 Unity Catalog 位置、库依赖项、更新集群大小等。让我们深入探讨这些具体配置,以便更好地了解您对部署的 DLT 管道的控制。
有几个参数用于定义使用 Databricks 提供程序进行 Terraform 配置的 DLT 管道的配置和行为。为了更好地了解这些参数,以下部分涵盖了 Databricks 提供程序中 Terraform 的所有可用参数(最新版本可以在此找到:registry.terraform.io/providers/databricks/databricks/latest/docs/resources/pipeline)。
一般来说,databricks_pipeline 参数可以被看作是属于以下三类之一:
-
运行时配置:名称,频道,开发,持续,版本,光子,配置,和 库
-
管道计算 配置:集群
-
管道数据集存储配置:目录,目标,和 存储
让我们更详细地了解每个参数,以更好地理解我们的 Terraform 配置对目标 DLT 管道的影响。
name
name 参数为 DLT 管道分配一个字母数字名称,以便标识该管道。name 参数应为一个字符串,可以包含大小写字母、数字、空格和特殊字符(包括表情符号字符)。此外,管道 name 参数不一定需要唯一;Databricks Terraform 提供程序并不强制名称唯一。在创建 DLT 管道时,Databricks 数据智能平台会为每个管道分配一个唯一的管道标识符,因此 name 参数仅作为数据工程师区分其 DLT 管道与其他管道的一种方便方式。
notification
notification 参数用于指定在特定管道事件期间将收到电子邮件通知的收件人列表。触发通知的 DLT 管道事件类型包括 on-update-success,on-update-failure,on-update-fatal-failure 和 on-flow-failure。
channel
通道参数控制 DLT 管道更新集群应使用的 Databricks 运行时类型。只有两个选项可供选择:CURRENT和PREVIEW。CURRENT选择最新的稳定 Databricks 运行时版本,并且是默认选项。如果您的 DLT 管道在开发环境中运行,并且您想尝试尚未进入当前 Databricks 运行时的未来性能功能和优化,您可能希望选择PREVIEW。
development
开发参数是一个布尔标志,用于控制是否希望以开发模式执行 DLT 管道。当设置为true时,Terraform 将部署一个 DLT 管道,并将管道模式设置为开发模式。这也会通过 DLT UI 右上角的切换按钮在 DLT UI 中显示出来。
图 8.2 – 在 DLT UI 中,开发模式可以通过切换按钮显示
同样,当该参数设置为false时,Terraform 将把管道模式设置为生产模式。如果您还记得第二章,我们提到过在开发模式下,DLT 在运行时异常发生时不会重试管道更新,并且还会保持更新集群的运行状态,以帮助数据工程师排查和修复错误,从而缩短调试周期。
continuous
持续参数是一个布尔标志,用于控制管道更新执行的频率。当设置为true时,Terraform 将部署一个 DLT 管道,持续更新管道中的数据集。同样,当设置为false时,DLT 管道将以触发执行模式进行部署。在这种执行模式下,数据工程师需要通过点击 DLT UI 上的开始按钮或通过调用 Pipelines REST API 来触发管道更新的开始。
edition
版本参数选择您在部署 DLT 管道时希望使用的产品版本。可以选择的选项只有三个:CORE,PRO,和ADVANCED。如果您还记得第二章,产品版本选择了您在运行 DLT 管道时希望启用的功能集。因此,管道定价反映了通过版本启用的功能数量。例如,PRO产品版本将允许数据工程师使用期望来强制执行数据质量,但也会产生最高的操作费用。另一方面,CORE产品版本可用于将传入数据追加到流表中,并将产生最少的操作费用来进行更新。
photon
Photon参数是一个布尔标志,控制是否使用 Photon 处理引擎来更新 DLT 管道。当设置为true时,Terraform 将部署一个启用 Photon 引擎的更新集群。在数据集更新过程中,您的 DLT 管道可以利用这个快速的向量化处理引擎,使连接、聚合、窗口和排序的速度比默认集群快。设置为false时,DLT 将创建一个使用传统 Catalyst 引擎的更新集群。由于更快的处理速度和更好的性能,启用 Photon 执行将导致更高的 DBU 定价。
配置
配置参数允许数据工程师部署一个具有可选运行时配置的 DLT 管道。配置参数是一个可选的键值对列表。例如,可以使用该参数来填充环境变量、云存储位置或集群关闭设置等。
库
库参数可用于安装 DLT 管道更新可能依赖的集群库,以便将更新应用于管道。库参数还支持引用本地笔记本或任意文件依赖项,如果数据工程师希望使用本地文件而不是构建工件来包含依赖的代码文件。例如,以下库块可以用于包含一个在用户工作区的主目录中定义的自定义日期实用程序,作为 Python 文件:
library {
notebook {
path = "/Users/<username>/common/utils/date_utils.py"
}
}
集群
集群参数控制在更新、维护活动期间或在更新和维护任务中使用的默认集群类型。如果没有指定集群块,DLT 将创建一个默认集群,用于应用更新到管道的数据集。此外,集群参数还将包含一个模式参数,您可以在其中指定使用哪种类型的自动扩展。如果您还记得,在第二章中,我们描述了 DLT 中的两种自动扩展模式:传统模式和增强模式。例如,以下配置将创建一个更新集群,使用增强模式自动扩展,从最小的三台工作节点扩展到最多八个节点:
cluster {
node_type_id = "i3.xlarge"
autoscale {
min_workers = 3
max_workers = 8
mode = "ENHANCED"
}
}
目录
目录参数确定用于在 Unity Catalog 中存储 DLT 管道输出数据集的目录。随着 DLT 管道执行数据集的定义,这些数据集需要指定一些目标位置。您可以指定目录和模式的组合(在下节目标中介绍),或者可以指定一个云存储位置——但不能同时指定两者。此参数与下一个参数存储参数是互斥的。或者,数据工程师可以继续将 DLT 管道的数据集存储在传统的 Hive 元数据存储中,指定以下配置:
catalog {
name = "hive_metastore"
}
重要提示
如果在 Terraform 配置文件中更改了 catalog 或 storage 参数,并且应用了这些更改,Terraform 将会重新创建整个 DLT 流水线并应用新的更改。这些值在原始 DLT 流水线部署后无法更新。
target
target 参数指定了在 DLT 流水线中存储输出数据集的架构。这个参数与前面的 catalog 参数一起,指定了在 Unity Catalog 或传统 Hive Metastore 中的完全限定架构。数据工程师可以选择使用 catalog 和 target 参数中设置的值,作为查询 DLT 流水线中间数据集的便捷方式。这可以用于常见的任务,例如数据验证、调试或一般的数据整理。
storage
storage 参数可用于指定一个云存储位置,用于存储 DLT 流水线的输出数据集和其他相关元数据。请注意,这个参数与前面的 catalog 参数互斥。storage 参数可以包含完全限定的存储位置路径、卷位置,或 Databricks 文件系统(DBFS)中的位置。例如,以下配置块将创建一个 DLT 流水线,其输出数据集将存储在 DBFS 中:
storage {
path = "/pipelines/my-dlt-pipeline-output/"
}
重要提示
storage 和 catalog 参数是互斥的。在编写 Terraform 配置文件时,您只能指定其中一个。
到目前为止,您应该已经能够自信地使用 databricks_pipeline 资源,通过 Terraform 的 Databricks 提供程序声明 DLT 流水线。您还应该对可用的不同类型的配置选项有更深入的理解,以便自定义目标 DLT 流水线。在接下来的部分中,我们将探讨如何使用现有的版本控制系统自动化 DLT 流水线的部署,以便最新的更改能够在发布后尽快在目标工作区中同步。
自动化 DLT 流水线部署
Terraform 可以与自动化的 持续集成/持续部署(CI/CD)工具结合使用,例如 GitHub Actions 或 Azure DevOps Pipelines,自动将代码更改部署到您的 Databricks 工作区。由于 Terraform 是跨平台的,目标 Databricks 工作区可以位于主要的云服务提供商之一:GCP、AWS 或 Azure。这使得您的开发团队能够在一组代码工件中维护基础设施,同时又能灵活地将相同的资源应用于其他云提供商。
让我们了解一个典型的 CI/CD 流程,该流程使用 Databricks 提供的 Terraform 提供程序将 Databricks 资源部署到目标工作区。CI/CD 流程将包含两个自动化构建流水线——一个用于验证在功能分支中所做的更改,另一个用于将已批准并合并到主代码分支的更改部署到 Databricks 工作区。
首先,一名团队成员创建一个新的功能分支,用于跟踪他们组织的基础设施即代码(IaC)代码库的更改。完成后,工程师将打开一个拉取请求,要求一个或多个团队成员审查更改,留下反馈,并批准或拒绝更改。打开拉取请求后,构建流水线将被触发运行,流水线将检出功能分支,使用 Terraform init 命令初始化当前工作目录,使用 Terraform validate 命令验证 Terraform 计划,并生成一个 Terraform 计划的输出。可选地,这个 Terraform 计划可以作为评论自动包含在拉取请求中,供团队成员审查。
当拉取请求获得团队成员的批准后,功能分支可以合并到主代码库分支——简称为主分支。
一旦功能分支合并到主分支,构建发布流水线就会被触发运行。构建发布流水线将检出最新的主分支副本,并使用 Terraform apply 命令应用更改。在应用 Terraform 计划后,组织基础设施的更新将反映在目标 Databricks 工作区中。
图 8.3 – 使用构建工具自动部署 Databricks 资源
到目前为止,你应该完全理解如何使用像 Azure DevOps 这样的工具设计自动化的 Databricks 部署,借助 Terraform 同步基础设施的更改。让我们将前面各节中学到的内容结合起来,使用典型的开发环境将我们自己的 DLT 流水线部署到目标 Databricks 工作区。
实践练习 – 使用 VS Code 部署 DLT 流水线
在这个动手实践中,我们将使用流行的代码编辑器 Visual Studio Code (VS Code) 来编写新的 Terraform 配置文件,部署 DLT 流水线到目标 Databricks 工作区。多年来,VS Code 因其易用性、轻便的内存占用、友好的代码导航、语法高亮和代码重构功能,以及丰富的扩展社区而获得了极大的欢迎。此外,VS Code 是围绕开源社区构建的,意味着它可以免费下载安装并使用。更重要的是,VS Code 是一个跨平台的代码编辑器,支持 Windows、macOS 和 Linux 操作系统。在这个动手实践中,我们将使用一个社区扩展——HashiCorp 编写的 Terraform 插件,用于帮助开发 Terraform 配置文件。例如,VS Code 的 Terraform 插件提供了 Terraform 语法高亮、自动补全、代码格式化、通过 VS Code 命令面板访问 Terraform 命令,并且总的来说,提供了一个轻松的体验,帮助我们导航 Terraform 配置文件,以便部署 Databricks 工作区对象。
设置 VS Code
VS Code 可以从其官方网站下载,网址为 code.visualstudio.com/download。如果你还没有安装 VS Code,选择适合本地操作系统的安装程序下载。下载过程可能需要几分钟,具体取决于你的网络连接速度。下载完成后,解压 ZIP 文件以查看下载的内容。接下来,双击应用程序文件,Visual Studio Code,启动代码编辑器。或者,你也可以将应用程序文件移动到本地操作系统的 Applications 目录中。
接下来,让我们安装 HashiCorp 提供的 Terraform 扩展。在一个网页浏览器窗口中,访问 Visual Studio Marketplace 上的 Terraform 扩展,网址为 marketplace.visualstudio.com/items?itemName=HashiCorp.terraform。或者,你可以在 VS Code 中的 Marketplace 搜索框中搜索该扩展。点击 安装 按钮,下载并安装适用于 VS Code 的 Terraform 扩展。
图 8.4 – HashiCorp 提供的 Terraform 扩展可以从 Visual Studio Marketplace 安装。
你可能会被提示允许浏览器打开本地的 VS Code 应用程序。如果是这样,点击 允许 按钮以打开 VS Code 并安装扩展。扩展将在几分钟内下载并安装完成。安装完成后,你应该可以在 VS Code 左侧导航栏中看到 HashiCorp Terraform 的菜单项。
图 8.5 – HashiCorp Terraform 扩展将在左侧导航栏中创建新的菜单项
现在,Terraform 扩展已经成功安装,当代码编辑器检测到 Terraform 文件时,扩展会自动激活。你可以通过在打开的 Terraform 文件右下角看到 Terraform 的标志来确认扩展是否已激活。
创建一个新的 Terraform 项目
让我们为我们的动手练习创建一个新的目录:
$ mkdir chapter_8_hands_on
$ cd chapter_8_hands_on
创建一个空的 Terraform 配置文件,命名为 main.tf,可以从命令行提示符或者使用 VS Code 创建:
$ touch main.tf
(可选)你可以从本章的 GitHub 仓库克隆示例项目,仓库地址为 github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter08。接下来,通过选择 文件 | 打开文件夹,并导航到目录的位置,在 VS Code 中打开该目录。
定义 Terraform 资源
让我们从扩展本章开始时 设置本地 Terraform 环境 部分的 Terraform 示例入手。你可以复制现有的 main.tf 文件,或者直接编辑现有 main.tf 配置文件的内容。
首先,让我们通过在 databricks_notebook 资源定义中添加第二个数据集来开始定义 DLT 流水线(出于简洁性考虑,定义 DLT 流水线源笔记本部分的代码在下面的代码块中已被省略)。现在我们将有一个包含两个数据集的数据流水线——一个铜层,接着是银层。请在 main.tf 文件中更新 databricks_notebook 资源定义,内容如下:
resource "databricks_notebook" "dlt_pipeline_notebook" {
path = "${data.databricks_current_user.my_user.home}/chp_8_terraform/taxi_trips_pipeline.py"
...
.load(path))
@dlt.table(
name="yellow_taxi_silver",
comment="Financial information from incoming taxi trips."
)
@dlt.expect_or_fail("valid_total_amount", "total_amount > 0.0")
def yellow_taxi_silver():
return (dlt.readStream("yellow_taxi_bronze")
.withColumn("driver_payment",
F.expr("total_amount * 0.40"))
.withColumn("vehicle_maintenance_fee",
F.expr("total_amount * 0.05"))
.withColumn("adminstrative_fee",
F.expr("total_amount * 0.1"))
.withColumn("potential_profits",
F.expr("total_amount * 0.45")))
EOT
)
}
接下来,在我们创建新的 DLT 流水线之前,我们需要在 Unity Catalog 中定义一个位置来存储流水线数据集。请将以下目录和架构资源定义添加到 main.tf 文件的底部:
resource "databricks_catalog" "dlt_target_catalog" {
name = "chp8_deploying_pipelines_w_terraform"
comment = "The target catalog for Taxi Trips DLT pipeline"
}
resource "databricks_schema" "dlt_target_schema" {
catalog_name = databricks_catalog.dlt_target_catalog.id
name = "terraform_demo"
comment = "The target schema for Taxi Trips DLT pipeline"
}
现在我们有了包含 DLT 流水线定义的更新源笔记本,以及一个存储流水线数据集的位置,我们可以定义一个 DLT 流水线。请将以下流水线定义添加到 main.tf 文件:
resource "databricks_pipeline" "taxi_trips_pipeline" {
name = "Taxi Trips Pipeline"
library {
notebook {
path = "${data.databricks_current_user.my_user.home}/chp_8_terraform/taxi_trips_pipeline.py"
}
}
cluster {
label = "default"
num_workers = 2
autoscale {
min_workers = 2
max_workers = 4
mode = "ENHANCED"
}
driver_node_type_id = "i3.2xlarge"
node_type_id = "i3.xlarge"
}
continuous = false
development = true
photon = false
serverless = false
catalog = databricks_catalog.dlt_target_catalog.name
target = databricks_schema.dlt_target_schema.name
edition = "ADVANCED"
channel = "CURRENT"
}
您会注意到,我们已经定义了包含 DLT 管道定义的笔记本的位置、用于管道更新和维护任务的默认集群,以及其他运行时设置,如开发模式、产品版本、频道等。
接下来,我们需要协调对 DLT 管道的更新,以便我们可以在重复的时间表上触发运行,配置警报通知或设置超时阈值。将以下工作流定义添加到main.tf文件的底部:
resource "databricks_job" "taxi_trips_pipeline_job" {
name = "Taxi Trips Pipeline Update Job"
description = "Databricks Workflow that executes a pipeline update of the Taxi Trips DLT pipeline."
job_cluster {
job_cluster_key = "taxi_trips_pipeline_update_job_cluster"
new_cluster {
num_workers = 2
spark_version = "15.4.x-scala2.12"
node_type_id = "i3.xlarge"
driver_node_type_id = "i3.2xlarge"
}
}
task {
task_key = "update_taxi_trips_pipeline"
pipeline_task {
pipeline_id = databricks_pipeline.taxi_trips_pipeline.id
}
}
trigger {
pause_status = "PAUSED"
periodic {
interval = "1"
unit = "HOURS"
}
}
}
最后,我们需要输出已部署资源的工作流 URL,以便我们可以轻松地从浏览器打开工作流 UI。将以下输出定义添加到main.tf文件中:
output "workflow_url" {
value = databricks_job.taxi_trips_pipeline_job.url
}
部署 Terraform 项目
在我们开始部署新资源之前,第一步是初始化 Terraform 项目。在父目录中执行 Terraform 的init命令,可以通过 VS Code 命令面板或 Shell 提示符来完成:
$ terraform init
接下来,通过执行 Terraform 的plan命令来预览 Terraform 文件中的更改,以查看拟议的基础设施更改:
$ terraform plan
总共有五个新的资源会被创建,包括databricks_notebook资源,它代表了包含 DLT 管道定义的笔记本、目标 Unity Catalog 的目录、目标 Unity Catalog 架构、databricks_pipeline资源,它代表我们的 DLT 管道,以及databricks_job资源,它代表将触发管道更新的工作流。
在验证计划之后,我们现在可以将我们的 DLT 管道部署到 Databricks 工作区。接下来,执行 Terraform 的apply命令,将新的基础设施更改部署到我们的工作区:
$ terraform apply
一旦所有资源更改应用完毕,您应该期待 Terraform 输出 Databricks 工作流的 URL。
将工作流 URL 复制并粘贴到浏览器窗口中,并确保该地址解析到目标工作区中新创建的工作流。您会注意到,新工作流包含一个用于更新 DLT 管道的任务。该工作流已暂停,如 Terraform 配置所示。您也可以点击蓝色的立即运行按钮来触发工作流的一个新的立即运行。
图 8.6 – Terraform 将输出用于更新 DLT 管道的工作流 URL
尽管将更改部署到目标 Databricks 工作区很简单,但撤销这些更改同样容易。执行以下命令以从目标 Databricks 工作区中删除所有资源更改:
$ terraform destroy
通过输入yes确认该决定。完全撤销所有资源从您的 Databricks 工作区中可能需要几分钟。
正如你所看到的,通过几个按键和几次点击,使用 Databricks Terraform 提供者在 Databricks 工作区中配置和去配置资源变得既快速又简便。我们没有指示 Terraform 如何将资源部署到目标 Databricks 工作区,而是专注于通过配置做出哪些更改,让 Terraform 工具为我们处理繁重的工作。
总结
在本章中,我们介绍了如何使用 Databricks Terraform 提供者实现 CI/CD 过程,以便在工作区之间部署数据管道。我们看到,如何轻松设置本地开发环境来处理 Terraform 配置文件,以及在将配置应用于目标环境之前,如何轻松测试我们的 Terraform 计划。我们还从 Terraform 注册中心安装了 Databricks Terraform 提供者,并将其导入到 Terraform 配置文件中。接下来,我们深入了解了 databricks_pipeline 资源,它由 Databricks Terraform 提供者用于将 DLT 管道部署到目标工作区。我们检查了资源规范中的每个参数,并了解了如何控制 DLT 管道的运行时配置、计算设置,甚至管道输出数据集的位置。最后,我们看到,通过将 Terraform 配置文件存储在 GitHub 等版本控制系统中并利用 Azure DevOps Pipelines 等构建工具自动化部署,自动化配置文件变得如此简单。我们通过一个实际操作示例来结束本章,展示了如何使用 Terraform 扩展和流行的代码编辑器 VS Code 从本地开发环境部署 DLT 管道。
然而,Terraform 并不适合所有人,可能对你的用例来说,它过于复杂或难以使用。在下一章中,我们将深入探讨Databricks 资源包(DABs),这是另一个 CI/CD 工具,可以简化将 Databricks 代码工件打包并部署到数据和机器学习工作负载的过程。
第九章:利用 Databricks 资产包简化数据管道部署
本章探讨了一种相对较新的 持续集成与持续部署 (CI/CD) 工具,称为 Databricks 资产包 (DABs),它可以用来简化数据分析项目在不同 Databricks 工作区中的开发和部署。在本章中,我们将深入了解 DABs 的核心概念。我们将通过几个实践操作示例来演示 DAB 的实际应用,帮助你熟悉作为 DAB 开发下一个数据分析项目。最后,我们将介绍如何通过版本控制系统(如 GitHub)使用 DAB 来促进跨团队协作,以及 DAB 如何简化即使是最复杂的数据分析部署。
在本章中,我们将覆盖以下主要内容:
-
Databricks 资产包简介
-
Databricks 资产包的应用
-
简化跨团队协作
-
版本控制与维护
技术要求
为了跟随本章中的示例,建议你拥有 Databricks 工作区的管理员权限,以便能够将 DAB 部署到目标工作区。你还需要下载并安装版本为 0.218.0 或更高版本的 Databricks CLI。所有的代码示例可以从本章的 GitHub 仓库中下载,地址为 github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter09。在本章中,我们将部署多个新的工作流、DLT 管道、笔记本和集群。预计这将消耗大约 5-10 Databricks Units (DBUs)。
Databricks 资产包简介
DAB 提供了一种简单便捷的方式,可以与 YAML 元数据一起开发你的数据和 人工智能 (AI) 项目,用于声明相关的基础设施—就像一个捆绑包。DAB 为数据工程师提供了一种程序化验证、部署和测试 Databricks 资源到目标工作区的方法。这可能包括部署工作区资产,如 Delta Live Tables (DLT) 管道、工作流、笔记本等。DAB 还提供了一种便捷的方式来开发、打包和部署机器学习工作负载,使用可重用的模板(我们将在 使用模板初始化资产包 部分介绍 DAB 模板),这些模板称为 MLOps 堆栈。
DAB 是围绕表达基础设施即代码(IaC)的原则设计的,利用配置来驱动数据应用程序架构组件的部署。DAB 提供了一种将 IaC 配置与数据资产(如 Python 文件、笔记本和其他依赖项)一起管理的方式。如果你觉得 Terraform(在第八章中有介绍)对于你组织在 Databricks 数据智能平台中的需求过于复杂,DAB 也可以作为一种替代方案。
DAB 与 Terraform 有一些相似之处,两者都是 IaC 工具,能够让用户定义云资源并以云中立的方式部署这些资源。然而,它们也有许多不同之处。让我们比较一下 DAB 和 Terraform 之间的一些相似性和差异性,以便更好地了解在组织的需求下,何时选择哪个工具:
图 9.1 – DAB 和 Terraform 都是 IaC 工具,但它们满足的是非常不同的需求
在我们开始编写第一个 DAB 之前,先花点时间了解一下构成 DAB 配置文件的主要构建块。
DAB 配置文件的元素
DAB 的核心是一个 YAML 配置文件,名为databricks.yml。该配置文件为工程师提供了一个配置其项目资源部署的入口点。该文件由许多可组合的构建块组成,这些构建块告诉 Databricks 的命令行界面(CLI)将什么资源部署到目标 Databricks 工作区,并如何配置每个资源。每个构建块都接受不同的参数来配置该组件。
在本章稍后,我们将介绍如何将配置文件分解为多个 YAML 文件,但为了简单起见,我们从单个 YAML 文件开始。在这个 YAML 配置文件中,我们将声明我们的 Databricks 资源以及其他元数据。这些构建块,或映射,告诉 DAB 工具创建什么 Databricks 资源,更重要的是,告诉 DAB 工具操作哪个 Databricks REST API 来创建和配置 Databricks 资源。
这些映射可以是各种 Databricks 资源。例如,DAB 配置文件可以包含以下映射的任意组合:
| 映射名称 | 是否必需? | 描述 |
|---|---|---|
| bundle | 是 | 包含有关当前资产包的顶级信息,包括 Databricks CLI 版本、现有集群标识符和 git 设置。 |
| variables | 否 | 包含在 DAB 部署执行过程中将动态填充的全局变量。 |
| workspace | 否 | 用于指定非默认工作区位置,例如根存储、工件存储和文件路径。 |
| permissions | 否 | 包含有关要授予已部署资源的权限的信息。 |
| resources | 是 | 指定要部署的 Databricks 资源以及如何配置它们。 |
| artifacts | 否 | 指定部署工件,如 Python .whl 文件,这些文件将在部署过程中生成。 |
| include | 否 | 指定一个相对文件路径模式列表,以包括其他配置文件。这是将 DAB 配置文件分割成多个子配置文件的一个好方法。 |
| sync | 否 | 指定在部署过程中要包含或排除的相对文件路径模式列表。 |
| targets | 是 | 除了 Databricks 工作区外,指定有关工作流、管道和工件的上下文信息。 |
表 9.1 – databricks.yml 文件中的映射
让我们看一个简单的 DAB 配置文件,以便我们熟悉一些基本概念。以下示例将创建一个名为**Hello, World!**的新 Databricks 工作流,该工作流将运行一个打印简单而流行表达式 Hello, World! 的笔记本:
bundle:
name: hello_dab_world
resources:
jobs:
hello_dab_world_job:
name: hello_dab_world_job
tasks:
- task_key: notebook_task
existing_cluster_id: <cluster_id>
notebook_task:
notebook_path: ./src/hello_dab_world.py
targets:
dev:
default: true
workspace:
host: https://<workspace_name>.cloud.databricks.com
在这个简单的示例中,我们的 DAB 配置文件由三个主要部分组成:
-
bundle : 该部分包含有关当前 DAB 的高级信息——在此案例中是其名称。
-
resources : 这定义了一个新的 Databricks 工作流,包含一个单一的笔记本任务,应该在现有集群上运行。
-
targets : 这指定了有关目标 Databricks 工作区的信息,工作流和笔记本应该部署到该工作区。
现在我们已经对 DAB 配置文件的基础知识有了充分了解,接下来让我们看看如何在不同的部署场景下部署 Databricks 资源。
指定部署模式
DAB 配置文件中有一个可用的属性是部署模式,它允许我们在部署资源时指定操作模式。部署模式有两种类型:开发模式和生产模式。
在 开发 模式下,所有资源都以特殊前缀 [dev <用户名>] 标记,表示这些资源处于开发阶段。此外,所有可用资源在部署时都会带有 dev 元数据标签,进一步表明这些资源处于开发阶段。正如你可能从 第二章 回忆起的那样,DLT 也有一个开发模式可用。当 DLT 管道在开发模式下使用 DAB 部署时,所有已部署的 DLT 管道将在目标工作区启用开发模式的情况下进行部署。
在开发生命周期中,工程师通常需要尝试更改并快速迭代设计变更。因此,开发模式也会暂停所有 Databricks 工作流计划,并允许相同工作流的并行运行,使工程师能够直接从 Databricks CLI 以临时方式运行工作流。同样,开发模式也允许您指定一个现有的通用集群用于部署过程,您可以通过 -- compute-id <cluster_id> 参数从 Databricks CLI 指定集群 ID,或者将集群 ID 添加到 YAML 配置文件的顶级 bundle 映射中。
让我们来看看如何指定一个目标工作区,以便将其用作开发环境,并使用默认的现有通用集群覆盖所有集群:
targets:
dev:
default: true
mode: development
compute_id: <cluster_id>
workspace:
host: https://<workspace_name>.cloud.databricks.com
相反,您还可以指定生产模式。在 生产 模式下,资源不会添加特殊的命名前缀,也不会应用标签。然而,生产模式会在将资源部署到目标工作区之前验证这些资源。例如,它会确保所有 DLT 管道已设置为生产模式,并且指定云存储位置或工作区路径的资源不会指向用户特定的位置。
在下一节中,我们将卷起袖子,深入使用 Databricks CLI 来实验资源包,并查看它们的实际应用。
Databricks 资源包在实际应用中的效果
DAB 完全依赖于 Databricks CLI 工具(请参阅第八章获取安装说明)来从模板创建新的资源包,将资源包部署到目标工作区,甚至从工作区中删除先前部署的资源包。对于本节内容,您需要使用版本 0.218.0 或更高版本的 Databricks CLI。您可以通过传递 -- version 参数快速检查本地 Databricks CLI 的版本:
databricks –-version
您应该会得到类似于以下 图 9 .2 的输出:
图 9.2 - 检查已安装的 Databricks CLI 版本
一旦您成功安装了推荐版本的 Databricks CLI,您可以通过显示 bundle 命令的手册页来测试安装是否成功。输入以下命令,以从 CLI 显示可用的参数和说明:
$ databricks bundle --help
我们将看到如下的手册页:
图 9.3 – Databricks CLI 中 bundle 命令的手册页
在开始编写 DABs 并将资源部署到 Databricks 工作区之前,我们需要先进行 Databricks 工作区的认证,以便我们可以部署资源。DABs 使用 OAuth 令牌与 Databricks 工作区进行认证。DABs 可以使用两种类型的 OAuth 认证——用户与机器(U2M)认证和机器与机器(M2M)认证。
用户与机器认证
U2M 认证涉及一个人在循环中生成一个 OAuth 令牌,该令牌可以在将新资源部署到目标工作区时使用。这种认证类型涉及一个用户,当 CLI 工具提示时,用户将通过 web 浏览器登录。此认证类型适用于开发场景,用户希望在非关键的开发工作区中尝试 DAB 并部署资源。
U2M 是与 Databricks 工作区认证的最简单方法,可以直接通过 Databricks CLI 完成:
$ databricks auth login --host <workspace-url>
工作区的信息,如工作区的 URL、昵称和认证详情,都会保存在本地机器的用户目录下的隐藏文件中。例如,在 Mac 和 Linux 系统中,这些信息将被写入用户主目录下的本地 ~/.databrickscfg 文件:
图 9.4 – 多个 Databricks 配置文件保存到本地配置文件的示例
你可以通过使用 CLI 命令传递 --profile <profile_nickname> 参数来快速切换不同的 Databricks 工作区。例如,以下命令将把 DAB 应用到 TEST_ENV 配置文件下保存的工作区:
$ databricks bundle deploy –-profile TEST_ENV
U2M 认证严格设计用于开发目的。对于生产环境,不推荐使用这种认证类型,因为它无法自动化,且无法限制访问到最低必要权限。在这种情况下,推荐使用 M2M 认证。
让我们看看这种替代认证类型,特别是当你在生产环境中自动化 DAB 部署时。
机器与机器认证
M2M 认证本身并不涉及人类。这种认证类型是为完全自动化的 CI/CD 工作流设计的。此外,这种认证类型与版本控制系统如 GitHub、Bitbucket 和 Azure DevOps 配合良好。
M2M 需要使用服务主体来抽象化 OAuth 令牌的生成。此外,服务主体使自动化工具和脚本仅通过 API 访问 Databricks 资源,相较于使用用户或组,提供了更高的安全性。因此,服务主体是生产环境的理想选择。
M2M 需要 Databricks 账户管理员创建一个服务主体,并从 Databricks 账户控制台生成 OAuth 令牌。一旦 OAuth 令牌在服务主体的身份下生成,该令牌就可以用来填充环境变量,如DATABRICKS_HOST、DATABRICKS_CLIENT_ID 和 DATABRICKS_CLIENT_SECRET,这些环境变量用于 GitHub Actions 或 Azure DevOps 等自动化构建和部署工具。
使用模板初始化资产包
DAB 还提供项目模板,允许开发人员使用预定义设置快速创建新包。DAB 模板包含预定义的工件和常用的 Databricks 项目设置。例如,以下命令将初始化一个本地 DAB 项目:
$ databricks bundle init
从 CLI 进行操作时,用户将被提示选择一个 DAB 模板:
图 9.5 – 使用 Databricks CLI 从模板初始化一个新的 DAB 项目
在撰写本文时,DAB 提供四个模板供选择:default-python,default-sql,dbt-sql 和 mlops-stacks(docs.databricks.com/en/dev-tools/bundles/templates.html)。不过,你也可以选择创建组织模板并生成可重用的项目包。
现在我们对 DAB 的基础知识有了清晰的了解,让我们将迄今为止学到的内容整合起来,并将一些资源部署到 Databricks 工作区。
实操练习 – 部署你的第一个 DAB
在这个实操练习中,我们将创建一个基于 Python 的资产包,并在目标工作区中部署一个简单的 Databricks 工作流,该工作流运行一个 DLT 管道。
我们从创建一个本地目录开始,稍后将在该目录中创建我们 DAB 项目的框架。例如,以下命令将在用户的主目录下创建一个新目录:
$ mkdir –p ~/chapter9/dabs/
接下来,导航到新创建的项目目录:
$ cd ~/chapter9/dabs
通过输入以下命令并在重定向到浏览器窗口时完成单点登录(SSO)登录,生成一个新的 OAuth 令牌,使用 U2M 认证:
$ databricks auth login
现在我们已经创建了目录,并且与目标工作区进行了身份验证,接下来让我们使用 Databricks CLI 初始化一个空的 DAB 项目。输入以下命令以弹出选择 DAB 模板的提示:
$ databricks bundle init
接下来,从模板选择提示中选择default-python。为你的项目输入一个有意义的名称,例如my_first_dab。当提示你选择一个笔记本模板时,选择No。当提示你是否包含一个示例 DLT 管道时,选择Yes。最后,当提示你是否添加示例 Python 库时,选择No。项目框架将被创建,此时你可以列出目录内容,以便查看生成的构件:
$ cd ./my_first_dab # a new dir will be created
$ ls -la # list the project artifacts
为了更方便地导航到新创建的项目文件,使用你喜欢的代码编辑器(如 VS Code)打开项目目录:
图 9.6——使用 default-python 模板生成的 DAB 项目框架,从 VS Code 查看
继续探索生成的 DAB 项目的子目录。你应该会注意到几个重要的目录和文件:
-
src:此目录包含作为笔记本文件的 DLT 管道定义。
-
resources:DAB 可以被分解为多个与单个资源或资源子集相关的 YAML 文件。此目录包含 DLT 管道的资源定义以及运行管道的工作流定义,包括调度、笔记本任务定义和作业集群属性。
-
databricks.yml:这是我们 DAB 的主要入口点和定义。它告诉 Databricks CLI 部署哪些资源以及如何部署它们,并指定目标工作空间信息。
-
README.md:这是项目的 README 文件,包含有关项目不同部分的有用信息,以及如何部署或撤销资源的说明。
打开src目录下包含的dlt_pipeline.ipynb笔记本。注意,这个笔记本定义了两个数据集——一个是读取来自 NYC Taxi 数据集的原始、未处理 JSON 文件的视图,另一个是基于fare_amount值小于 30 的行过滤视图的表。
接下来,打开databricks.yml文件。你会注意到该文件有三个主要部分:bundle、include和targets。
为了简化,在targets映射下,删除所有部分,保留dev部分。我们将在此练习中只部署到开发环境。
最后,确保dev目标指向正确的开发工作空间。你的databricks.yml文件应该类似于下面的内容:
bundle:
name: my_first_dab
include:
-resources/*.yml
targets:
mode: development
default: true
workspace:
host: https://<workspace_name>.cloud.databricks.com
保存对databricks.yml文件的更改,并返回到终端窗口。让我们通过从 Databricks CLI 执行validate命令来验证我们对 DAB 项目所做的更改:
$ databricks bundle validate
现在我们的项目已经根据我们的需求进行了修改,是时候将 bundle 部署到我们的开发工作空间了。从你的 Databricks CLI 中执行以下命令:
$ databricks bundle deploy
Databricks CLI 将解析我们的 DAB 定义,并将资源部署到我们的开发目标。登录到开发工作区并验证是否已创建一个名为**[dev ] my_first_dab_job**的工作流,并且您的 Databricks 用户被列为所有者。
恭喜!您刚刚创建了第一个 DAB 并将其部署到开发工作区。您已经在自动化部署数据管道和其他 Databricks 资源的路上迈出了重要一步。
让我们通过执行已部署工作流的新运行来测试部署是否成功。从同一个 Databricks CLI 中,输入以下命令。这将启动新创建的工作流的执行,并触发 DLT 管道的更新:
$ databricks bundle run
您可能会被提示选择要运行的资源。在这里,请选择my_first_dab_job。如果成功,您应该会看到 CLI 的确认消息,说明工作流正在运行。返回到您的 Databricks 工作区,验证确实已开始执行运行。
在某些情况下,您可能需要从目标工作区中撤销部署资源。要撤销先前创建的工作流和 DLT 管道定义,我们可以使用 Databricks CLI 中的destroy命令。输入以下命令以恢复在本次实践中所做的所有更改。您需要确认是否永久删除所有资源:
$ databricks bundle destroy
到目前为止,我们已经在目标 Databricks 工作区中创建了一个简单的工作流和 DLT 管道,并在源笔记本中定义了它们。我们使用本地代码编辑器编写了 DAB 项目,并将更改从本地计算机部署出去。然而,在生产环境中,您将与组织内的团队合作,共同编写数据管道和其他 Databricks 资源,这些资源协同工作,为您的组织生成数据产品。
在下一节中,我们将探讨如何在这个简单的练习基础上进行扩展,并与团队成员一起使用自动化工具部署 Databricks 资源,如工作流、笔记本或 DLT 管道。
实践练习 – 使用 GitHub Actions 简化跨团队协作
通常,您将与一个由数据工程师组成的团队一起工作,部署 Databricks 资产,如 DLT 管道、通用集群或工作流等。在这些情况下,您可能会使用版本控制系统(如 GitHub、Bitbucket 或 Azure DevOps)与团队成员协作。
DAB 可以轻松地集成到您的 CI/CD 管道中。让我们看看如何使用 GitHub Actions 自动部署代码库主分支所做的更改,并将资源更改自动部署到生产 Databricks 工作区。
GitHub Actions 是 GitHub 中的一项功能,允许用户直接从 GitHub 仓库实现 CI/CD 工作流,使得基于某些触发事件(例如将功能分支合并到主分支)声明要执行的工作流变得简单。结合 DABs,我们可以实现一个强大、完全自动化的 CI/CD 管道,将对 Databricks 代码库所做的更改自动部署。这使得我们的团队能够更加灵活,尽快部署可用的更改,从而加速迭代开发周期并快速测试更改。
设置环境
在这个动手实践中,我们将创建一个 GitHub Action,在我们的 GitHub 仓库中分支合并后,自动将更改部署到 Databricks 工作空间。让我们回到本章早些时候的示例。如果你还没有这样做,可以从本章的 GitHub 仓库克隆示例:github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter09。
首先,让我们在仓库的根目录下创建一个新的私有文件夹——即 .github。在此文件夹中,让我们创建另一个名为 workflows 的子文件夹。这个嵌套目录结构是一个特殊的模式,其存在将被 GitHub 仓库自动识别并解析为 GitHub Actions 工作流。在这个文件夹中,我们将定义 GitHub Actions 工作流,工作流同样使用 YAML 配置文件声明 CI/CD 工作流。在 .github/workflows 文件夹中创建一个名为 dab_deployment_workflow.yml 的新 YAML 文件。
接下来,我们将在我们喜欢的代码编辑器中打开工作流文件,以便更方便地操作。
配置 GitHub Action
我们首先通过在 GitHub Actions 工作流文件中添加基本结构来开始。我们将在 YAML 文件中为 GitHub Actions 工作流指定一个用户友好的名称,例如 DABs in Action。在这个文件中,我们还将指定每当一个批准的拉取请求被合并到我们的代码仓库的主分支时,CI/CD 管道就应当运行。将以下内容复制并粘贴到新创建的文件 dab_deployment_workflow .yml 中:
name: "DABs in Action"
on:
push:
branches:
- main
接下来,让我们在 GitHub Actions 的 YAML 文件中定义一个任务,该任务将克隆 GitHub 仓库、下载 Databricks CLI,并将我们的 DAB 部署到目标 Databricks 工作空间。将以下任务定义添加到工作流文件中:
jobs:
bundle-and-deploy:
name: "DAB Deployment Job"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: databricks/setup-cli@main
- run: databricks bundle deploy --target prod
working-directory: ./dabs
env:
DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_SERVICE_PRINCIPAL_TOKEN }}
你还会注意到,我们在之前的示例中使用了相同的 Databricks CLI bundle 命令来部署我们的 Databricks 资源,使用本地安装来部署资源。此外,在 working-directory 参数下,我们指定了 DAB 配置文件将位于 GitHub 仓库根目录下的 dabs 文件夹中。我们还利用了 GitHub Secrets(docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository)安全地存储了用于与目标 Databricks 工作空间进行身份验证的 API 令牌,并遵循了使用服务主体的最佳实践(请参阅 用户到机器身份验证 部分)来自动化资源的部署。
你可能还记得,服务主体只能调用部分 API,并遵循最小权限的最佳实践,而用户账户则会提供比必要的更多权限。此外,我们的用户可能会在组织中进出,这使得诸如用户注销等维护任务变得头疼。
测试工作流
现在我们已经定义了何时触发 CI/CD 流水线以及负责将 DAB 部署到目标工作空间的工作流作业,我们可以测试 GitHub Actions 工作流。
让我们在现有的 GitHub Actions 工作流文件中添加一个部分,该部分将触发我们在前一个示例中创建的 my_first_dab_job Databricks 工作流。你还会注意到,在 needs 参数下,我们声明了对 DAB Deployment Job 的依赖关系,必须先完成该作业,才能执行 Databricks 工作流的运行。换句话说,在部署更改之前,我们不能测试这些更改。将以下作业定义添加到工作流文件中的 bundle-and-deploy 作业下:
run-workflow:
name: "Test the deployed pipeline workflow"
runs-on: ubuntu-latest
needs:
- bundle-and-deploy
steps:
- uses: actions/checkout@v3
- uses: databricks/setup-cli@main
- run: databricks bundle run my_first_dab_job
working-directory: ./dabs
env:
DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_SERVICE_PRINCIPAL_TOKEN }}
保存 GitHub Actions 工作流文件。现在,通过在 GitHub 仓库中打开一个新的拉取请求并将其合并到主分支来测试更改。
首先,使用 git 创建一个新的功能分支:
$ git checkout –b increaseAutoScaling
接下来,在代码编辑器中打开 Databricks 工作流的 DAB 配置文件。将我们的作业集群的自动缩放大小从四个工作节点更新为五个。保存文件并提交更改到分支。最后,将更改推送到远程仓库。使用 Web 浏览器导航到 GitHub 仓库并创建一个新的拉取请求。批准更改并将分支合并到主分支。确保触发 GitHub Actions 工作流,并且代码更改已部署到目标 Databricks 工作空间。你还应该看到 my_first_dab_job Databricks 工作流的一个新运行已由 GitHub Actions 工作流执行。
现在我们已经看到了将我们的 DABs 融入 CI/CD 管道中有多么容易,让我们扩展这个例子,看看当我们希望将我们的代码库的不同版本部署到 Databricks 工作空间时,DABs 如何帮助我们。
版本控制和维护
DABs 可以简化迭代地将更改部署到不同的环境。可能会有场景,您希望尝试不同的更改,并记录这些更改来自存储库的特定版本。顶级bundle映射允许用户指定存储库 URL 和分支名称,以注释部署到目标 Databricks 工作空间的代码库的不同版本。这是一个很好的方式来记录 bundle 部署来自特定存储库和功能分支的情况。例如,以下代码注释说明了一个资产 bundle 使用了实验性功能分支作为项目来源:
bundle:
name: new-feature-dab
git:
origin_url: https://github.com/<username>/<repo_name>
branch: my_experimental_feature_br
以另一个例子来说,DABs 使得自动化和文档化常规维护活动变得简单,例如将 Databricks 运行时升级到最新版本。这是一个很好的方式来试验运行时的 beta 版本,并测试与现有 Databricks 工作流的兼容性。例如,如果工作流开始失败,DABs 可用于自动化手动部署和测试过程,甚至回滚更改。
概述
在本章中,我们介绍了如何使用 DABs 自动化部署您的 Databricks 资源。我们看到了 Databricks CLI 在从预配置模板创建新 bundle、将 CLI 工具与目标 Databricks 工作空间进行身份验证、触发 Databricks 工作流运行以及管理端到端 bundle 生命周期中的重要性。我们还看到了通过在 DAB 配置文件中使用开发模式来快速迭代设计和测试的方法。
在下一章中,我们将总结在生产环境中监控数据应用所需的技能。我们将涉及 Databricks 数据智能平台的关键特性,包括警报、查看管道事件日志以及使用 Lakehouse Monitoring 测量统计指标。
第十章:生产环境中的数据管道监控
在前几章中,我们学习了如何使用 Databricks 数据智能平台构建、配置和部署数据管道。为了完善湖仓的数据管道管理,本书的最后一章将深入探讨生产环境中数据管道监控的关键任务。我们将学习如何直接利用 Databricks 数据智能平台的全面监控技术,追踪管道健康状况、管道性能和数据质量等。我们还将通过实际操作练习实现一些现实世界的示例。最后,我们将讨论确保数据管道平稳运行的最佳实践,及时检测和解决问题,并确保为您的分析和业务需求提供可靠、准确的数据。
在本章中,我们将涵盖以下主要内容:
-
数据管道监控简介
-
管道健康和性能监控
-
数据质量监控
-
生产环境故障解决的最佳实践
-
实操练习 – 设置一个 Webhook 警报,当作业运行时间超过预期时触发
技术要求
为了跟随本章中的示例,您需要拥有 Databricks 工作空间权限,以创建和启动通用集群,便于导入和执行本章附带的笔记本。还建议将您的 Databricks 用户提升为工作空间管理员,这样您才能创建和编辑警报目标。所有代码示例可从本章的 GitHub 仓库下载:github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter10。本章将创建并运行多个新笔记本,预计消耗约 10-15 Databricks Units (DBUs)。
数据管道监控简介
随着数据团队将数据管道部署到生产环境中,能够在发生处理错误、延迟或数据质量问题时立即检测到,这对于在问题扩展到下游系统和流程之前进行捕捉和纠正,能产生巨大的影响。因此,数据团队构建和部署管道的环境应该能够监控它们,并在出现问题时发出警报。
探索监控数据管道的方法
数据团队可以通过 Databricks 数据智能平台在生产环境中监控数据管道。比如,数据团队可以通过以下方式手动观察数据管道的更新:
-
从 Delta Live Tables (DLT) UI 查看管道状态
-
从 DLT 事件日志中查询管道信息
尽管这些手动方式提供了一种快速查看数据管道最新状态的方式,但它们显然不是一个可扩展的解决方案,特别是当你的数据团队增加越来越多的管道时。相反,组织会转向更自动化的机制。例如,许多组织选择利用 Databricks 数据智能平台内置的通知系统。通知在平台内的许多对象中都很常见。例如,数据管理员可以在以下场景中配置通知,提醒数据团队有关特定 Databricks 资源状态的变化:
-
DLT 管道(无论是更新还是流)
-
Databricks 工作流(在最上层的作业级别)
-
Databricks 工作流任务(比前述选项更细粒度的通知)
尽管这些通知有助于在数据处理过程中提醒团队有关事件或状态变化,数据团队还需要机制来提醒彼此有关进入企业湖仓的数据内容中的问题。
使用 DBSQL 警报通知数据有效性
Databricks 数据智能平台可以通过平台内的 DBSQL 部分创建警报通知,称为DBSQL 警报。DBSQL 警报是一个有用的工具,可以提醒数据团队关于数据进入他们的企业湖仓的信息。DBSQL 警报通过指定一个特定的查询结果条件来操作,只有当条件被满足时,数据才会被认为是有效的。然而,如果警报中的某个条件被违反,比如订单金额超过某个美元阈值,那么系统就会触发通知并发送到警报目标。下图描述了一个 DBSQL 警报,当销售订单超过特定金额时通过电子邮件通知收件人——在这个例子中,金额阈值是 10,000 美元。在此示例中,查询是一个最大值聚合,触发条件是当最大值聚合超过 10,000 美元时,警报目标是一个电子邮件地址。
图 10.1 – 配置 DBSQL 警报通过电子邮件通知收件人
此外,DBSQL 警报可以设置为按重复计划执行,例如每小时执行一次。这是一种极好的方式,通过 Databricks 数据智能平台内置的机制,自动化进行数据验证检查,确保数据集内容的准确性。下图是一个示例,展示了如何使用警报在重复的时间间隔内调度数据验证查询,并在特定条件或条件集被违反时通知数据团队。
图 10.2 – 警报触发条件的配置
监控生产环境中的数据管道的另一种机制是通过工作流通知。在 Databricks 数据智能平台中,通知消息可以发送到企业消息平台,如 Slack 或 Microsoft Teams,或发送到事件管理系统,如 PagerDuty。本章稍后我们将探讨如何实现基于 HTTP Webhook 的交付目标,这在 Web 服务架构环境中非常流行。
可以从特定工作流中发送两种类型的通知——作业状态和任务状态。作业状态通知是关于特定工作流整体成功或失败的高级状态。然而,您还可以配置通知在任务级别发送到监控目标,例如,如果您希望监控工作流中的任务何时被重试。
图 10.3 – 配置作业级别和任务级别的通知
虽然警报通知是自动通知团队成员处理问题的好方法,但数据团队还需要定期和临时地监控数据管道的健康状况。我们将在下一节讨论这一点。
管道健康状况和性能监控
Databricks 数据智能平台提供了一个供数据团队查询数据管道状态的地方,称为事件日志。事件日志包含与特定 DLT 管道相关的所有事件的历史记录。特别地,事件日志将包含一个事件流,其中列出包含以下录制元数据的事件对象:
-
发生的事件类型
-
事件的唯一标识符
-
事件发生的时间戳
-
事件的高级描述
-
关于事件的细粒度详情
-
事件级别指示(INFO、WARN、ERROR 或 METRICS)
-
事件的来源
与标量函数不同,标量函数返回单一值,表值函数(TVF)则是返回一个表作为结果的函数。对于发布到 Unity Catalog 中目录和模式的 DLT 管道,Databricks 数据智能平台提供了一种特殊的 TVF,名为 event_log(),用于查询有关特定 DLT 管道的全面信息。event_log() 函数可以接受两个参数之一作为输入:管道数据集的完全限定表名或管道 ID 作为参数。
图 10.4 – event_log() TVF 返回发生的事件列表
event_log() 函数将检索有关给定 DLT 管道的信息,包括以下内容:
-
数据质量检查(预期结果)的结果
-
审计信息
-
管道更新状态
-
数据血统信息
一种常见的方法是,数据管理员可以通过注册一个视图与特定管道的数据集一起,使查询特定 DLT 管道事件变得更加容易。这允许用户在后续查询中方便地引用事件日志结果。以下 SQL 数据定义语言 (DDL) 语句将创建一个视图,用于检索 ID 为 my_dlt_pipeline_id 的 DLT 管道的事件日志:
CREATE VIEW my_pipeline_event_log_vw AS
SELECT
*
FROM
event_log('<my_dlt_pipeline_id>');
有时,特定管道的事件日志可能会增长得太大,使得数据管理员很难快速总结最新的状态更新。相反,数据团队可以进一步缩小事件日志的范围,聚焦于 DLT 管道中的特定数据集。例如,数据团队可以在某个特定数据集上创建视图,通过 table() 函数捕获所有事件,并提供完全限定的表名作为函数的参数。以下 SQL DDL 语句将创建一个视图,用于检索名为 my_gold_table 的数据集的事件日志:
CREATE VIEW my_gold_table_event_log_vw AS
SELECT
*
FROM
event_log(table(my_catalog.my_schema.my_gold_table));
event_log() TVF 函数为数据团队提供了对特定 DLT 管道和数据集上执行的操作的良好可见性,使得实现端到端的可观察性和可审计性变得更加容易。
重要提示
当前,如果一个 DLT 管道被配置为将输出数据集发布到 Unity Catalog,那么只有特定 DLT 管道的所有者可以查询这些视图。为了共享事件日志的访问权限,管道所有者必须将事件日志馈送的副本保存到 Unity Catalog 中的另一个表中,并授予其他用户或组访问权限。
让我们来看一下如何利用 event_log() 函数查询特定 DLT 管道的数据质量事件。
实践练习 – 查询数据集的数据质量事件
重要提示
对于以下练习,您需要使用共享的通用集群或 Databricks SQL 仓库来查询事件日志。此外,事件日志仅适用于查询已配置为在 Unity Catalog 中存储数据集的 DLT 管道。对于已配置为在传统 Hive Metastore 中存储数据集的 DLT 管道,将找不到事件日志。
数据质量指标作为序列化的 JSON 字符串存储在事件日志中。我们需要将 JSON 字符串解析成不同的数据结构,以便能够方便地查询事件日志中的数据质量事件。我们将使用 from_json() SQL 函数解析序列化的 JSON 字符串,以满足我们的数据质量预期。我们需要指定一个 schema 作为参数,指示 Spark 如何将 JSON 字符串解析为反序列化的数据结构——具体来说,是一个包含期望名称、数据集名称、通过记录数和失败记录数的结构体数组。最后,我们将使用 explode() SQL 函数,将期望结构体数组转换为每个期望的新行。
我们可以利用之前定义的视图来监控我们 DLT 管道中数据集的持续数据质量。让我们创建另一个与 DLT 管道数据质量相关的视图:
CREATE OR REPLACE TEMPORARY VIEW taxi_trip_pipeline_data_quality_vw AS
SELECT
timestamp,
event_type,
message,
data_quality.dataset,
data_quality.name AS expectation_name,
data_quality.passed_records AS num_passed_records,
data_quality.failed_records AS num_failed_records
FROM
(
SELECT
event_type,
message,
timestamp,
explode(
from_json(
details :flow_progress.data_quality.expectations,
"ARRAY<
STRUCT<
name: STRING,
dataset: STRING,
passed_records: INT,
failed_records: INT
>
>"
)
) AS data_quality
FROM
my_table_event_log_vw
);
数据团队常常提出一些常见问题,比如:“处理了多少条记录?”,“有多少条记录未通过数据质量验证?”或者“通过记录与未通过记录的比例是多少?”。让我们进一步分析前面的例子,并总结一下我们管道中每个数据集的高层数据质量指标。我们来统计一下应用了预期值的行数,以及每个数据集中通过记录与未通过记录的百分比:
SELECT
timestamp,
dataset,
sum(num_passed_records + num_failed_records)
AS total_expectations_evaluated,
avg(
num_passed_records /
(num_passed_records + num_failed_records)
) * 100 AS avg_pass_rate,
avg(
num_failed_records /
(num_passed_records + num_failed_records)
) * 100 AS avg_fail_rate
FROM
taxi_trip_pipeline_data_quality_vw
GROUP BY
timestamp,
dataset;
我们得到以下输出:
图 10.5 – DLT 事件日志中捕获的事件
如你所见,event_log() 函数使数据团队能够轻松查询有关给定 DLT 管道的全面信息。数据团队不仅可以查询管道更新的状态,还可以查询质量数据是否已成功地导入到湖仓。然而,数据团队仍然需要一种方式,在运行时自动通知数据质量检查失败的情况,尤其是在下游报告的数据准确性对业务至关重要时。我们将在接下来的部分中更详细地探讨这个问题。
数据质量监控
持续监控你湖仓中数据集的数据质量,对于成功部署到生产环境的业务关键型数据应用至关重要。例如,假设某个关联列突然注入了空值,这可能会影响到依赖于上游数据集连接的下游报告。突然间,商业智能(BI)报告可能会刷新,但数据可能显得过时或不准确。通过在问题出现时自动检测数据质量问题,数据团队可以及时收到潜在问题的警报,并立即采取措施干预,修正可能的数据损坏,甚至数据丢失。
图 10.6 – 及早检测问题对确保下游流程的质量至关重要
引入湖仓监控
Lakehouse Monitoring,作为 Databricks 数据智能平台的一个新功能,赋予数据团队跟踪和监控湖仓中数据及其他资产数据质量的能力。数据团队可以自动测量列之间的数据统计分布、空值数量、最小值、最大值、中位数值以及其他统计属性。通过 Lakehouse Monitoring,数据团队能够自动检测数据集中的重大问题,如数据偏斜或缺失值,并提醒团队成员关注问题,以便他们采取适当措施。
湖仓监控在监控 Delta 表、视图、物化视图和流表的数据质量时最为有效。它甚至可以在机器学习(ML)管道中使用,测量数据集的统计摘要,并在检测到数据漂移时触发警报通知。此外,湖仓监控可以根据监控度量的粒度需求进行精细或粗粒度定制。
湖仓监控从创建一个监控对象开始,监控对象随后将附加到湖仓中的一个数据资产(例如 Delta 表)。在后台,监控对象将创建两个额外的表,用于捕捉对应 Delta 表或其他数据资产的统计度量。
然后,监控表将被用于驱动仪表板,数据团队和其他相关方可以使用该仪表板查看湖仓中数据质量的实时数据洞察。
图 10.7 – 一个湖仓监控器将为监控的数据资产创建两个度量表。
湖仓监控器可以配置为衡量数据资产的不同方面,这也被称为配置文件类型。可以创建三种监控配置文件类型:
-
快照:这是一个通用但强大的监控工具。它用于监控表的数据质量和其他度量指标。
-
时间序列:它适用于时间序列数据集。它用于监控数据在时间段窗口内的质量变化。
-
推断:将机器学习模型推断的质量与输入在一段时间内的变化进行比较是非常有用的。
本章仅覆盖时间序列和快照类型。推断的讨论超出了本书的范围,但我们鼓励你探索湖仓监控如何对机器学习用例有所帮助(docs.databricks.com/en/lakehouse-monitoring/fairness-bias.html)。
也可以创建比较表的统计指标与基准表的监控器。例如,可以用于比较智能恒温器设备本周的相对湿度与上周的湿度,或者将某个数据集的录入销售数量与上月的销售报告进行比较。
让我们来看一个实际的例子,看看如何在湖仓中使用湖仓监控器。
实操练习 – 创建一个湖仓监控器
在这个动手练习中,我们将创建一个 lakehouse 监控工具,用于衡量目标 Delta 表的数据质量。虽然我们的 Delta 表确实包含时间戳信息,但我们将选择一个 快照配置文件 来监控我们 lakehouse 中目标 Delta 表的数据质量。回想一下,快照配置文件是一个通用的 lakehouse 监控工具,正如前面所提到的,它还具有相当大的灵活性。快照分析器将允许我们衡量数据集的标准总结性指标,或者围绕数据质量插入自定义的业务计算。
就像 Databricks 数据智能平台中的许多资源一样,您可以通过多种方式创建新的 lakehouse 监控工具。例如,您可以使用 Databricks UI、Databricks REST API、Databricks CLI(详见 第九章),或者像 Terraform 这样的自动化工具,等等。也许最简单的创建新监控工具的方式是通过 UI。在这个动手练习中,我们将使用 Databricks UI 来创建 lakehouse 监控工具。这是开始实验 Lakehouse 监控和不同数据质量度量的绝佳方法,用于评估数据集。然而,建议在生产环境中将您的 lakehouse 监控工具迁移到自动化构建工具,如 Databricks 资产包(DABs)(详见 第九章)或 Terraform(详见 第八章)。
如果您还没有这样做,可以在 github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter10 下载并克隆本章的相关代码资源。
第一步是生成一个目标 Delta 表,我们希望监控其数据质量。克隆或导入数据生成器笔记本,或创建一个新的笔记本并使用以下代码生成器源代码。
在笔记本的第一个单元格中,我们将利用 %pip 魔法命令来下载并安装 dbldatagen Python 库,用于生成样本数据:
%pip install dbldatagen==0.4.0
接下来,我们将定义一个辅助函数,用于生成一个包含智能恒温器读数的合成数据集,记录随时间变化的情况:
import dbldatagen as dg
from pyspark.sql.types import IntegerType, FloatType, TimestampType
def generate_smart_thermostat_readings():
"""Generates synthetics thermostat readings"""
ds = (
dg.DataGenerator(
spark,
name="smart_thermostat_dataset",
rows=10000,
partitions=4)
.withColumn("device_id", IntegerType(),
minValue=1000000, maxValue=2000000)
.withColumn("temperature", FloatType(),
minValue=10.0, maxValue=1000.0)
.withColumn("humidity", FloatType(),
minValue=0.1, maxValue=1000.0)
.withColumn("battery_level", FloatType(),
minValue=-50.0, maxValue=150.0)
.withColumn("reading_ts", TimestampType(), random=False)
)
return ds.build()
# Generate the data using dbldatagen
df = generate_smart_thermostat_readings()
df.display()
最后,我们将把新创建的数据集保存为 Unity Catalog 中的 Delta 表:
(df.write
.format("delta")
.mode("overwrite")
.saveAsTable(FULLY_QUALIFIED_TABLE_NAME))
既然我们的 Delta 表已经在 lakehouse 中创建完成,让我们在 Catalog Explorer 中使用 UI 来创建一个新的监控工具。
在左侧导航栏中,点击“目录浏览器”图标。接下来,通过展开目录列表或使用搜索字段过滤结果,导航到为本章创建的目录。点击为本章创建的架构。最后,点击之前由我们的数据生成笔记本创建的 Delta 表。点击标题为质量的数据质量标签。
图 10.8 – 可以直接从 Databricks 用户界面创建新的监控器
接下来,点击开始使用按钮,开始创建新的监控器。一个弹出对话框将打开,提示您选择监控器的配置文件类型,以及一些高级配置选项,如调度、通知传递和存储生成的仪表板的工作区目录。
点击下拉菜单选择配置文件类型,并选择生成快照配置文件的选项。
接下来,点击高级选项部分以展开对话框表单。用户界面将允许用户捕获数据集指标,可以选择手动执行或定义一个 cron 调度来定期执行指标计算。您会注意到,该对话框提供了使用传统 cron 语法定义调度的灵活性,或者通过选择对话框表单中的日期和时间下拉菜单来定义调度。对于本次实践,我们将选择前一种方式,并通过点击按钮手动刷新监控指标。
可选地,您可以选择将关于监控指标计算成功或失败的通知通过电子邮件发送给一组电子邮件接收者。您最多可以添加五个电子邮件地址,以便通知能够送达。确保您的用户电子邮件地址列在通知部分,并且勾选复选框以接收关于指标收集失败的通知。
如果你还记得之前提到的,湖仓监控器将创建两个指标表。我们需要在 Unity Catalog 中提供存储这些指标表的位置。在指标部分,添加为本章实践创建的目录和架构名称。例如,输入chp10.monitor_demo。
我们需要指定的最后一项是存储生成的湖仓监控仪表板的工作区位置。默认情况下,生成的资源将存储在用户的主目录下,例如**/Users/<user_email_address>/databricks_lakehouse_monitoring**。在本次实践中,我们将接受默认位置。
我们准备好创建监控器了!点击创建按钮,以为我们的 Delta 表创建湖仓监控器。
由于我们尚未为湖仓监控配置调度,因此我们需要手动执行度量收集。在 Catalog Explorer 中,在我们 Delta 表的质量标签下,点击刷新度量按钮以手动触发度量收集。
图 10.9 – 监控度量可以通过 Catalog Explorer UI 手动触发
表格度量的更新将会被触发并执行,可能需要几分钟才能完成。更新完成后,点击查看仪表板按钮查看捕获的度量数据。恭喜你!你已经创建了第一个湖仓监控,并且你正在稳步推进,为数据团队实现强大且自动化的数据质量可观察性。
现在我们知道了如何在生产问题出现时提醒我们的团队成员,让我们关注一些解决生产部署中失败问题的方法。
生产故障解决的最佳实践
DLT 框架是针对故障解决设计的。例如,DLT 会自动响应三种常见的管道故障类型:
-
Databricks Runtime 回归(详见 第二章)
-
更新处理失败
-
数据事务失败
让我们更详细地看一下更新失败和数据事务失败。
处理管道更新失败
DLT 框架是针对强大的错误处理设计的。在管道更新期间,框架会尝试将最新的更新应用到数据流图中定义的表。如果发生处理错误,框架会将错误分类为可重试错误或不可重试错误。可重试错误意味着框架将运行时错误分类为可能由当前条件集引起的问题。例如,系统错误不会被认为是可重试错误,因为它与运行时环境有关,而执行重试无法解决。然而,网络超时会被视为可重试错误,因为它可能受到临时网络环境条件的影响。默认情况下,如果检测到可重试错误,DLT 框架会对管道更新进行两次重试。
从表事务失败中恢复
由于 Delta Lake 事务日志的特性,数据集的更改是原子性的,这意味着它们只能在表事务(如数据操作语言(DML)语句)提交到事务日志时发生。因此,如果一个事务在执行过程中失败,则整个事务会被放弃,从而防止数据集进入需要数据团队介入并手动撤销数据更改的非确定性状态。
现在我们已经理解了如何处理生产环境中的管道故障,让我们通过一个真实世界的示例来巩固本章的主题。
实践练习 – 设置 Webhook 警报,当作业运行时间超过预期时进行通知
在这个实践练习中,我们将创建一个自定义 HTTP Webhook,当 Databricks 中的定时作业超时时,会向 HTTP 端点发送通知。
Webhook 警报是 Databricks 数据智能平台中的一种通知机制,它使数据团队能够通过自动发布特定作业执行结果,来监控其数据管道。例如,您可以收到关于作业成功运行、执行状态和运行失败的通知。
为什么我们使用工作流而不是直接使用 DLT 管道?
实际上,DLT 管道通常只是一个完整数据产品中的许多依赖项之一。Databricks 工作流是一个流行的编排工具,能够准备依赖项、运行一个或多个 DLT 管道,并执行下游任务。在本次练习中,我们将配置来自 Databricks 工作流的通知,而不是直接从 DLT 管道中获取通知,以模拟典型的生产场景。
让我们首先进入您的 Databricks 工作区并登录。接下来,创建一个新的工作流。我们可以通过点击工作区导航栏左侧的工作流图标,进入工作流 UI。为工作流命名时,可以选择一个有意义的名称,例如生产 监控演示。
如果您还没有下载,可以在github.com/PacktPublishing/Building-Modern-Data-Applications-Using-Databricks-Lakehouse/tree/main/chapter10下载本章练习的示例笔记本。我们将使用名为04a-IoT 设备数据生成器.py的物联网设备数据生成器笔记本和名为04b-IoT 设备数据管道.py的物联网设备 DLT 管道定义笔记本。
在工作流 UI 中,创建一个包含两个任务的新工作流。第一个任务将使用04a-IoT 设备数据生成器.py笔记本准备输入数据集;第二个任务将使用04b-IoT 设备数据管道.py笔记本执行读取生成数据的 DLT 管道。
图 10.10 – 工作流将生成物联网设备数据并执行 DLT 管道更新
现在我们的工作流已经创建完成,假设我们的管道执行时间比预期的要长。若出现潜在的处理延迟,是否能及时收到通知,以便您的数据团队立即进行调查,或者防止由于处理错误导致长时间运行的任务产生巨额云计算费用,这不就非常有帮助吗?
幸运的是,Databricks 数据智能平台使得配置这种类型的通知变得简单。让我们为工作流创建一个超时阈值。这将自动通知我们的 HTTP webhook 端点,告知我们的工作流执行时间超过了预期。一旦工作流超过了这个超时阈值,当前的执行将被停止,并且该执行被标记为失败。我们希望在这种失败场景发生时收到通知。
在工作流 UI 中,点击新创建的工作流生产监控演示,以查看详细信息。在作业通知部分,点击添加度量阈值按钮,添加一个新的运行时长阈值。让我们将最大时长设置为 120 分钟,然后点击保存按钮。接下来,点击**+ 添加通知按钮,添加一个新的通知。展开目标下拉菜单,选择+ 添加新系统目标**。一个新的浏览器标签页将打开,显示 Databricks 工作区的工作区管理设置。在通知部分,点击管理按钮。点击添加目标按钮。选择Webhook作为目标类型,为目标提供一个有意义的名称,输入通知应发送的端点 URL,并在端点使用基本 HTTP 认证时输入用户名和密码信息。点击创建按钮以创建 Webhook 目标。
图 10.11 – 创建一个新的 Webhook 目标
最后,点击保存按钮以最终确定度量阈值通知。
图 10.12 – 可以为工作流的任务设置执行时长阈值
现在我们已经设定了运行时长阈值,每当我们的工作流运行超过 120 分钟时,工作流将会停止,并且会向我们的 HTTP webhook 目标发送一个状态为超时的通知消息。
恭喜!现在你已经自动化了生产环境中数据管道的监控,当出现故障条件时,团队将会自动收到通知。这意味着你的团队可以在问题发生时立即介入并修正数据处理问题,从而最小化潜在的停机时间。
总结
在本章中,我们介绍了几种实现管道和数据质量可观测性的技术,以便数据团队能够在问题出现时迅速做出反应,避免下游大规模的中断。成为一个成功的数据团队的关键之一就是能够快速应对问题。我们看到了 Databricks 数据智能平台在许多方面内置了警报通知,并且我们可以配置不同类型的警报目标,以在条件不满足时发送通知。
我们介绍了 Databricks 平台内置的监控功能,例如管道事件日志,它使得管道拥有者可以轻松查询数据管道的健康状况、可审计性、性能,以及实时的数据质量。我们还看到,湖仓监控是一个强大且多功能的功能,它允许数据团队自动监控数据集的统计指标,并在阈值被突破时通知团队成员。我们还介绍了评估数据质量的技术,以确保整个管道中没有下游错误和不准确的情况。
最后,我们通过一个实际案例来结束这一章,演示了如何在遇到一个现实且非常常见的问题时,自动提醒数据团队——当一个计划任务运行超时。
恭喜你完成了本书的阅读!感谢你与我一起通过每一章进行这段旅程。我们已经涵盖了许多话题,但到目前为止,你应该为自己的成就感到骄傲。到现在为止,你应该已经建立了一个扎实的湖仓基础,接下来你可以继续在其上构建。实际上,我希望这本书能给你带来灵感,继续你的湖仓之旅,并构建出具有重大意义的现代数据应用。祝你好运,鼓励你保持学习!