Grafana Grafana

从 JSON 文件 Provision 仪表板

从磁盘加载仪表板,从目录映射文件夹,并在不丢失变更的前提下创作仪表板。

12 min 中级

仪表板是 JSON 文档,这使它们很容易存放在 Git 中。仪表板 provider 告诉 Grafana 监视哪个文件夹,Grafana 会加载在那里找到的每个 JSON 文件,并持续检查变化。本指南涵盖 provider 文件、文件夹映射、仪表板 JSON 本身,以及一个创作仪表板而不会留下仅存在于 UI 中的编辑的工作流。

Provider 文件

provisioning/dashboards/providers.yaml:

apiVersion: 1

providers:
  - name: default
    orgId: 1
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30
    allowUiUpdates: false
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true

每个字段的作用:

  • name 是必需的,并且在所有 provider 中必须唯一。
  • options.path 是 Grafana 扫描 JSON 文件的目录。它必须存在于容器内。
  • updateIntervalSeconds 是 Grafana 多久重新扫描一次该路径。所有仪表板在启动时加载,此后无需重启即可拾取变更。
  • allowUiUpdates:为 false 时,被 provision 的仪表板无法从 UI 保存,这使 Git 保持权威。为 true 时,UI 的保存会保留在数据库中,直到源文件发生变化,那时文件会覆盖它们。
  • disableDeletion:为 true 时,删除文件不会从 Grafana 中删除其仪表板。如果 Git 应是事实,请保持为 false。
  • folder 或 folderUid 将所有仪表板放入一个指定的文件夹。没有它们时,仪表板会放到根目录。
  • foldersFromFilesStructure: true 将 path 的子目录映射为 Grafana 文件夹。使用它时请让 folder 和 folderUid 留空。不支持嵌套文件夹,所以子目录只能有一层。

按文件夹组织仓库

在 foldersFromFilesStructure: true 下,这样的布局:

dashboards/
├── infrastructure/
│   ├── node-exporter.json
│   └── kubernetes.json
└── applications/
    └── webapp.json

会创建两个 Grafana 文件夹,infrastructure 和 applications,其中包含相应的仪表板。如果你需要为不同的分组使用不同的设置,例如为一个”草稿”区域使用 allowUiUpdates: true 的 provider,请改用多个独立的 provider。

仪表板 JSON 要点

被 provision 的文件必须包含仪表板模型本身,并且顶层有 uid 和 title。不要把它包在 HTTP API 所使用的 {"dashboard": {...}, "overwrite": true} 信封中,这是导致”Dashboard title cannot be empty”错误的常见原因。

{
  "uid": "webapp-overview",
  "title": "Webapp Overview",
  "tags": ["webapp", "provisioned"],
  "schemaVersion": 39,
  "version": 1,
  "refresh": "30s",
  "time": { "from": "now-6h", "to": "now" },
  "templating": {
    "list": [
      {
        "name": "namespace",
        "type": "query",
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "query": "label_values(kube_pod_info, namespace)"
      }
    ]
  },
  "panels": [
    {
      "id": 1,
      "type": "timeseries",
      "title": "Request rate",
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "targets": [
        {
          "refId": "A",
          "expr": "sum(rate(http_requests_total{namespace=\"$namespace\"}[5m]))",
          "datasource": { "type": "prometheus", "uid": "prometheus" }
        }
      ]
    }
  ]
}

让仪表板作为代码时表现良好的准则:

  • 设置稳定的 uid。 Grafana 每次加载时都会更新具有该 UID 的仪表板。避免在多个文件中重复使用同一个 UID,或在同一文件夹中使用两次相同的标题。
  • 省略数字 id。 它由数据库分配,并且在不同实例之间不同。
  • 按 UID 引用数据源,与你 provision 的 uid 相匹配({"type": "prometheus", "uid": "prometheus"})。为分享而导出的仪表板可能包含诸如 ${DS_PROMETHEUS} 之类的占位符,你必须将其替换为真实的 UID,或替换为数据源模板变量。
  • 环境变量插值在仪表板 JSON 内部不起作用。 请使用仪表板模板变量来处理各环境的差异。
  • Grafana 13 在内部以更新的 schema 存储仪表板,并在加载时迁移较旧的 JSON,所以现有文件仍可工作。

在 UI 中创作,再把结果带回 Git

手写面板 JSON 很慢,所以大多数团队先在 UI 中起草再导出。一个保持文件为事实的工作流:

  1. 运行一个临时实例(前面指南中的 compose 配置),其中某个 provider 设有 allowUiUpdates: true。
  2. 在 UI 中构建或调整仪表板。
  3. 从仪表板的分享或导出菜单将其导出为 JSON。如果你希望数据源被替换为输入,请选择面向外部分享的导出选项,然后设置真实的 UID。
  4. 移除数字 id,设置稳定的 uid,并将文件提交到仓库文件夹中。
  5. 打开拉取请求。合并之后,生产环境的 Grafana 会拾取它,而那里的 allowUiUpdates: false 会阻止任何人产生分歧。

你也可以通过 API 获取已有的仪表板:

curl -s -u admin:$GRAFANA_ADMIN_PASSWORD \
  http://localhost:3000/api/dashboards/uid/webapp-overview \
  | jq '.dashboard | del(.id)' > dashboards/applications/webapp.json

一致地格式化 JSON(例如 jq -S .),使 diff 只显示真正的变更。

用代码生成仪表板

如果许多仪表板具有相同的形态,请生成 JSON,而不是复制。Grafana Foundation SDK(Go、TypeScript、Python、PHP、Java)可以从类型化代码构建仪表板,其输出就是你放入 provider 文件夹的同样的 JSON。本系列中的 Foundation SDK 指南对此有介绍。

在 CI 中验证

在错误到达 Grafana 之前将其捕获:

# valid JSON?
jq empty dashboards/**/*.json
# every file has a uid and title?
for f in dashboards/*/*.json; do
  jq -e '.uid and .title' "$f" >/dev/null || echo "missing uid/title: $f"
done
# duplicate uids?
jq -r '.uid' dashboards/*/*.json | sort | uniq -d

应用与验证

变更会在 updateIntervalSeconds 之内被拾取。要强制重新扫描:

curl -X POST -u admin:$GRAFANA_ADMIN_PASSWORD \
  http://localhost:3000/api/admin/provisioning/dashboards/reload

curl -s -u admin:$GRAFANA_ADMIN_PASSWORD \
  "http://localhost:3000/api/search?type=dash-db" | jq '.[] | {title, uid, folderTitle}'

如果某个仪表板没有出现,请检查来自 provisioning logger 的启动和重新扫描日志行。通常的原因是容器中不存在该路径、JSON 被包在 API 信封中,或两个文件共用了一个 uid。

热门指南