使用 Grafana Operator 管理 Grafana
将实例、文件夹、仪表板、数据源和告警规则声明为 Kubernetes 自定义资源。
Grafana Operator 把每一种 Grafana 资源都变成一个 Kubernetes 对象。GrafanaDashboard 是一个清单,GrafanaDatasource 是一个清单,operator 通过 API 针对 Grafana 实例对它们进行调谐。这天然契合 Flux 和 Argo CD 之类的 GitOps 工具,并且适用于集群内外的 Grafana 实例,包括 Grafana Cloud。所有资源都使用 apiVersion: grafana.integreatly.org/v1beta1。
安装 operator
用 Helm 从它的 OCI chart 安装(请查看 operator 的安装文档以获取当前的 chart 位置和版本,并固定版本):
helm upgrade -i grafana-operator \
oci://ghcr.io/grafana/helm-charts/grafana-operator \
--namespace grafana --create-namespace \
--version <operator-version>
operator 的文档也介绍了 Kustomize 安装方式。使用 Flux 时,把同一个 chart 放进一个 HelmRelease 中。
描述实例
实例可以是受管的(由 operator 部署 Grafana),也可以是外部的(连接到已经存在的 Grafana)。受管实例:
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
name: grafana
namespace: grafana
labels:
dashboards: "grafana"
spec:
config:
log:
mode: console
security:
admin_user: root
admin_password: secret # example only; see below
外部实例,例如 Grafana Cloud stack,指向一个 URL,并从 Secret 获取令牌:
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
name: my-stack
namespace: grafana
labels:
dashboards: my-stack
spec:
external:
url: https://my-stack.grafana.net/
apiKey:
name: grafana-cloud-credentials
key: GRAFANA_CLOUD_INSTANCE_TOKEN
spec.config 块使用与 grafana.ini 相同的 section 和键。不要在清单里写真实的密码。operator 文档展示了如何从 Secret 读取管理员凭据。
实例上的标签很重要:其他每个资源都通过 instanceSelector.matchLabels,使用这些标签来选择它的目标实例。
文件夹
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaFolder
metadata:
name: applications
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
title: Applications
uid: applications # set a stable UID so other resources can reference it
如果省略 title,它默认取 metadata.name。只要其他资源会用 folderRef 指向该文件夹,就请设置 spec.uid,因为没有它时,调谐器可能会接管一个同名的已有文件夹。
数据源
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
name: prometheus
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
datasource:
name: prometheus
type: prometheus
access: proxy
url: http://prometheus-service:9090
isDefault: true
jsonData:
timeInterval: 5s
凭据通过 valuesFrom 来自同一命名空间中的 Secret,所以它们永远不会出现在清单中:
apiVersion: v1
kind: Secret
metadata:
name: credentials
type: Opaque
stringData:
PROMETHEUS_USERNAME: grafana
PROMETHEUS_PASSWORD: change-me
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
name: prometheus-secured
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
valuesFrom:
- targetPath: basicAuthUser
valueFrom:
secretKeyRef:
name: credentials
key: PROMETHEUS_USERNAME
- targetPath: secureJsonData.basicAuthPassword
valueFrom:
secretKeyRef:
name: credentials
key: PROMETHEUS_PASSWORD
datasource:
name: prometheus-secured
type: prometheus
access: proxy
basicAuth: true
url: http://prometheus-service:9090
secureJsonData:
basicAuthPassword: ${PROMETHEUS_PASSWORD}
不要把凭据放在 url 中。像对待任何 Kubernetes Secret 一样,用 SOPS 或 External Secrets 加密或同步该 Secret。
仪表板
GrafanaDashboard 可以从多种来源获取它的 JSON,这是 operator 最大的优势:
# inline JSON
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: simple
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: "grafana"
folderRef: applications
json: >
{ "uid": "simple", "title": "Simple Dashboard", "panels": [] }
# from a ConfigMap
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
configMapRef:
name: dashboard-definition
key: json
# from a URL, such as grafana.com or a raw Git file
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
url: "https://grafana.com/api/dashboards/1860/revisions/30/download"
# from an OCI artifact
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
oci:
reference: ghcr.io/team-a/dashboards:v1.4.7
path: board.json
pullSecretRef:
name: ghcr-pull
你会用到的其他字段:
folderRef按名称指向一个GrafanaFolder,folderUID则指向已有文件夹的 UID。gzipJson将仪表板保存为经 base64 编码的 gzip(cat dashboard.json | gzip | base64 -w0),用于对清单来说过大的仪表板。variables覆盖模板变量,例如为每个环境设置namespace或数据源。resyncPeriod和contentCacheDuration控制多久重新应用一次内容,以及获取到的 URL 缓存多久。
告警规则
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaAlertRuleGroup
metadata:
name: webapp-alerts
spec:
folderRef: applications
instanceSelector:
matchLabels:
dashboards: "grafana"
interval: 5m
rules:
- uid: webapp-down
title: Webapp targets down
condition: C
for: 5m
noDataState: NoData
execErrState: Error
data:
- refId: A
# the Prometheus query step, as in the alerting guide
- refId: C
# the threshold expression step
每个规则组都需要一个文件夹和至少一条规则。数据步骤使用与 provisioning 文件相同的结构,所以你可以复用告警指南中的 YAML。operator 还有用于联系点的资源;请查看它的示例以了解当前的列表。
运维它
kubectl get grafanas,grafanafolders,grafanadatasources,grafanadashboards -A
kubectl describe grafanadashboard simple -n grafana
describe 显示资源的状态条件,说明最近一次同步到 Grafana 是否成功,失败的话原因是什么。常见原因:instanceSelector 没有匹配到任何实例、令牌错误,或者 Secret 位于错误的命名空间。
Operator 还是文件 provisioning?
当你已经在 Kubernetes 上运行 GitOps、希望每个团队把自己的仪表板和告警作为独立对象来拥有,或者管理 Grafana Cloud 或多个实例时,请使用 Operator。当你只有一个 Grafana 和少数几个资源时,请使用 Helm chart 的 provisioning values,因为这不需要额外的控制器。两者可以共存,但要让一个工具拥有每个资源。
热门指南
-
将 Grafana 作为代码运维:全景图 哪些可以放在文件里,哪些需要 API,以及各部分如何组合在一起。 -
使用 grafana.ini 和环境变量配置 Grafana 服务器 了解配置文件的位置、覆盖的顺序,以及如何将设置保存在 Git 中。 -
从 JSON 文件 Provision 仪表板 从磁盘加载仪表板,从目录映射文件夹,并在不丢失变更的前提下创作仪表板。 -
Provision 告警规则、联系点与通知策略 用 YAML 定义整条告警管线,使其像代码一样被审查和版本化。 -
使用 Helm 和 Provisioning Values 在 Kubernetes 上部署 Grafana 通过 Helm chart 运行 Grafana,数据源、仪表板和设置都来自 values 文件。 -
从命令行重新加载、验证并排查 Provisioning 问题 找出 Grafana 加载了什么,无需重启即可重新加载,并修复常见的 provisioning 错误。