使用 Terraform 管理 Grafana
通过 HCL 创建文件夹、数据源、仪表板、告警、团队和 service account。
文件 provisioning 涵盖数据源、仪表板和告警,但不包括用户、团队、service account 或权限。Terraform 涵盖所有这些,通过 API 针对任何运行中的 Grafana(自托管或 Grafana Cloud)工作,并保存一个状态文件,以便检测并纠正漂移。本指南设置 provider 并管理常见资源。
先决条件与 provider
你需要 Terraform 或 OpenTofu、一个可访问的 Grafana,以及一个其角色可以管理你所声明内容的 service account 令牌(创建方法参见认证指南)。不要把令牌写进代码:
terraform {
required_providers {
grafana = {
source = "grafana/grafana"
version = ">= 2.9.0" # pin to the version you have tested
}
}
}
variable "grafana_url" {
type = string
default = "http://localhost:3000"
}
variable "grafana_token" {
type = string
sensitive = true
}
provider "grafana" {
url = var.grafana_url
auth = var.grafana_token
}
通过环境变量传入令牌,使它永远不会落到文件里:
export TF_VAR_grafana_token='glsa_...'
terraform init
对于 Grafana Cloud,你可以在同一份配置中创建 stack 及其 service account。Grafana 文档展示了一种双 provider 的模式:一个使用 Cloud access policy 令牌来创建 stack(grafana_cloud_stack)和 service account(带其令牌的 grafana_cloud_stack_service_account)的 cloud provider,以及第二个以该 stack 为别名、使用该令牌的 provider。
文件夹与数据源
resource "grafana_folder" "apps" {
title = "Applications"
}
resource "grafana_data_source" "prometheus" {
type = "prometheus"
name = "Prometheus"
uid = "prometheus"
url = "http://prometheus:9090"
is_default = true
json_data_encoded = jsonencode({
timeInterval = "15s"
})
}
resource "grafana_data_source" "postgres" {
type = "postgres"
name = "Postgres"
uid = "postgres-main"
url = "postgres.internal:5432"
username = "grafana"
json_data_encoded = jsonencode({
database = "app"
sslmode = "require"
})
secure_json_data_encoded = jsonencode({
password = var.postgres_password
})
}
显式设置 uid 能让仪表板和告警规则在各环境之间保持可移植。通过 secure_json_data_encoded 传入的 secret 会被 Grafana 加密存储,但它们也会被写入 Terraform 状态,所以请将状态存放在加密且有访问控制的后端中。
仪表板
resource "grafana_dashboard" "webapp" {
folder = grafana_folder.apps.uid
config_json = file("${path.module}/dashboards/webapp.json")
overwrite = true
}
使用 fileset 加载整个目录:
resource "grafana_dashboard" "all" {
for_each = fileset("${path.module}/dashboards", "*.json")
folder = grafana_folder.apps.uid
config_json = file("${path.module}/dashboards/${each.value}")
}
仪表板 JSON 与你用于文件 provisioning 的是同一个文件,所以你可以在两种方式之间切换。把仪表板挂到文件夹时,较旧的 provider 版本引用文件夹的 id,而较新的则倾向于其 uid;请查阅你所固定版本的 provider 文档。
告警
Terraform 为告警的每个部分都提供了资源:
resource "grafana_contact_point" "slack" {
name = "team-slack"
slack {
url = var.slack_webhook_url
recipient = "#alerts"
}
}
resource "grafana_notification_policy" "default" {
group_by = ["alertname", "namespace"]
contact_point = grafana_contact_point.slack.name
group_wait = "30s"
group_interval = "5m"
repeat_interval = "4h"
}
规则组使用带 rule 块的 grafana_rule_group,其 data 条目与 provisioning YAML 相对应(查询、reduce、阈值)。静默时段和模板有 grafana_mute_timing 和 grafana_message_template。请在 provider 文档中查找确切的属性名称,因为告警资源在 provider 版本之间有过修订。
grafana_notification_policy 像文件 provisioning 一样管理整个策略树,所以请确保只有一个工具拥有它。
团队、service account 与权限
这些是文件无法表达的资源:
resource "grafana_team" "platform" {
name = "platform"
email = "platform@example.com"
}
resource "grafana_service_account" "ci" {
name = "ci-dashboards"
role = "Editor"
}
resource "grafana_service_account_token" "ci" {
name = "ci-token"
service_account_id = grafana_service_account.ci.id
}
resource "grafana_folder_permission" "apps" {
folder_uid = grafana_folder.apps.uid
permissions {
team_id = grafana_team.platform.id
permission = "Edit"
}
}
把生成的令牌当作 secret 输出,并将它推入你的 secret 管理器,而不是把它打印出来。
工作流
terraform fmt -check
terraform validate
terraform plan -out tfplan
terraform apply tfplan
在 CI 中,于拉取请求上运行 plan,合并时运行 apply。plan 会确切显示哪些仪表板、数据源和告警将会改变。
纳管已有的内容
如果有人在 UI 中构建了东西,请把它们导入状态,而不是重新创建:
terraform import grafana_folder.apps <folder-uid>
terraform import grafana_dashboard.webapp <dashboard-uid>
terraform import grafana_data_source.prometheus <data-source-uid>
较新的 Terraform 版本也可以使用 import 块。导入 ID 的格式可能因 provider 版本而异,所以请在资源文档中确认。
Terraform 与文件 provisioning 并用
不要用两者同时管理同一个资源。选定一个边界并坚持下去。常见的划分是:数据源和仪表板用文件 provisioning(启动时加载,不需要 API 令牌),团队、service account 和权限用 Terraform。另一种是全部用 Terraform,而 provisioning 文件仅限于服务器设置。
热门指南
-
将 Grafana 作为代码运维:全景图 哪些可以放在文件里,哪些需要 API,以及各部分如何组合在一起。 -
使用 grafana.ini 和环境变量配置 Grafana 服务器 了解配置文件的位置、覆盖的顺序,以及如何将设置保存在 Git 中。 -
从 JSON 文件 Provision 仪表板 从磁盘加载仪表板,从目录映射文件夹,并在不丢失变更的前提下创作仪表板。 -
Provision 告警规则、联系点与通知策略 用 YAML 定义整条告警管线,使其像代码一样被审查和版本化。 -
使用 Helm 和 Provisioning Values 在 Kubernetes 上部署 Grafana 通过 Helm chart 运行 Grafana,数据源、仪表板和设置都来自 values 文件。 -
从命令行重新加载、验证并排查 Provisioning 问题 找出 Grafana 加载了什么,无需重启即可重新加载,并修复常见的 provisioning 错误。