Dashboards
Dashboards is the core feature of Grafana and of course something that you can manage through the operator.
To view the entire configuration that you can do within dashboards, look at our API documentation.
Dashboard management
You can configure dashboards as code in many different ways.
- json
- gzipJson
- URL
- Jsonnet
Json
A pure JSON representation of your Grafana dashboard. Normally you would create your dashboard manually within Grafana, when you have come up with how you want the dashboard to look like, you export it as JSON, grab the JSON using the export function in grafana and put inside the GrafanaDashboard CR.
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-sample
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: "grafana"
json: >
{
"id": null,
"title": "Simple Dashboard",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
gzipJson
It’s just like JSON but instead of adding pure JSON to the dashboard CR you add a gzipped representation. This allows you to do really big dashboards while not hitting the etcd maximum request size of 1,5 MiB.
Assuming a dashboard is already saved in dashboard.json
, the steps below describe how you can prepare a CR.
cat dashboard.json | gzip | base64 -w0
Take the output and put it in your GrafanaDashboard CR, for example:
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-gzipped
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
gzipJson: |-
H4sIAAAAAAAAA4WQQU/DMAyF7/0VVc9MggMgcYV/AOKC0OQubmM1jSPH28Sm/XfSNJ1WcaA3f+/l+dXnqk5fQ6Z5qf3eubt5VlKHCTXvNAaH9RtE2zKI2fQnCgFNsxihj8n39V3mqD/zQwMyXE004ol95q3wMaIsEhpSaPMTlT0WasngK3sVdlN6By4uUi8Q7AezUwpJeig4gEe3ajItTfM5T5l0wuNUwfNx82RLg9nLhTeZXW4iAu2GVHcVNPEtByX2tyuzJtgJRrslrygHKJ3WsZhuCkq+X8c6ivrXDd6zwrLrX3vZP/3PY1yuHHcWR/hEiSlmutpzEQ5XdF+IIz+Uzpeq+gWtMMT1HwIAAA==
URL
Probably the easiest way to get started to add dashboards to your Grafana instances.
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-from-grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
url: "https://grafana.com/api/dashboards/1860/revisions/30/download"
NOTE: You don’t have to rely on Grafana Dashboard registry for this, any URL reachable by the operator would work.
Jsonnet
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-jsonnet
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: "grafana"
jsonnet: |
local grafana = import 'grafonnet/grafana.libsonnet';
local dashboard = grafana.dashboard;
local row = grafana.row;
local singlestat = grafana.singlestat;
local prometheus = grafana.prometheus;
local template = grafana.template;
dashboard.new(
'JVM',
tags=['java'],
)
.addTemplate(
grafana.template.datasource(
'PROMETHEUS_DS',
'prometheus',
'Prometheus',
hide='label',
)
)
.addTemplate(
template.new(
'env',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current, env)',
label='Environment',
refresh='time',
)
)
.addTemplate(
template.new(
'job',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current{env="$env"}, job)',
label='Job',
refresh='time',
)
)
.addTemplate(
template.new(
'instance',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current{env="$env",job="$job"}, instance)',
label='Instance',
refresh='time',
)
)
.addRow(
row.new()
.addPanel(
singlestat.new(
'uptime',
format='s',
datasource='Prometheus',
span=2,
valueName='current',
)
.addTarget(
prometheus.target(
'time() - process_start_time_seconds{env="$env", job="$job", instance="$instance"}',
)
)
)
)
Plugins
Plugins is a way to extend the grafana functionality in dashboards and datasources.
Plugins can be installed to grafana instances managed by the operator and be defined in both datasources and dashboards.
They cannot be installed using external grafana instances due to how the installation of plugins is done at the start of the instance using environment variables which is a built in feature in grafana.
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: keycloak-dashboard
spec:
instanceSelector:
matchLabels:
dashboards: grafana
plugins:
- name: grafana-piechart-panel
version: 1.3.9
# NOTE: the json block below is incomplete as it's not the main focus of the example
json: >
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
}
Look here for more examples on how to install plugins
Content cache duration
To not constantly perform requests to external URL every time a dashboard reconcile or a resync period expires we save URLs in a cache in the operator. This cache is saved in the status field of the dashboard CR.
By default this cache is 24h
long, you can change this value by setting contentCacheDuration manually per dashboard.
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-from-url
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
url: "https://grafana.com/api/dashboards/7651/revisions/44/download"
contentCacheDuration: 48h
Remember, depending on where you get your dashboards you might become rate limited if you have multiple dashboards with relatively short contentCacheDuration
or if all the requests happens at the same time.
Dashboard uid management
Whenever a dashboard is imported into a Grafana, it gets assigned a random uid
unless it’s hardcoded in dashboard’s code. Random uid
is undesirable from the operator’s perspective as it would create the need to track those uids across Grafana instances.
To mitigate the scenario, if uid
is not hardcoded, the operator will insert the value taken from CR’s metadata.uid
(this value is automatically generated by Kubernetes itself for all resources).
Select the folder where the dashboard will be deployed
By default, a dashboard will appear in a Folder with the name of the namespace
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafana-folder-default
labels:
dashboards: "grafana"
spec:
# the folder will be "default" by default
instanceSelector:
matchLabels:
dashboards: "grafana"
json: |
{
"id": null,
"title": "Folder Without Folder Defined",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
If you want to put the dashboard in a specific folder, you have two choices:
- Use an
GrafanaFolder
resouce as a reference:
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaFolder
metadata:
name: folder-ref
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
title: "Folder"
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafana-folderref
labels:
dashboards: "grafana"
spec:
folderRef: folder-ref
instanceSelector:
matchLabels:
dashboards: "grafana"
json: |
{
"id": null,
"title": "Folder Ref With Folder",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
- Use a static UID as a folder reference
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafana-folderuid
labels:
dashboards: "grafana"
spec:
folderUID: ec94e912-02f4-463b-9968-1f5f7db3531f
instanceSelector:
matchLabels:
dashboards: "grafana"
json: |
{
"id": null,
"title": "Folder UID With Folder",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
"list": []
},
"annotations": {
"list": []
},
"refresh": "5s",
"schemaVersion": 17,
"version": 0,
"links": []
}
Custom folders
Note: This method is not recommended. Prefer to use the GrafanaFolder CR and
folderRef
field to declare a folder instead.
In a standard scenario, the operator would use the namespace a CR is deployed to as a folder name in grafana. folder
field can be used to set a custom folder name:
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-with-custom-folder
spec:
folder: "Custom Folder"
instanceSelector:
matchLabels:
dashboards: "grafana"
url: "https://raw.githubusercontent.com/grafana-operator/grafana-operator/master/examples/dashboard_from_url/dashboard.json"
Note: the field folder is ignored when
folderUID
orfolderRef
is already present in the GrafanaDashboard declaration.
Dashboard customization by providing environment variables
Will be pleasant for scenarios when you would like to extend the behaviour of jsonnet generation by parametrizing it with runtime Env vars:
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: grafanadashboard-jsonnet
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: "grafana"
envs:
- name: API_VERSION
value: "1.0.0"
- name: ENV_FROM_CM -- just example, such cm and secrets are not provided by vendor
valueFrom:
configMapKeyRef:
name: custom-grafana-dashboard-cm
key: GRAFANA_URL
- name: ENV_FROM_SECRET
valueFrom:
secretKeyRef:
name: custom-grafana-dashboard-secrets
key: PROMETHEUS_USERNAME
envFrom: -- just example, such cm and secrets are not provided by vendor
- configMapRef:
name: custom-grafana-dashboard-cm
- secretRef:
name: custom-grafana-dashboard-secrets
jsonnet: |
local grafana = import 'grafonnet/grafana.libsonnet';
local dashboard = grafana.dashboard;
local row = grafana.row;
local singlestat = grafana.singlestat;
local prometheus = grafana.prometheus;
local template = grafana.template;
local labelFromEnvs = std.extVar('API_VERSION');
dashboard.new(
'JVM',
tags=['java'],
)
.addTemplate(
grafana.template.datasource(
'PROMETHEUS_DS',
'prometheus',
'Prometheus',
hide='label',
)
)
.addTemplate(
template.new(
'env',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current, env)',
label='Environment',
refresh='time',
)
)
.addTemplate(
template.new(
'job',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current{env="$env"}, job)',
label='Job',
refresh='time',
)
)
.addTemplate(
template.new(
'instance',
'$PROMETHEUS_DS',
'label_values(jvm_threads_current{env="$env",job="$job"}, instance)',
label='Instance',
refresh='time',
)
)
.addRow(
row.new()
.addPanel(
singlestat.new(
'uptime',
format='s',
datasource='Prometheus',
span=2,
valueName='current',
)
.addTarget(
prometheus.target(
'time() - process_start_time_seconds{env="$env", job="$job", instance="$instance", apiVersion="$labelFromEnvs"}',
)
)
)
)
Providing runtime to build jsonnet dashboards
This feature provides the ability to pass your jsonnet project with all own or external runtime-required libs/dependencies required in runtime to build your dashboard.
It bridges the signature of the jsonnet generation command jsonnet -J path/to/libs target.jsonnet
and Grafana Operator Dashboard CRD.
To do this, there are 3 parameters:
jPath
- Jsonnet local libs path, must be the same as in your local jsonnet project. Optional part.fileName
- Jsonnet file name which must be built. Required part.gzipJsonnetProject
- Gzip archived project in a byte array representation. Only .tar.gz files are supported. Required part.
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: my-favorite-dashboard-with-internal-dependencies
spec:
resyncPeriod: 30s
instanceSelector:
matchLabels:
dashboards: "grafana"
envs:
- name: API_VERSION
value: "1.0.0"
- name: ENV_FROM_CM -- just example, such cm and secrets are not provided by vendor
valueFrom:
configMapKeyRef:
name: custom-grafana-dashboard-cm
key: GRAFANA_URL
- name: ENV_FROM_SECRET
valueFrom:
secretKeyRef:
name: custom-grafana-dashboard-secrets
key: PROMETHEUS_USERNAME
envFrom: -- just example, such cm and secrets are not provided by vendor
- configMapRef:
name: custom-grafana-dashboard-cm
- secretRef:
name: custom-grafana-dashboard-secrets
jsonnetLib:
jPath:
- "vendor"
fileName: "overview.jsonnet"
gzipJsonnetProject: |-
{{- (.Files.Get "dashboards.tar.gz") | b64enc | nindent 6 }}
[Example documentation](../examples/dashboard_with_custom_folder/readme).