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
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:
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.
Recommended by LinkedIn
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:
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.