Grafana TankaFlexible, reusable and concise configuration for Kubernetes
Edit page
IntroductionInstallation
Tutorial
OverviewRefresher on deployingUsing JsonnetParameterizingAbstractionKubernetes libraryk8s-libsonnetInstallationUsing itFull exampleEnvironments
Writing Jsonnet
Libraries
Advanced features
References
Frequently asked questionsKnown issues

Kubernetes library

The last section has shown that using a library for creating Kubernetes objects can drastically simplify the code you need to write. However, there is a huge amount of different kinds of objects and the Kubernetes API is evolving (and thus changing) quite rapidly.

Writing and maintaining such a library could be a full-time job on it's own. Luckily, it is possible to generate such a library from the Kubernetes OpenAPI specification! Even better, it has already been done for you.

k8s-libsonnet

The library is called k8s-libsonnet (replacing the discontinued ksonnet-lib), currently available at https://github.com/jsonnet-libs/k8s-libsonnet.

Note: The ksonnet project has been abandoned, the library is not maintained anymore. However, the community backed by Grafana Labs has picked up on this with the k8s-libsonnet library.

As k8s-libsonnet has broken compatibility in a few places with ksonnet-lib (for good reason), we have instrumented the widely used ksonnet-util library with a compatibility layer to improve the developer and user experience: https://github.com/grafana/jsonnet-libs/tree/master/ksonnet-util

If you do not have any strong reasons against it, just adopt the wrapper as well, it will ease your work. Many of the original ksonnet-util enhancements have already made their way into k8s-libsonnet.

The docs for k8s-libsonnet library can be found here: https://jsonnet-libs.github.io/k8s-libsonnet/

Installation

Like every other external library, k8s-libsonnet can be installed using jsonnet-bundler. However, Tanka already did this for you during project creation (tk init):

$ tk init
  └─ jb install github.com/jsonnet-libs/k8s-libsonnet/1.21@main github.com/grafana/jsonnet-libs/ksonnet-util

This created the following structure in /vendor:

vendor
├── github.com
│   ├── grafana
│   │   └── jsonnet-libs
│   │       └── ksonnet-util
│   │           ├── ...
│   │           └── kausal.libsonnet # Grafana's wrapper
│   └── jsonnet-libs
│       └── k8s-libsonnet
│           └── 1.21
│               ├── ...
│               └── main.libsonnet   # k8s-libsonnet entrypoint
├── 1.21 -> github.com/jsonnet-libs/k8s-libsonnet/1.21
└── ksonnet-util -> github.com/grafana/jsonnet-libs/ksonnet-util

Info: The vendor/ is the location for external libraries, while lib/ can be used for your own ones. Check import paths for more information.

Aliasing

Because of how jb works, the library can be imported as github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet. Most external libraries (including our wrapper) expect it as a simple k.libsonnet (without the package prefix).

To support both, Tanka automatically created an alias file for you: /lib/k.libsonnet that just imports the actual library, exposing it under this alternative name as well.

More information: This works, because import behaves like copy-pasting. So the contents of k8s-libsonnet/1.21 are "copied" into our new file, making them behave exactly the same.

Using it

First we need to import it in main.jsonnet:

- local k = import "kubernetes.libsonnet";
+ local k = import "github.com/grafana/jsonnet-libs/ksonnet-util/kausal.libsonnet";
  local grafana = import "grafana.jsonnet";
  local prometheus = import "prometheus.jsonnet";
  { /* ... */ }

Note: ksonnet-util imports literal k.libsonnet, so aliasing is a must here. This works, because /lib and /vendor are automatically searched for libraries, and k.libsonnet can be found in /lib due to aforementioned aliasing.

Now that we have installed the correct version, let's use it in /environments/default/grafana.jsonnet instead of our own helper:

local k = import "github.com/grafana/jsonnet-libs/ksonnet-util/kausal.libsonnet";

{
  // use locals to extract the parts we need
  local deploy = k.apps.v1.deployment,
  local container = k.core.v1.container,
  local port = k.core.v1.containerPort,
  local service = k.core.v1.service,
  // defining the objects:
  grafana: {
    // deployment constructor: name, replicas, containers
    deployment: deploy.new(name=$._config.grafana.name, replicas=1, containers=[
      // container constructor
      container.new($._config.grafana.name, "grafana/grafana")
      + container.withPorts( // add ports to the container
          [port.new("ui", $._config.grafana.port)] // port constructor
        ),
    ]),

    // instead of using a service constructor, our wrapper provides

Full example

Now that creating the individual objects does not take more than 5 lines, we can merge it all back into a single file (main.jsonnet) and take a look at the whole picture:

local k = import "github.com/grafana/jsonnet-libs/ksonnet-util/kausal.libsonnet";

{
  _config:: {
    grafana: {
      port: 3000,
      name: "grafana",
    },
    prometheus: {
      port: 9090,
      name: "prometheus"
    }
  },

  local deployment = k.apps.v1.deployment,
  local container = k.core.v1.container,
  local port = k.core.v1.containerPort,
  local service = k.core.v1.service,

  prometheus: {

That's a pretty big improvement, considering how verbose and error-prone it was before!


PreviousAbstractionNextEnvironments