Beliebte Suchanfragen
//

Kubernetes-Konfiguration mit Jsonnet

8.4.2025 | 4 Minuten Lesezeit

YAML ist die gängige Konfigurationssprache im Container- und Kubernetes-Umfeld. Das hat zum einen Vorteile, denn YAML besitzt wenige Konstrukte und spezielles Markup, das man sich merken muss. Andererseits wirft die Arbeit mit YAML auch einige Probleme auf, die andere Konfigurationssprachen lösen.

So kommt es bei YAML auf die Einrückungstiefe an, und wenn YAML-Dateien über String-Templates verarbeitet werden, führt das zu dem Effekt, der sich in vielen Helm-Charts wiederfindet: YAML wird in ein anderes YAML-File eingesetzt und die Helm-Commands "indent" oder "nindent" müssen für die richtige Einrückungstiefe sorgen:

apiVersion: v1
kind: Service
metadata:
  name: {{ service_name }}
  labels:
    {{- include "service.labels" . | nindent 4 }}

Ändert sich die Struktur der YAML-Dateien, müssen gegebenenfalls alle solchen Werte angepasst werden – das ist weder schön, noch sonderlich wartungsfreundlich. Ähnliche Probleme bringen Conditionals und andere Konstrukte mit sich, die String Templates bieten. Letzten Endes ist die ganze Herangehensweise fehleranfällig und reduziert die Möglichkeiten für Wiederverwendung und Modularität von Kubernetes-Konfiguration.

Jsonnet fördert die Wiederverwendbarkeit

Einen Ausweg aus dem Dilemma eröffnen Konfigurationssprachen wie Jsonnet [1], die Konfiguration auf funktionale Weise managen und damit beispielsweise die Wiederverwendung von Config-Snippets ohne die oben beschriebenen Klimmzüge ermöglichen. Jsonnet ist eine Übermenge von JSON und bietet über die Markup-Funktionen hinaus einige wenige Merkmale einer richtigen Programmiersprache, zum Beispiel Funktionsaufrufe, Variablen und Conditionals.

Ein Projekt, das auf Jsonnet aufbaut und mit Wissen über Kubernetes-Objekte ausstattet, ist Tanka [2] von den Grafana Labs. Tanka bietet insbesondere bei Kubernetes-Projekten gegenüber YAML etliche Vorteile, zum Beispiele deutlich kürzere und damit besser lesbare Konfigurationsdateien. Wer als Team auf Tanka setzt, kann sich eine eigene Bibliothek bauen, um den Grad an Wiederverwendung noch zu steigern.

Ist das Tool installiert, lässt sich mit einem Aufruf ein Verzeichnis für Tanka initialisieren:

tk init

Damit werden diverse Dateien angelegt und auch die k8-libsonnet-Bibliothek installiert, um einfacher mit Kubernetes-Ressourcen zu arbeiten. Tanka unterstützt Environments, um etwa für Development und Production unterschiedliche Manifeste oder Werte zu verwenden. Standardmäßig installiert "tk init" ein Default-Environment, das sich in "environment/default" wiederfindet, wo wiederum die Datei "main.jsonnet" liegt, die den Einstiegspunkt darstellt – dazu muss übrigens das Binary von "jsonnet-bundler" installiert sein.

Diese JSON-Datei (und somit Jsonnet-Datei) startet im Kubernetes-Cluster einen Pod, wenn sie mit "tk apply" angewendet wird.

{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "nginx-pod",
    "labels": {
      "app": "nginx"
    }
  },
  "spec": {
    "containers": [
      {
        "name": "nginx",
        "image": "nginx:latest",
        "ports": [
          {
            "containerPort": 80
          }
        ]
      }
    ]
  }
}

Die folgenden Zeilen importieren den Pod in das Main-Manifest "main.jsonnet":

local pod = import 'pod.jsonnet';
{
  data: pod
}

Wenn alle Dateien korrekt sind, zeigt "tk show" das aus den Jsonnet-Dateien erzeugte YAML an. "tk diff" zeigt (wie kubectl) die potenziellen Änderungen, wenn das Tanka-Projekt auf einem Kubernetes-Cluster umgesetzt werden soll.

Natürlich ist an dieser Stelle noch nichts gewonnen, denn die Jsonnet-Datei des Pod ist sogar länger als ihr YAML-Gegenstück, aber etwas Refactoring vereinfacht die Jsonnet-Pod-Definition drastisch. Wir werden hier nicht alle Refactoring-Schritte reproduzieren, die das Tanka-Tutorial ausführlich durchgeht. Zum einen geht es dabei darum, aus den Kubernetes-Manifesten Funktionen zu machen, die es erlauben, Parameter zu übergeben; zum anderen vereinfacht die Library k8-libsonnet die Konfiguration. Am Ende steht die in Jsonnet mit wenigen Zeilen formulierte Definition eines kompletten Service (Beispiel aus dem Tanka-Tutorial)

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

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

  new(name, port):: {
    deployment: deploy.new(name, replicas=1, containers=[
      container.new("grafana/grafana")
      + container.withPorts(
          [port.new("ui", 3000)]
        ),
    ]),

    // Helper, um automatisch einen Service für ein Deployment anzulegen
    service: k.util.serv.util.serviceFor(self.deployment)
             + service.mixin.spec.withType("NodePort"),
  }
}

Helm-Support

Manche werden sich vielleicht fragen, was jetzt mit den schönen Helm-Charts passieren soll, die bekanntlich in YAML abgefasst sind. Die gute Nachricht ist: Sie lassen sich mit Tanka weiter nutzen und sogar besser parametrisieren als mit dem Helm-Tool selbst. Dazu ist es nötig, die Jsonnet-Helm-Bibliothek zu installieren. Anschließend können in einer Jsonnet-Datei das Helm-Chart geladen und anschließend die Helm-Values geändert werden, etwa so:

  grafana: helm.template("grafana", "./charts/grafana", {
    namespace: "monitoring",
    values: {
      persistence: { enabled: true }
    }
  })

Ein Wermutstropfen ist dabei, dass das nur funktioniert, wenn die Helm-Charts im Jsonnet-Projektverzeichnis installiert sind. Hierbei hilft das Tanka-Subcommand "tk tool charts", das die Charts an der passenden Stelle installiert.

Alternativ zu Tanka existieren noch weitere Tools wie Kubecfg, die auf der Jsonnet-Homepage zu finden sind. Daneben gibt es Jsonnet-Bindings für verschiedene Programmiersprachen/Umgebungen, angefangen bei Python, das vom Core-Projekt bereitgestellt wird, über Go, PHP, Ruby, Node.js und Rust als Community-Projekte.

Wer sich ohne irgendwelche Tools zu installieren mit Jsonnet vertraut machen möchte, findet einen interaktiven Playground mit praktischen Übungen auf der Jsonnet-Seite.

Fazit

Neben Tanka/Jsonnet gibt es noch weitere Ansätze, das "YAML-Problem" beim Management von Cloud-Konfiguration zu lösen, beispielsweise CUE [3], das aber etwas komplexer ist. Ansätze wie Pulumi gehen noch einen Schritt weiter und erlauben die Erstellung und Konfiguration von Kubernetes-Ressourcen mit verbreiteten Programmiersprachen wie Python, Java oder Go – mit der entsprechenden Freiheit für jeden Entwickler, damit sein eigenes Süppchen zu kochen. Tanka/Jsonnet trifft demgegenüber den Sweet Spot zwischen absoluter Freiheit, die leicht ins Chaos führt, und dem engen YAML-Korsett.

Einen Styleguide zu Jsonnet [4] bietet die KI-Firma Databricks, die nach eigenen Angaben über 1000 Jsonnet-Templates verwendet. Sie setzt dazu allerdings nicht auf Tanka, sondern auf einen eigenen Jsonnet-Compiler [5], der als Open Source verfügbar ist.

Links

[1] Jsonnet

[2] Tanka

[3] CUE

[4] Jsonnet Style Guide

[5] Writing a fast Jsonnet compiler

Beitrag teilen

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//
Jetzt für unseren Newsletter anmelden

Alles Wissenswerte auf einen Klick:
Unser Newsletter bietet dir die Möglichkeit, dich ohne großen Aufwand über die aktuellen Themen bei codecentric zu informieren.