KCL and KPM: The Art of Managing Kubernetes Configurations

The KCL Programming Language
DevOps.dev
Published in
6 min readApr 24, 2023

--

A Practical Guide to Simplifying Kubernetes Configuration Management Using KCL and KPM.

What is KCL

KCL is an open-source, constraint-based record and functional programming language. It leverages mature programming language technology and practices to facilitate the writing of many complex configurations. KCL is designed to improve modularity, scalability, and stability around configuration, simplify logic writing, speed up automation, and create a thriving extension ecosystem. To learn more about specific KCL usage scenarios, please refer to the KCL website. This blog will not go into too much detail about that.

What is KPM

KPM is the KCL package manager. KPM downloads your KCL package’s dependencies, compiles your KCL packages, makes packages, and uploads them to the kcl package registry.

Why use KCL

When we manage the Kubernetes resources, we often maintain it by hand, or use Helm and Kustomize tools to maintain our YAML configurations or configuration templates, and then apply the resources to the cluster through kubectl tools. However, as a “YAML engineer”, maintaining YAML configuration every day is undoubtedly trivial and boring, and prone to errors. For example as follows:

apiVersion: apps/v1
kind: Deployment
metadata: ... # Omit
spec:
selector:
matchlabels:
cell: RZ00A
replicas: 2
template:
metadata: ... # Omit
spec:
tolerations:
- effect: NoSchedules
key: is-over-quota
operator: Equal
value: 'true'
containers:
- name: test-app
image: images.example/app:v1 # Wrong ident
resources:
limits:
cpu: 2 # Wrong type. The type of cpu should be str
memory: 4Gi
# Field missing: ephemeral-storage
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: is-over-quota
operator: In
values:
- 'true'
  • The structured data in YAML is untyped and lacks validation methods, so the validity of all data cannot be checked immediately.
  • YAML has poor programming ability. It is easy to write incorrect indents and has no common code organization methods such as logical judgment. It is easy to write a large number of repeated configurations and difficult to maintain.
  • The design of Kubernetes is complex, and it is difficult for users to understand all the details, such as the toleration and affinity fields in the above configuration. If users do not understand the scheduling logic, it may be wrongly omitted or superfluous added.

Therefore, KCL expects to solve the following problems in Kubernetes YAML resource management:

  • Use production-level high-performance programming language to write code to improve the flexibility of configuration, such as conditional statements, loops, functions, package management and other features to improve the ability of configuration reuse.
  • Improve the ability of configuration semantic verification at the code level, such as optional/required fields, types, ranges, and other configuration checks.
  • Provide the ability to write, combine, and abstract configuration blocks, such as structure definition, structure inheritance, constraint definition, etc.

How to use KCL to generate and manage Kubernetes resources

First, you can visit the KCL Quick Start to download and install KCL according to the instructions, and then prepare a Kubernetes environment.

Generate Kubernetes manifests

We can write the following KCL code and name it main.k. KCL is inspired by Python. Its basic syntax is very close to Python, which is easy to learn. The configuration mode is simple, k [: T] = v, where k denotes the configured attribute name, v denotes the configured attribute value and : T denotes an optional type annotation.

apiVersion = "apps/v1"
kind = "Deployment"
metadata = {
name = "nginx"
labels.app = name
}
spec = {
replicas = 3
selector.matchLabels = metadata.labels
template.metadata.labels = metadata.labels
template.spec.containers = [
{
name = metadata.name
image = "${metadata.name}:1.14.2"
ports = [{ containerPort = 80 }]
}
]
}

In the above KCL code, we declare the apiVersion, kind, metadata, spec and other variables of a Kubernetes Deployment resource, and assign the corresponding contents respectively. In particular, we will assign metadata.labels fields are reused in spec.selector.matchLabels and spec.template.metadata.labels field. It can be seen that, compared with YAML, the data structure defined by KCL is more compact, and configuration reuse can be realized by defining local variables.

We can get a Kubernetes YAML file by executing the following command line

kcl main.k

The output is

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

Of course, we can use KCL together with kubectl and other tools. Let’s execute the following commands and see the result:

$ kcl main.k | kubectl apply -f -
deployment.apps/nginx-deployment configured

It can be seen from the command line that it is completely consistent with the deployment experience of using YAML configuration and kubectl application directly.

Check the deployment status through kubectl

$ kubectl get deploy

NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 15s

How to use KPM to manage Kubernetes resources

You can get kpm from the kpm github release and set the kpm binary path to the environment variable PATH.

# KPM_INSTALLATION_PATH is the path of the `kpm` binary.
export PATH=$KPM_INSTALLATION_PATH:$PATH

Use the following command to ensure that you install kpm successfully.

kpm --help

Set environment variables

You need to set an environment variable KPM_HOME to hold the KCL packages downloaded by kpm.

Note: kpm does not support downloading external packages into the current kcl package directory, so make sure that '$KPM_HOME' is not in the same directory as the current KCL package.

# The directory to save the packages downloaded by Kpm. 
export KPM_HOME="/user/xxx/xxx/path"

To ensure that KCLVM can find the packages downloaded by kpm, you need to set the environment variables $KCLVM_VENDOR_HOME and point it to $KPM_HOME for KCLVM after downloading KCLVM.

export KCLVM_VENDOR_HOME=$KPM_HOME

Init an empty KCL package

First, create an empty folder for the KCL package and go into that folder.

mkdir my_package # create an empty folder 'my_package'
cd my_package # go into the folder 'my_package'

Create a new kcl package named my_package.

kpm init my_package

kpm will create two kcl package configuration files: kcl.mod and kcl.mod.lock in the directory where you executed the command.

- my_package
|- kcl.mod
|- kcl.mod.lock
|- # You can write your kcl program directly in this directory.

kcl.mod.lock is the file generated by kpm to fix the dependency version. Do not modify this file manually.

kpm initializes kcl.mod for an empty project as shown below:

[package]
name = "my_package"
edition = "0.0.1"
version = "0.0.1"

Add a dependency from Git Registry

If you need to use the KCL model in Konfig to write the kcl program.

kpm add -git https://github.com/awesome-kusion/konfig.git -tag v0.0.1

You can see that kpm adds the dependency you just added to kcl.mod.

[package]
name = "my_package"
edition = "0.0.1"
version = "0.0.1"

[dependencies]
# 'konfig' is the package name
# If you want to use the contents of this package,
# you need to write the import statment with the package name 'konfig' as the prefix.
konfig = { git = "https://github.com/awesome-kusion/konfig.git", tag = "v0.0.1" }

Write a KCL program that uses the content in konfig

Create the main.k file in the current package.

- my_package
|- kcl.mod
|- kcl.mod.lock
|- main.k # Your KCL program.

And write the following into the main.k file.

import konfig.base.pkg.kusion_kubernetes.api.apps.v1 as apps

demo = apps.Deployment {
metadata.name = "nginx-deployment"
spec = {
replicas = 3
selector.matchLabels = {
app = "nginx"
}
template.metadata.labels = {
app = "nginx"
}
template.spec.containers = [
{
name = "nginx"
image = "nginx:1.14.2"
ports = [
{containerPort = 80}
]
}
]
}
}

Use the kpm compile the kcl package

You can use kpm to compile the main.k file you just wrote.

kcl main.k -S demo

If you get the following output, congratulations !, you have successfully compiled your kcl package with kpm.

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: "nginx:1.14.2"
name: nginx
ports:
- containerPort: 80

Integration OAM into KCL

Kubevela and Open Application Model (OAM) brings modular, extensible, and portable design for modeling application deployment with higher level yet consistent API.

For the same ways, You can use OAM in KCL code directly just like follows:


import konfig.base.pkg.kusion_kubevela.v1beta1

app: v1beta1.Application {
metadata.name = "webservice-app"
spec.components = [{
name = "front-end"
type = "webservice"
properties = {
image = "oamdev/testapp:v1"
cmd = ["node", "server.js"]
ports = [{port = 8080, expose = True}]
exposeType = "NodePort"
cpu = "0.5"
memory = str(512Mi)
}
traits = [
{
type = "scaler"
properties.replicas = 1
}
]
}]
}

Run the following commands


$ kcl main.k -S app

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: webservice-app
spec:
components:
- name: front-end
properties:
image: "oamdev/testapp:v1"
cmd:
- node
- server.js
ports:
- port: 8080
expose: true
exposeType: NodePort
cpu: "0.5"
memory: 512Mi
traits:
- properties:
replicas: 1
type: scaler
type: webservice

Want More?

See KCL Website and KCL v0.4.6 Release Note for more information.

--

--