Grafana Grafana

从命令行重新加载、验证并排查 Provisioning 问题

找出 Grafana 加载了什么,无需重启即可重新加载,并修复常见的 provisioning 错误。

10 min 中级

当一切都在文件中定义时,故障也存在于文件中:错误的缩进、缺失的 UID、容器中不存在的路径。Grafana 会告诉你它做了什么,但你得知道去哪里看。本指南涵盖 reload 端点、一些验证命令,以及你最常遇到的错误,这样你无需打开 UI 就能修复它们。

什么时候应用什么

资源何时读取如何重新应用
grafana.ini 和 GF_* 环境变量仅在启动时重启 Grafana
数据源启动时重启,或使用 reload 端点
仪表板启动时,之后每隔 updateIntervalSeconds等待,或使用 reload 端点
告警资源启动时重启(或按你所用版本的文档,通过 Admin API 重新运行 provisioning)
插件应用设置启动时重启,或使用 reload 端点

无需重启即可重新加载

这些管理端点会重新读取 provisioning 文件。它们使用 POST,以及用 Grafana 管理员用户进行的基本认证;API 令牌不适用于 Admin API:

BASE=http://localhost:3000
AUTH="admin:$GRAFANA_ADMIN_PASSWORD"

curl -X POST -u "$AUTH" $BASE/api/admin/provisioning/dashboards/reload
curl -X POST -u "$AUTH" $BASE/api/admin/provisioning/datasources/reload
curl -X POST -u "$AUTH" $BASE/api/admin/provisioning/plugins/reload
curl -X POST -u "$AUTH" $BASE/api/admin/provisioning/notifications/reload

成功的调用会返回一条说明配置已重新加载的 JSON 消息。不要公开暴露这些端点,因为它们需要管理员凭据。对于其他任何情况,重启始终是安全的。

检查 Grafana 实际加载了什么

将文件与 API 的视图进行比较:

# health
curl -s $BASE/api/health

# effective server settings (sensitive values masked)
curl -s -u "$AUTH" $BASE/api/admin/settings | jq '.paths, .server'

# data sources, with read-only flag for provisioned ones
curl -s -u "$AUTH" $BASE/api/datasources | jq '.[] | {name, uid, type, readOnly}'

# data source connectivity
curl -s -u "$AUTH" $BASE/api/datasources/uid/prometheus/health

# dashboards and folders
curl -s -u "$AUTH" "$BASE/api/search?type=dash-db" | jq '.[] | {title, uid, folderTitle}'
curl -s -u "$AUTH" $BASE/api/folders | jq '.[] | {title, uid}'

# alerting
curl -s -u "$AUTH" $BASE/api/v1/provisioning/alert-rules | jq '.[] | {uid, title}'
curl -s -u "$AUTH" $BASE/api/v1/provisioning/contact-points | jq '.[].name'
curl -s -u "$AUTH" $BASE/api/v1/provisioning/policies | jq .

如果你 provision 的某个东西没有出现在这些列表中,日志会告诉你原因。

阅读日志

Provisioning 消息来自名称以 provisioning 开头的 logger。调试时提高详细程度:

# docker compose
GF_LOG_LEVEL=debug docker compose up -d grafana
docker compose logs grafana | grep -i provision

# kubernetes
kubectl logs deploy/grafana -n monitoring -c grafana | grep -i provision

在消息中查找文件名。Grafana 会指明它解析失败的文件,或它拒绝的资源。

在文件到达 Grafana 之前检查它们

大多数错误都可以在 CI 中捕获:

# YAML syntax
yamllint grafana/provisioning

# JSON syntax and required fields
jq empty dashboards/**/*.json
jq -e '.uid and .title' dashboards/*/*.json >/dev/null

# duplicate dashboard UIDs
jq -r '.uid' dashboards/*/*.json | sort | uniq -d

# every provisioned file starts with apiVersion: 1
grep -L '^apiVersion: 1' grafana/provisioning/*/*.yaml

一个廉价的端到端测试是:在 CI 中用相同的文件启动一个一次性的 Grafana,等待 /api/health,并对上面的 API 输出进行断言。

常见问题与修复

数据源或仪表板没有出现。 provisioning 文件夹没有挂载到 Grafana 查找的位置。确认 [paths] provisioning(默认 /etc/grafana/provisioning)与你的挂载匹配,并且子文件夹名称恰好是 datasources、dashboards、alerting 和 plugins。

读取文件时出现”Permission denied”。 官方镜像以非 root 用户运行,所以挂载的文件必须对它可读。将它们设为全局可读(chmod -R a+rX),或在卷上设置匹配的所有权。

仪表板失败,提示”Dashboard title cannot be empty”。 JSON 被包在 API 信封中({"dashboard": {...}})。被 provision 的文件必须包含仪表板模型本身。

仪表板加载了,但面板显示找不到数据源。 面板引用了一个没有被 provision 的 UID,或者文件中仍有来自导出的 ${DS_...} 占位符。在数据源文件中设置 uid,并在仪表板 JSON 中使用同样的 UID。

两个仪表板互相争夺,或其中一个不断被还原。 它们共用了一个 uid,或者在同一文件夹中标题相同。让每个 UID 在整个安装范围内唯一。如果某次 UI 编辑不断被覆盖,那是文件在获胜,这是设计使然,除非 provider 设有 allowUiUpdates: true。

删除了文件,但仪表板仍在。 provider 设有 disableDeletion: true,或者该仪表板被手动移到了某个文件夹。通过 API 删除它,或关闭 disableDeletion。

在多实例设置中,数据源的更改被忽略。 对于多个 Grafana 副本,只有当文件的 version 与已存储的相同或更高时,实例才会更新数据源。更改文件时请提高 version。

环境变量没有被替换。 插值适用于值,而不是键。它在仪表板 JSON、告警规则注解、查询模型、静默时段或模板中不起作用。检查该变量确实已在容器中设置(docker compose exec grafana env | grep NAME)。

告警导入冲突。 存在一个因手动编辑而产生的、具有相同 UID 或名称的资源。删除手工创建的那个,或将其加入文件中的某个 delete... 块,然后重启。

重建之后密钥不再工作。 secret_key 变了,所以已存储的密钥无法解密。恢复原来的密钥,或重新 provision 需要凭据的数据源。

管理员密码没有改变。 admin_password 只在首次创建数据库时应用。使用 grafana cli admin reset-admin-password,或重新创建该卷。

Helm 或 Operator 资源没有同步。 对于 chart,检查 sidecar 容器日志中的标签或命名空间不匹配。对于 Operator,对该资源运行 kubectl describe 并阅读它的状态条件。

可靠的变更流程

  1. 在分支中更改文件。
  2. CI 运行上面的 linter,并用这些文件启动一个一次性的 Grafana。
  3. 合并。交付机制重启或调谐 Grafana。
  4. 检查 /api/health 和验证命令。
  5. 如果出现问题,回退这次提交。因为文件就是事实,回退会恢复之前的状态。

热门指南