作者:来自 Elastic Jeffrey Rengifo
只需在共享的 HCL 库中定义一次你的黄金信号(golden signals)面板,然后使用 for_each 为每个团队生成对应的仪表板,同时内置配置漂移检测和 Git 回滚能力。
Elastic 提供了 Kibana Dashboards API 和原生 Terraform resource,用于以代码方式管理仪表板。该能力在 Elastic 9.4 中以技术预览(technical preview)形式推出,并在 Elastic 9.5 中正式发布(GA)。
你只需要在 HCL 中定义一次黄金信号(golden signals)面板库,然后通过 for_each 从该库为每个团队生成一个仪表板。当你需要修改错误阈值、面板布局或查询时,只需提交一个 Pull Request,就可以一次性更新所有团队的仪表板。
如果发生配置漂移(drift)或出现问题,你可以通过 Git 回滚进行恢复。
为什么手动管理可观测性仪表板在大规模场景下会失效
大型组织通常会拥有数百个仪表板。各团队会创建类似的面板,并通过 Kibana UI 进行维护。
当需要进行小范围修改时(例如重命名面板、修复字段、添加新的错误阈值),没有简单的方法可以将更改应用到所有仪表板中。你要么逐个打开每个仪表板,在 UI 中进行编辑;要么导出 NDJSON,执行字符串替换,然后重新导入。
仪表板现在也是代码
Elastic 提供了类型化的 Kibana Dashboards API 和原生的elasticstack_kibana_dashboard Terraform resource。你可以在 HCL 文件中定义一个仪表板,然后像管理普通代码一样管理版本和变更。
黄金信号仪表板:一个定义,供所有团队使用
平台团队负责维护一个基于四个黄金信号(golden signals)构建的标准仪表板:延迟(latency)、流量(traffic)、错误(errors)和饱和度(saturation)。
每个团队都应该获得这个标准仪表板,同时部分团队可以添加一两个自己的面板。
我们的目标是:
-
只维护一个标准定义;
-
从该定义生成每个团队的仪表板;
-
一次修改即可同步到所有团队。
前置条件
-
运行 Elastic 9.4 或更高版本的 Elastic Cloud 部署或自建集群,或者 Elastic Cloud Serverless 项目;
-
已安装 Terraform;
-
一个 Elasticsearch API key。
本文使用的完整 Terraform 配置、初始化脚本(seed script)以及捕获的 Terraform plan 输出,都可以在配套代码仓库中获取。
配置 Elastic Terraform provider
在其他 Terraform 文件旁边创建一个 [provider.tf](https://github.com/Delacrobix/Observability-dashboards-as-code-one-standard-across-every-team-with-Terraform/blob/main/provider.tf "provider.tf"):
`
1. terraform {
2. required_providers {
3. elasticstack = {
4. source = "elastic/elasticstack"
5. version = "~> 0.11"
6. }
7. }
8. }
10. variable "elasticsearch_endpoint" {
11. type = string
12. }
14. variable "elasticsearch_api_key" {
15. type = string
16. sensitive = true
17. }
19. variable "kibana_endpoint" {
20. type = string
21. }
23. variable "kibana_api_key" {
24. type = string
25. sensitive = true
26. }
28. provider "elasticstack" {
29. elasticsearch {
30. endpoints = [var.elasticsearch_endpoint]
31. api_key = var.elasticsearch_api_key
32. }
33. kibana {
34. endpoints = [var.kibana_endpoint]
35. api_key = var.kibana_api_key
36. }
37. }
`AI写代码
通过本地 terraform.tfvars 文件提供你的凭据(并将其添加到 .gitignore 中,以确保密钥不会进入代码仓库):
`
1. elasticsearch_endpoint = "https://...es.region.cloud.es.io"
2. elasticsearch_api_key = "..."
3. kibana_endpoint = "https://...kb.region.cloud.es.io"
4. kibana_api_key = "..."
`AI写代码
只要该 API key 在目标 space 中具有仪表板写入权限(dashboard write privileges),你可以将同一个 API key 同时用于 elasticsearch_api_key 和 kibana_api_key。
然后初始化工作目录:
`terraform init` AI写代码
在 HCL 中定义单个团队的 Kibana 仪表板
首先,为单个团队创建一个基准仪表板。面板位于一个 48 列的网格布局中,每个面板都是一个内联配置的 Lens 可视化对象。
对于 KPI 卡片,使用 config_json(它支持辅助指标和数值颜色配置);对于时间序列图表,使用 xy_chart_config。
将基础资源添加到新的 [dashboards.tf](https://github.com/Delacrobix/Observability-dashboards-as-code-one-standard-across-every-team-with-Terraform/blob/main/dashboards.tf "dashboards.tf") 文件中:
``
1. resource "elasticstack_kibana_dashboard" "golden_signals" {
2. title = "Golden Signals - payments"
3. description = "Latency, traffic, errors"
4. query = { language = "kql", text = "" }
5. refresh_interval = { pause = false, value = 60000 }
6. time_range = { from = "now-15m", to = "now" }
8. panels = [
9. {
10. type = "vis"
11. grid = { x = 0, y = 0, w = 12, h = 5 }
12. config_json = jsonencode({
13. type = "metric"
14. data_source = {
15. type = "esql"
16. query = "FROM logs-payments-* | STATS `5xx errors` = COUNT(CASE(status >= 500, 1, null))"
17. }
18. metrics = [{ type = "primary", column = "5xx errors" }]
19. })
20. },
21. # More panels follow the same shape: other metric tiles, xy_chart_config line charts, and a breakdown datatable. See the companion repo for the full file.
22. ]
23. }
``AI写代码
每个面板都会设置类型和网格位置,然后选择一种图表类型。
KPI 卡片会将完整的 Lens 配置序列化到 config_json 中;ES|QL 查询位于 data_source 下,而指标列则通过 metrics[*].column 中的名称进行引用。
仪表板的时间选择器(time picker)已经会自动限定 ES|QL 面板的数据范围,因此查询中无需显式添加 @timestamp 范围过滤条件。
使用 terraform plan 预览 Kibana 仪表板变更
运行 terraform plan 查看 Terraform 将要创建的内容:
`terraform plan` AI写代码
plan 输出会列出新的 elasticstack_kibana_dashboard.golden_signals resource,以及它将要设置的每个属性:包括顶层仪表板字段,以及每个面板对应的条目,其中包含面板的网格位置、图表类型和数据源。
`
1. Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
2. + create
4. Terraform will perform the following actions:
6. # elasticstack_kibana_dashboard.golden_signals will be created
7. + resource "elasticstack_kibana_dashboard" "golden_signals" {
8. + description = "Latency, traffic, errors"
9. + title = "Golden Signals - payments"
10. + query = { language = "kql", text = "" }
11. + refresh_interval = { pause = false, value = 60000 }
12. + time_range = { from = "now-15m", to = "now" }
13. + panels = [
14. # Every panel described in full: KPI tiles (config_json),
15. # line charts (xy_chart_config), and the breakdown datatable.
16. ]
17. }
19. Plan: 1 to add, 0 to change, 0 to destroy.
`AI写代码
检查 plan 是在将任何内容发布到 Kibana 之前的最后一步。
现在不要执行 apply。下一节会扩展该文件,添加按团队生成的仪表板,然后通过一次 terraform apply 部署所有内容。
从共享面板库生成每个团队的可观测性仪表板
在基础仪表板之上,每个团队都会获得一组标准面板,同时可以选择添加少量自己的面板。为每个团队硬编码一个 resource 的方式无法扩展。
相反,可以将面板库和团队映射定义为 locals,然后使用 for_each 构建仪表板。
面板库中的每个条目都会描述一种图表类型、一个标题以及所需的数据;resource 会根据 chart_type 自动生成对应的 Lens 配置块(指标卡片使用 config_json,折线图使用 xy_chart_config)。
将 [dashboards.tf](https://github.com/Delacrobix/Observability-dashboards-as-code-one-standard-across-every-team-with-Terraform/blob/main/dashboards.tf "dashboards.tf") 的内容替换为:
``
1. locals {
2. panel_library = {
3. errors = {
4. chart_type = "metric"
5. title = "Error rate"
6. esql_query_tpl = "FROM {idx} | STATS `5xx errors` = COUNT(CASE(status >= 500, 1, null))"
7. esql_column = "5xx errors"
8. }
9. saturation = {
10. chart_type = "metric"
11. title = "Saturation (CPU)"
12. # Saturation reads from the metrics TSDB, so this query is not parameterized by {idx}.
13. esql_query_tpl = "TS metrics-payments-* | STATS avg_cpu = AVG(cpu.pct)"
14. esql_column = "avg_cpu"
15. }
16. latency = {
17. chart_type = "xy"
18. title = "Latency p95"
19. x_json = jsonencode({
20. operation = "date_histogram"
21. field = "@timestamp"
22. suggested_interval = "auto"
23. })
24. y_json = jsonencode({
25. operation = "percentile"
26. field = "duration_ms"
27. percentile = 95
28. })
29. }
30. # ... more entries (traffic, cart_value) in the companion repo.
31. }
33. teams = {
34. payments = {
35. index = "logs-payments-*"
36. panels = ["errors", "saturation", "latency", "traffic"]
37. }
38. checkout = {
39. index = "logs-checkout-*"
40. panels = ["errors", "cart_value", "latency", "traffic"]
41. }
42. }
43. }
45. resource "elasticstack_kibana_dashboard" "golden_signals" {
46. for_each = local.teams
47. title = "Golden Signals - ${each.key}"
48. description = "Latency, traffic, and errors for the ${each.key} service"
49. query = { language = "kql", text = "" }
50. refresh_interval = { pause = false, value = 60000 }
51. time_range = { from = "now-15m", to = "now" }
53. sections = [
54. {
55. title = "KPIs"
56. grid = { y = 0 }
57. collapsed = false
58. panels = [
59. for i, p in [for q in each.value.panels : q if local.panel_library[q].chart_type == "metric"] : {
60. type = "vis"
61. grid = { x = (i % 4) * 12, y = 0, w = 12, h = 5 }
62. config_json = jsonencode({ ... }) # one metric tile per panel; see the companion repo for the full config
63. }
64. ]
65. },
66. {
67. title = "Trends"
68. grid = { y = 1 }
69. collapsed = false
70. panels = [
71. for i, p in [for q in each.value.panels : q if local.panel_library[q].chart_type == "xy"] : {
72. type = "vis"
73. grid = { x = (i % 3) * 16, y = 0, w = 16, h = 10 }
74. vis_config = { by_value = { xy_chart_config = { ... } } }
75. }
76. ]
77. },
78. # A third "Breakdown" section holds the request-by-status datatable. See the companion repo.
79. ]
80. }
``AI写代码收起代码块
添加一个团队只需要在 teams 中增加一条配置。向所有团队添加一个面板,只需要在 panel_library 中增加一条配置,并在每个团队中添加一个引用。
完整配置(包括数据源 ES|QL 查询、指标、图层、坐标轴默认设置以及图例位置)都位于 dashboards.tf 中。
饱和度(saturation)面板会通过 ES|QL 的 TS 命令查询 metrics 数据流,该命令专为 TSDB(时间序列数据库)设计。要使查询正常工作,与 metrics-payments-* 匹配的数据流必须使用 time_series 模式,因此配置中还提供了一个索引模板(metrics_tsdb.tf),用于启用该模式。
使用 terraform apply 将代码形式的仪表板应用到 Kibana
运行 terraform plan 确认将创建两个团队仪表板(payments 和 checkout),然后执行 apply:
`terraform apply` AI写代码
打开 Kibana,你会看到每个团队都有一个黄金信号(Golden Signals)仪表板,并且每个仪表板都由各自的索引模式(index pattern)支持。
GitOps 流程中的代码化仪表板:在 Pull Request 中审查变更
现在,仪表板已经成为版本控制中的一种资源,就像其他基础设施一样。
你可以编辑面板库或某个团队的配置选择,然后创建一个 Pull Request。审查者可以查看 Terraform plan 的差异,并了解哪些仪表板会发生变化。
例如,假设你将 panel_library.errors.esql_query_tpl 中的“严重错误”(critical error)阈值从 status >= 500 收紧为 status >= 503。
运行 terraform plan 后,可以看到该变更会同时应用到两个团队:
注意:完整输出请参见
terraform-plan-update.txt。
对 panel_library.errors 的一次修改,会传播到所有引用它的团队。
Pull Request 合并后,就可以运行 terraform apply。
执行 apply 完成后,刷新 Kibana 中的仪表板,新阈值即可生效:
检测仪表板漂移并通过 Git 回滚
如果有人通过 UI 编辑了仪表板,下一次运行 terraform plan 时会显示差异,因为代码状态和实际运行状态已经不再匹配。
要实际体验这一点,请在 Kibana 中打开 Golden Signals - payments 仪表板,将 Latency p95 面板重命名为 Latency p95 (EDITED),然后保存该仪表板。
然后运行 terraform plan:
Terraform 会从实际运行中的仪表板读取面板标题,将其与代码中的定义进行比较,并提出撤销 UI 重命名的变更。
你可以决定保留该修改(更新代码以匹配实际状态),或者通过运行 terraform apply 将其回滚。
你可以提交新版本,也可以使用 Git 回滚一个或多个版本。
回顾之前的示例:如果你重新打开修改 panel_library.errors 的 Pull Request,该修改用于扩大错误阈值范围并添加更清晰的标题,那么 git diff dashboards.tf 会用两行内容展示完整变更意图:
每个引用 errors 的团队都会在下一次 terraform apply 时获取新的阈值,而回滚该提交会一次性将所有团队的变更恢复。
总结
手动管理 Kibana 可观测性仪表板无法在超过几个团队的规模下继续扩展。通过 Kibana Dashboards API 和 Terraform,你可以一次定义标准,通过共享面板库组合每个团队的仪表板,并在 Pull Request 中审查每一次变更。
一次编辑即可影响所有团队,并且可以通过回滚提交来恢复变更。
本文提出的文件结构只是众多组织方式中的一种,你可以根据仪表板之间共享信息的程度,以不同方式组织你的仪表板。
后续步骤
原文:Kibana observability dashboards as code with Terraform — Elastic Observability Labs