Grafana Tanka

repository·main·Indexed 25 days ago

https://github.com/grafana/tanka

A Jsonnet-based tool for Kubernetes cluster management designed to replace YAML. Grafana Tanka provides a programmable way to define Kubernetes resources and Helm charts, offering features such as reusable libraries, boilerplate reduction, and the ability to preview changes via `tk diff` before applying them to production environments.

Tokens
24.2K
Snippets
75
Records
157
Agent score
83%

What's inside Grafana Tanka

  1. Overview of Grafana Tanka

    main
    Grafana Tanka is an open-source tool designed to manage Kubernetes applications using the Jsonnet language. It provides a more expressive, reusable, and concise alternative to YAML for defining Kubernetes resources. Tanka is used to deploy production environments like Grafana Cloud and leverages powerful diffing capabilities to ensure reliable deployments by showing exact changes before they are applied.
  2. Jsonnet Syntax Overview

    main

    Jsonnet is a data templating language used by Tanka to express Kubernetes deployments. It is a superset of JSON and supports comments, local variables, and exported objects.

    • Comments: Use // for line comments and /* ... */ for block comments.
    • Local Variables: Defined using local name = value;. These are not exported.
    • Exported Objects: The top-level object (or the only non-local entity) is what is returned.
    • Unexported Fields: Use :: instead of : to define a field that is hidden from the final output.
    // Line comment
    /* Block comment */
    
    // a local variable (not exported)
    local greeting = "hello world!";
    
    // the exported/returned object
    {
      foo: "bar", // string
      bar: 5, // int
      baz: false, // bool
      list: [1,2,3], // array
      // object
      dict: {
        nested: greeting, // using the local
      },
      hidden:: "incognito!" // an unexported field
    }
  3. Understand the Tanka directory structure

    main

    Tanka projects are organized around environments and dependency management files. The core structure typically includes:

    • environments/: Contains environment-specific configurations.
    • lib/: Local libraries specific to the project.
    • vendor/: External libraries installed via jb (jsonnet-bundler).
    • jsonnetfile.json: Defines direct dependencies.
    • jsonnetfile.lock.json: Contains exact versions and SHA256 hashes of all dependencies.

    Note: The vendor/ directory can be added to .gitignore as long as jsonnetfile.lock.json is committed to source control.

  4. Understand the difference between Tanka and ksonnet

    main

    Tanka is designed as a fully compatible, drop-in replacement for the core ksonnet workflow, specifically the show, diff, and apply commands.

    Unlike ksonnet, Tanka does not include:

    • A rich code generator for CLI-based editing of Kubernetes objects.
    • Built-in dependency management.
    • Complex abstraction layers.

    Tanka prioritizes being a minimal tool, so these features are intentionally omitted and are not planned for future addition.

  5. Understand the difference between Tanka and Helm

    main

    Tanka uses Jsonnet instead of Helm's string-based YAML templating.

    Key differences include:

    • Structure Awareness: Helm uses string templating which is unaware of YAML syntax, making debugging difficult. Tanka uses Jsonnet, which is a superset of JSON and understands data structures.
    • Extensibility: Helm charts often require modification to support new parameters. Tanka/Jsonnet allows you to use mixing (patching and deep-merging) to apply changes on top of library outputs without modifying the original source.
  6. Create Kubernetes helper utilities

    main

    You can reduce boilerplate in your service definitions by creating a kubernetes.libsonnet file containing constructor functions for standard Kubernetes resources like Deployment and Service. This allows you to focus on the specific configuration (like container images and ports) rather than the structural requirements of the Kubernetes API.

    // /environments/default/kubernetes.libsonnet
    {
      deployment: {
        new(name, containers):: {
          apiVersion: "apps/v1",
          kind: "Deployment",
          metadata: {
            name: name,
          },
          spec: {
            selector: { matchLabels: { 
              name: name,
            }},
            template: {
              metadata: { labels: { 
                name: name,
              }},
              spec: { containers: containers }
            }
          }
        }
      }
    }
  7. Structure Jsonnet output for Tanka

    main

    Tanka invokes the Jsonnet compiler on a file named main.jsonnet. The compiler evaluates the code into a single JSON object. Because kubectl expects a YAML stream of individual manifests rather than a single nested tree, Tanka traverses the resulting JSON object to find valid Kubernetes manifests.

    An object is identified as a valid Kubernetes manifest if it contains both apiVersion and kind keys.

  8. Run Tanka acceptance tests

    main

    Acceptance tests for Tanka cover end-to-end use cases, such as creating a new Tanka environment and pushing it to an ephemeral Kubernetes cluster.

    To run these tests, you must have the Dagger CLI >= 0.11 installed. The command must be executed from the root directory of the project.

    make acceptance-tests
  9. Manage dependencies with jsonnetfile.json and jsonnetfile.lock.json

    main

    Tanka uses jb (jsonnet-bundler) to manage external libraries in the vendor/ directory.

    • jsonnetfile.json: The source of truth for direct dependencies. It should only include packages that are directly required by your project; recursive dependencies are handled automatically.
    • jsonnetfile.lock.json: Automatically generated on every run of jsonnet-bundler. It contains the exact versions and sha256 hashes of all packages in vendor/ to ensure reproducible builds.

    Best Practice: Both jsonnetfile.json and jsonnetfile.lock.json must be checked into source control to ensure jb install produces identical environments across different machines.

  10. Install Tanka (tk)

    main

    Tanka is distributed as a single binary called tk. It includes the Jsonnet compiler but requires kubectl and diff to be available on your $PATH. It is also recommended to install jb (Jsonnet Bundler) and helm for full functionality.

    macOS

    Install via Homebrew:

    brew install tanka

    ArchLinux

    Install via an AUR helper (e.g., yay):

    • From source: yay tanka
    • Pre-compiled binary: yay tanka-bin

    Binary Installation (Linux/macOS)

    Download the appropriate binary from GitHub Releases, place it in your $PATH (e.g., /usr/local/bin/tk), and make it executable.

    Build from Source (Go)

    If you have a Go toolchain, use go install or manual compilation via make.

    # macOS
    brew install tanka
    
    # ArchLinux (Source)
    yay tanka
    
    # ArchLinux (Binary)
    yay tanka-bin
    
    # Linux amd64
    sudo curl -Lo /usr/local/bin/tk https://github.com/grafana/tanka/releases/latest/download/tk-linux-amd64
    sudo chmod a+x /usr/local/bin/tk
    
    # Linux arm / arm64
    sudo curl -Lo /usr/local/bin/tk https://github.com/grafana/tanka/releases/latest/download/tk-linux-arm
    sudo curl -Lo /usr/local/bin/tk https://github.com/grafana/tanka/releases/latest/download/tk-linux-arm64
    sudo chmod a+x /usr/local/bin/tk
    
    # macOS Binary
    sudo curl -Lo /usr/local/bin/tk https://github.com/grafana/tanka/releases/latest/download/tk-darwin-amd64
    sudo chmod a+x /usr/local/bin/tk
    
    # Go install
    go install github.com/grafana/tanka/cmd/tk@latest
    
    # Manual build
    git clone https://github.com/grafana/tanka
    cd tanka
    make install
  11. Use top-level arguments for variable apiServer

    main

    You can use Jsonnet top-level arguments to make environment configuration (like apiServer or namespace) dynamic. This is useful for local test clusters where the API server address changes.

    Define your main.jsonnet as a function that accepts these arguments, then pass the values using the --tla-str flag with the tk CLI.

    // environments/minikube-test-setup/main.jsonnet
    function(apiServer) {
      apiVersion: 'tanka.dev/v1alpha1',
      kind: 'Environment',
      metadata: {
        name: 'minikube-test-setup',
      },
      spec: {
        apiServer: apiServer,
        namespace: 'monitoring',
      },
      data: { /* ... */ },
    }
    tk apply --tla-str apiServer=https://127.0.0.1:4758 environments/minikube-test-setup