Why I Switched to KCL: A YAML Survivor's Story

Why I Switched to KCL: A YAML Survivor's Story

When it comes to Kubernetes configurations, I’ve had more than my fair share of frustration. It all started with YAML. Sure, YAML’s supposed to be "human-readable",  but for me, it’s been a nightmare of hunting for extra spaces, struggling with indentation errors, and, being colorblind even with an IDE tuned, squinting to tell the difference between barely distinguishable colors.

There are few things more frustrating than staring at a YAML file, knowing there’s a typo somewhere but having absolutely no idea where it is. Let’s just say I’ve spent far too many hours scrolling through YAML code only to realize I missed a single space. And yes, tools like yamllint can help find those mistakes, but sometimes it’s so “helpful” that it’s overwhelming, throwing up a storm of alerts for minor issues and making it hard to spot the real errors. And there’s no getting around it - YAML’s lack of logic and strict indentation make managing Kubernetes resources at scale a daunting task.

After way too many late-night troubleshooting sessions, I finally came across KCL. With its structured approach, KCL language was like a breath of fresh air. It’s built to make Kubernetes (and not only, literally any yaml) configurations more logical, more modular, and almost enjoyable to work with. So, here’s the scoop on KCL and why it might just be the YAML antidote you’ve been looking for.



What is KCL?

KCL is a high-level, declarative language specifically designed for creating and managing Kubernetes configurations without all the hassle. Instead of drowning in YAML’s verbosity and repetitive code, KCL gives you the tools to write clean, readable configurations that are actually enjoyable to manage.

Why KCL is Better Than YAML

  • Readable, Less Verbose Code: No more fighting against YAML’s rigid, space-sensitive indentation. KCL is structured to cut down on redundant code and make configurations actually human-readable.
  • Logical Constructs: KCL has built-in logic, so you can add loops, conditionals, and reusable functions—things YAML just doesn’t do. This means you can set up complex configurations without bending over backward.
  • Type Safety: In KCL, types actually matter, which means fewer runtime surprises. You define types, and KCL validates them, catching issues before they get deployed. No more mysterious bugs popping up just because you missed a quote.
  • Composable and Merge-Friendly: KCL’s support for merging configurations makes it way easier to create modular setups across environments (dev, staging, prod), without hunting down “the right file” every time.
  • Killer Integration: KCL plugs right into tools like kubectl, Helm, and Kustomize, making it easy to drop into your current workflow.


An Example of How KCL Beats YAML

Here’s a side-by-side comparison of how a simple deployment looks in YAML vs. KCL.

YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app-container
          image: my-app-image:latest
          ports:
            - containerPort: 80        


KCL:

app = {
    apiVersion: "apps/v1",
    kind: "Deployment",
    metadata: {
        name: "my-app",
        labels: {app: "my-app"}
    },
    spec: {
        replicas: 3,
        selector: {matchLabels: {app: "my-app"}},
        template: {
            metadata: {labels: {app: "my-app"}},
            spec: {
                containers: [{
                    name: "app-container",
                    image: "my-app-image:latest",
                    ports: [{containerPort: 80}]
                }]
            }
        }
    }
}        

In KCL, you get a more readable, maintainable structure with real logical flow. Plus, KCL files don’t leave you guessing if you’ve got your indentation right or an extra space somewhere.


How KCL Works with Your Favorite Kubernetes Tools

One of the best things about KCL is how it integrates with the tools you’re probably already using: kubectl, Helm, and Kustomize. Each of these tools has plugins for KCL, so you can still use your go-to workflow—only now, it’s way less frustrating.

1. KCL kubectl Plugin

The kubectl plugin for KCL lets you apply KCL-based configurations directly, skipping the YAML middleman.

Why It’s Awesome

Direct Integration: You can run

kubectl kcl run -f deploy.yaml        

and it just works.

Error Checking: KCL checks for errors as you go, saving you from the “forgot-a-space” nightmare.

Debugging: You’ll get detailed error messages that actually help you solve the problem instead of leaving you in the dark.


2. KCL Helm Plugin

With Helm, you get templated Kubernetes resources, which is great - except when you’re still stuck managing YAML values. Enter the KCL Helm plugin, which lets you use KCL files as Helm values, adding flexibility without the YAML headaches.

How It Works

Imagine we have a workload chart with a Service and a Deployment. Using the Helm KCL plugin, we can apply additional customization to the resources. In this example, we’re going to add a custom annotation to all Deployment resources within the Helm chart using KCL.

Setting Up the KCL Configuration File

First, we create a kcl-run.yaml file that defines our KCL transformation. Here’s an example of how to configure the file to add a "managed-by": "helm-kcl-plugin" annotation to each Deployment resource within our chart.

# kcl-run.yaml
apiVersion: krm.kcl.dev/v1alpha1
kind: KCLRun
metadata:
  name: set-annotation
spec:
  # The KCL source code that applies the transformation
  source: |
    [resource | {if resource.kind == "Deployment": metadata.annotations: {"managed-by" = "helm-kcl-plugin"}} for resource in option("items")]

repositories:
  - name: workload
    path: ./workload-charts        

In this setup:

  • apiVersion and kind are set as required by KCL, where KCLRun indicates we’re running a KCL transformation.
  • source contains the KCL code that will iterate over each resource and, if it’s a Deployment, add an annotation.
  • repositories points to the path where Helm can find the workload chart.

This configuration will apply the annotation to all Deployment resources within the workload chart.

Applying the KCL Transformation with Helm

To deploy this transformation using Helm, run the following command:

helm template workload ./workload-charts -f kcl-run.yaml | kubectl apply -f -        

This command uses the kcl-run.yaml configuration, transforms the Helm chart resources with KCL, and then outputs the final YAML to kubectl for deployment.

Example Output

The resulting configuration after the transformation might look like this:

# Generated by KCL with Helm
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/instance: workload
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: workload
    app.kubernetes.io/version: 0.1.0
    helm.sh/chart: workload-0.1.0
  name: workload
spec:
  ports:
  - name: www
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app.kubernetes.io/instance: workload
    app.kubernetes.io/name: workload
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/instance: workload
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: workload
    app.kubernetes.io/version: 0.1.0
    helm.sh/chart: workload-0.1.0
  name: workload
  annotations:
    managed-by: helm-kcl-plugin
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: workload
      app.kubernetes.io/name: workload
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: workload
        app.kubernetes.io/name: workload
    spec:
      containers:
      - image: "nginx:alpine"
        name: frontend        

In this output:

  • The Service remains unchanged as per our KCL configuration.
  • The Deployment now includes an annotation "managed-by": "helm-kcl-plugin" as we specified in kcl-run.yaml.


3. KCL Kustomize Plugin

Kustomize is all about customizing Kubernetes configurations without using templating, and KCL makes it even easier by handling layered configurations and reusable modules.

With the KCL Kustomize plugin, you can have overlays that are written in KCL instead of YAML.

In this example below, we’ll use KCL to adjust settings for different environments based on conditions.

Define a main.k file for our KCL configuration:

# main.k
apiVersion = "apps/v1"
kind = "Deployment"
metadata = {
    name = "ldap"
    labels.app = "ldap"
}
spec = {
    replicas = 1
    if option("env") == "prod": replicas = 6  # Override replicas for prod
    selector.matchLabels = metadata.labels
    template.metadata.labels = metadata.labels
    template.spec.containers = [
        {
            name = metadata.name
            image = "osixia/openldap:1.1.11"
            args = ["--copy-service"]
            volumeMounts = [{ name = "ldap-data", mountPath = "/var/lib/ldap" }]
            ports = [{ containerPort = 80, name = "openldap" }]
        }
    ]
    template.spec.volumes = [
        {
            name = "ldap-data"
            emptyDir = {}
            if option("env") == "prod":
                emptyDir = None
                gcePersistentDisk = {
                    readOnly = True
                    pdName = "ldap-persistent-storage"
                }
        }
    ]
}        

Generate Base and Prod Configurations with KCL:

To see the configuration differences, we’ll use the kcl command-line tool to specify different environments.

Generate the base configuration:

kcl main.k -o deployment.yaml        

Generate the prod configuration:

kcl main.k -o prod-deployment.yaml -D env=prod        

View Configuration Differences:

This diff shows the differences applied by the env=prod setting, changing the replica count and volume configuration using the diff command:

diff deployment.yaml prod-deployment.yaml        

Expected output:

8c8
<   replicas: 1
---
>   replicas: 6
30c30,33
<         emptyDir: {}
---
>         emptyDir: null
>         gcePersistentDisk:
>           readOnly: true
>           pdName: ldap-persistent-storage        

A Real-World Workflow: Switching from YAML to KCL with Kubernetes

Here’s a step-by-step on how I’d use KCL in a typical Kubernetes workflow:

Define a Base Config in KCL: Set up a reusable, modular config in KCL. No more cluttered YAML files for every environment.

Layer with Kustomize Overlays: Use KCL to manage different environments (like dev, staging, and prod) with Kustomize overlays, which are more flexible and readable than YAML.

Parameterize with Helm: Instead of static values.yaml files, generate dynamic values in KCL. Use Helm with

helm install -f values.kcl        

and you’re ready to go.

Apply with kubectl: Use

kcl app.k | kubectl apply -f -        

and KCL will validate, check, and apply your configurations, catching errors early.




Final Thoughts

KCL has been a game-changer for me. I’m not constantly hunting for tiny formatting issues, I don’t have to worry about color in my IDE, and I can finally spend more time on meaningful work instead of troubleshooting YAML quirks. If you’re tired of YAML’s limitations and want something that respects your time and sanity, give KCL a try. It could save you hours and a lot of frustration.




Links

To view or add a comment, sign in

More articles by Oleksii Tsyganov

Insights from the community

Others also viewed

Explore topics