Overview of the cdk8s framework for “programming” Kubernetes manifests

Kubernetes has set the standard for running microservice applications and more. From startups to enterprises, many companies tend to design their applications ready to run on a Kubernetes cluster.

In order to run applications in K8s, they usually use Helm templates with manifest descriptions. Although the template format is easy to read and simple to learn, it can cause some headaches as the application deployment process grows more complex and involves the creation of ever more testing environments (to which some parts of the application are deployed), etc. Manifest refactoring may become a non-trivial task in the case that you actively use Go templates*.

What if developers don’t have the time to figure out all the subtleties of Helm templates, YAML syntax, and Go templates but yet still need to run the application in Kubernetes? The answer to their wishes may prove to be cdk8s.

* Although Go templates allow you to implement some advanced templating techniques, in order to achieve that, you will have to “mess around” with Helm templating as much as possible. This article sheds some light on a number of those techniques.

What is cdk8s?


cdk8s is an Open Source framework for generating Kubernetes resources. The name stands for Cloud Development Kit for Kubernetes.

The gist of cdk8s is it allows you to use common programming languages, such as TypeScript, JavaScript, Python, and Java instead of Helm templates to manage resources and applications in Kubernetes. Recently, it has added Go support. The list of languages it supports keeps on growing — for example, you can already find issues for Rust or Ruby.

The project is developed and supported by AWS as well as the wider community (the GitHub repository currently has 50+ contributors and about 2,800 stars).

With cdk8s, you get all the benefits of a programming language you know, as well as all the features of an IDE you are familiar with: type hints, autocomplete, loops, iterations, shared modules, and classes… Moreover, tests can help you avoid errors, increase productivity, and save yourself the need to write a vast number of Helm templates.

Once the resources the application requires are defined using the framework, cdk8s generates Helm templates for it. You can quickly deploy them to a K8s cluster via the kubectl apply command or some other utility with similar functionality.

cdk8s runs locally, but all generated Helm templates can be applied to any Kubernetes cluster.

The cdk8s components

The cdk8s framework consists of three main components:

  • the framework core,
  • the constructs library,
  • the command-line interface.

The constructs library (based on constructs) includes the essential components for creating manifests for a specific object, such as Pod, Service, Deployment, ReplicaSet, etc.

The library allows you to:

  • create API abstractions using strongly typed data types;
  • interact with methods and properties;
  • test using familiar testing tools and techniques.

To use the constructs library, import it via import { Construct } from 'constructs';.

The CLI allows you to initialize the template for cdk8s in the desired programming language and generate the final manifests.

Using cdk8s with TypeScript

From a technical perspective, cdk8s is a cross-platform libraries collection installed as an npm package. You have to install Node.js and npm to use cdk8s with TypeScript (JavaScript).

Install the framework via the following command:

$ npm install -g cdk8s-cli

You can initialize the cdk8s project by specifying a language template in a CLI — for example, python-app or javascript-app. Note that the examples shown below are for the TypeScript language:

$ mkdir /app
$ cd /app
$ cdk8s init typescript-app

The above command will create a project structure in the directory:

app@cdk8s:/app# cdk8s init typescript-app
Initializing a project from the typescript-app template

+ cdk8s-plus-22@1.0.0-beta.107
+ constructs@3.3.207
+ cdk8s@1.5.6
added 11 packages from 10 contributors and audited 11 packages in 1.165s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities

+ @types/jest@26.0.24
+ ts-jest@26.5.6
+ jest@26.6.3
+ @types/node@14.18.10
+ typescript@4.5.5
added 496 packages from 357 contributors and audited 509 packages in 151.565s

28 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

+ cdk8s-cli@1.0.89
added 141 packages from 103 contributors and audited 650 packages in 23.098s

72 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


> app@1.0.0 import /home/zhbert/tmp/app
> cdk8s import

Importing k8s v1.22.0...
Importing resources, this may take a few moments...
k8s

> app@1.0.0 compile /home/zhbert/tmp/app
> tsc


> app@1.0.0 test /home/zhbert/tmp/app
> jest "-u"

 PASS  ./main.test.ts
  Placeholder
    ✓ Empty (7 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        4.156 s
Ran all test suites.

> app@1.0.0 synth /home/zhbert/tmp/app
> cdk8s synth

dist/app.k8s.yaml
========================================================================================================

 Your cdk8s typescript project is ready!

   cat help         Print this message
 
  Compile:
   npm run compile     Compile typescript code to javascript (or "yarn watch")
   npm run watch       Watch for changes and compile typescript in the background
   npm run build       Compile + synth

  Synthesize:
   npm run synth       Synthesize k8s manifests from charts to dist/ (ready for 'kubectl apply -f')

 Deploy:
   kubectl apply -f dist/

 Upgrades:
   npm run import        Import/update k8s apis (you should check-in this directory)
   npm run upgrade       Upgrade cdk8s modules to latest version
   npm run upgrade:next  Upgrade cdk8s modules to latest "@next" version (last commit)

========================================================================================================

The name of the YAML file (dist/app.k8s.yaml in our case) to be generated depends on the parameters you pass to the library when it is called. This file contains all the resources that were used during the generation. You can optionally split the generated manifests into different files as well.

The dist/app.k8s.yaml file (created during the library initialization) is empty for the time being.

If you now examine the TypeScript application file (main.ts) (see Getting Started), you will see the basic template without any Kubernetes component definitions. Let’s fix this by adding the Deployment and Service objects:

import { Construct } from 'constructs';
import { App, Chart, ChartProps } from 'cdk8s';

// imported constructs
import { KubeDeployment, KubeService, IntOrString } from './imports/k8s';

export class MyChart extends Chart {
  constructor(scope: Construct, id: string, props: ChartProps = { }) 
  {
    super(scope, id, props);

    const label = { app: 'hello-k8s' };

    new KubeService(this, 'service', {
      spec: {
        type: 'LoadBalancer',
        ports: [ { port: 80, targetPort: IntOrString.fromNumber(8080) } ],
        selector: label
      }
    });

    new KubeDeployment(this, 'deployment', {
      spec: {
        replicas: 2,
        selector: {
          matchLabels: label
        },
        template: {
          metadata: { labels: label },
          spec: {
            containers: [
              {
                name: 'hello-kubernetes',
                image: 'paulbouwer/hello-kubernetes:1.7',
                ports: [ { containerPort: 8080 } ]
              }
            ]
          }
        }
      }
    });
  }
}

const app = new App();
new MyChart(app, 'hello');
app.synth();

As you can see, we have added a service of the LoadBalancer type and a Deployment with two replicas as well as a preset label.

Now, run the following command ​​to generate manifests:

$ cdk8s synth
dist/hello.k8s.yaml

The manifest files will be called hello-[something] since this name was passed as a parameter to initialize the class object.

Now, let’s look at the contents of the resulting dist/hello.k8s.yaml file: it contains all the required components for deploying the application (along with the resources we’ve added) to the Kubernetes cluster:

$ cat dist/hello.k8s.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-service-c8c17160
spec:
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: hello-k8s
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment-c8c7fda7
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-k8s
  template:
    metadata:
      labels:
        app: hello-k8s
    spec:
      containers:
        - image: paulbouwer/hello-kubernetes:1.7
          name: hello-kubernetes
          ports:
            - containerPort: 8080

All that is left to do now is to apply these YAML files in the cluster, e.g., using the kubectl command:

$ kubectl apply -f dist/hello.k8s.yaml

And we’re done!

Find more ready-made examples of using cdk8s in different languages (Go, Java, Python, and TypeScript) in the project repository.

Conclusion

cdk8s is a promising framework for generating Kubernetes resource manifests. Developers can quickly master the basics required to run an application in a cluster.

At the moment, there are examples of integrating cdk8s with Flux for the Python or JavaScript library. You can also integrate it with Argo (for the Python library).

The project was in beta status until October’21 when AWS Labs announced it became generally available (GA) and “ready for production usage with any conformant Kubernetes cluster.”

GitHub month’s statistics outlines how the cdk8s development is currently progressing:

Some add-ons to the main project are available in other cdk8s-team repos on GitHub. For example, you can find some ready-made constructs for specific applications (Redis, Grafana) there as well as the cdk8s-operator library for creating Kubernetes operators.

Finally, it is worth mentioning that cdk8s was included in the CNCF sandbox in November’20.

Comments

Your email address will not be published. Required fields are marked *