+
We are excited to announce Grafana Labs is to acquire Kausal. Read more

Developing Grafana Dashboards with Jsonnet

Twitter profile image
on Jan 17, 2018

We are currently developing KLUMPS, a set of preconfigured Kubernetes dashboards for Grafana. To define these dashboards we are using Jsonnet, a JSON templating language. This allows us to define panel templates that we can compose and parameterize, like g.queryPanel('up{job="node-exporter"}') which outputs tens of lines of JSON for a time-series chart (we’ll write another blog post about why and how we are using Jsonnet). These templates are ideal for large repetitive configurations like Grafana dashboards. This blog post describes the development setup we use to achieve a short feedback loop while working on the dashboards.

Slow Feedback on the Cluster

To monitor our own service we use Prometheus and Grafana. Both are defined as a Kubernetes deployments with their configurations inlined as a ConfigMap. This includes the dashboards, which are a big JSON blob inside our Grafana deployment YAML file. To test a dashboard modification I needed to kubectl apply that deployment configuration and wait for the configMap change to be picked up. This works really well for the occasional edit, but when writing new dashboards you want a faster feedback loop.

Reloading Dashboards

For that I’m running a local Grafana and make use of their new feature to load configurations from disk on a regular interval. The current master branch is basically a pre-5.0 version, which organizes its configuration a bit differently–calling it “provisioning”: The following dashboard load configuration needs to be placed in conf/provisioning/dashboards instead of conf/dashboards.

# dashboards.yml
- name: 'default'
  org_id: 1
  folder: ''
  type: file
  options:
    folder: /tmp/dashboards

After starting your local Grafana, it will look in the /tmp/dashboards directory for new dashboards, or reload modified ones.

Developing Dashboards with Jsonnet

Our preconfigured dashboards are organized so that the output can be used as a Kubernetes ConfigMap to be used by Grafana, which means the output contains all dashboards. To work on a single dashboard, we need to process the output. Say we want to work on the k8s-cluster-rsrc-use dashboard. The dashboards can be compiled using the jsonnet command:

jsonnet klumps.libsonnet

The output looks something like this:

{
   "k8s-cluster-rsrc-use.json": {
      "annotations": {
         "list": [ ]
      },
      "editable": true,
      "gnetId": null,
      "graphTooltip": 0,
      "hideControls": false,
      "id": 1,
      ...

There are two problems with this output: 1. Grafana does not understand groups of dashboards in one file (we only want the dashboard k8s-cluster-rsrc-use anyway), and 2. the dashboard has an id set which interferes with Grafana’s reloading logic.

Luckily we can post-process the JSON with jq, to select only the dashboard we want, and to delete the id field:

jsonnet klumps.libsonnet | jq '.["k8s-cluster-rsrc-use.json"] | del(.id)' \
    > /tmp/dashboards/klumps.json

The result is a single dashboard JSON file placed where Grafana has been configured to look for dashboards. Roughly five seconds later Grafana picks up the change and you can find the new dashboard in the dashboard list, or you can reload the tab if you are looking at the dashboard already (note that Grafana uses the dashboard title as the URL for the dashboard, if you change the title, you need to adapt the URL).

Kausal's mission is to enable software developers to better understand the behaviour of their code. We combine Prometheus monitoring, log aggregation and OpenTracing-compatible distributed tracing into a hosted observability service for developers.
Contact us to get started.