Skip to content

cdk8s+

cdk8s+ is a library with high level abstractions for authoring Kubernetes applications.

Built on top of the auto-generated building blocks provided by cdk8s, this library includes a hand crafted construct for each native kubernetes object, exposing richer API’s with reduced complexity.

Info

The documentation here relates to version 2.x of the cdk8s toolchain, which is the latest. If you are still using version 1.x, please refer to the Migrating from 1.x Guide.

Here is an example of how we would deploy a simple nginx container, once with the low-level API (on the left), and once with the high level abstraction (on the right).

corevsplus

Spec compatibility

cdk8s+ is vended as a separate library for each kubernetes spec version. Follow the pane on the left to navigate to the appropriate version you need. Per kubernetes compatibility guarantees, stable resources in a cdk8s-plus-XX library are compatible with any spec version higher or equal to 1.XX.0. Non-stable resources are not guaranteed to be compatible, as they may be removed in future spec versions.

Notice

cdk8s+ libraries are maintained only for the 3 latest Kubernetes releases. So, if the latest Kubernetes release is N, cdk8s-plus-N, cdk8s-plus-(N-1) and cdk8s-plus-(N-2) will be continously updated and supported. However, cdk8s-plus-(N-3) and so on will no longer be actively maintained.

Naming conventions

  • Stable resources are represented by a construct of the same kind. For example, the io.k8s.api.core.v1.Pod resource maps to the Pod construct.
  • Non stable resources are suffixed with their api version. For example, the io.k8s.api.networking.v1beta1.Ingress maps to the IngressV1Beta1 construct.

FAQ

I operate Kubernetes version 1.XX - which cdk8s+ library should I be using?

If there is a cdk8s-plus-XX library that matches your target Kubernetes version, we recommend using it since all Kubernetes manifests generated using it will be compatible.

If there is not a matching cdk8s-plus-XX library, we recommend using the closest matching version. The manifests generated by cdk8s-plus-XX may also work for older versions of Kubernetes, but you might encounter some unsupported spec properties or invalid manifests.

I’m using cdk8s-plus-XX - which kubernetes versions will my manifest work on?

If you are using stable APIs (those that are not in alpha or beta), manifests generated in cdk8s-plus-XX will work in Kubernetes versions 1.XX.0 and above. Unstable APIs (which are always labeled in cdk8s+ using a suffix, e.g. IngressV1Beta1) may work in newer versions of Kubernetes, but it is also possible they have been removed.

The manifests generated by cdk8s-plus-XX may also work for older versions of Kubernetes, but you might encounter some unsupported spec properties or invalid manifests.

At a glance

import * as kplus from 'cdk8s-plus-28';
import * as cdk8s from 'cdk8s';
import * as path from 'path';

// our cdk app
const app = new cdk8s.App();

// our kubernetes chart
const chart = new cdk8s.Chart(app, 'my-chart');

// lets create a volume that contains our app.
// we use a trick with a config map!
const appData = new kplus.ConfigMap(chart, 'AppData');
appData.addDirectory(path.join(__dirname, 'app'));

const appVolume = kplus.Volume.fromConfigMap(appData);

// lets create a deployment to run a few instances of a pod
const deployment = new kplus.Deployment(chart, 'Deployment', {
  replicas: 3,
});

// now we create a container that runs our app
const appPath = '/var/lib/app';
const port = 80;
const container = deployment.addContainer({
  image: 'node:14.4.0-alpine3.12',
  command: ['node', 'index.js', `${port}`],
  port: port,
  workingDir: appPath,
});

// make the app accessible to the container
container.mount(appPath, appVolume);

// finally, we expose the deployment as a load balancer service and make it run
deployment.exposeViaService({ serviceType: kplus.ServiceType.LOAD_BALANCER });

// we are done, synth
app.synth();
dist/my-chart.yaml
apiVersion: v1
data:
  index.js: |-
    var http = require('http');

    var port = process.argv[2];

    //create a server object:
    http.createServer(function (req, res) {
      res.write('Hello World!'); //write a response to the client
      res.end(); //end the response
    }).listen(port); //the server object listens on port 80
kind: ConfigMap
metadata:
  annotations: {}
  labels: {}
  name: chart-appdata-configmap-da4c63ab
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations: {}
  labels: {}
  name: chart-deployment-pod-d4285cc9
spec:
  replicas: 3
  selector:
    matchLabels:
      cdk8s.deployment: ChartDeploymentCFC2E30C
  template:
    metadata:
      annotations: {}
      labels:
        cdk8s.deployment: ChartDeploymentCFC2E30C
    spec:
      containers:
        - command:
            - node
            - index.js
            - "80"
          env: []
          image: node:14.4.0-alpine3.12
          name: main
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: /var/lib/app
              name: configmap-chart-appdata-configmap-da4c63ab
          workingDir: /var/lib/app
      volumes:
        - configMap:
            name: chart-appdata-configmap-da4c63ab
          name: configmap-chart-appdata-configmap-da4c63ab
---
apiVersion: v1
kind: Service
metadata:
  annotations: {}
  labels: {}
  name: chart-deployment-service-pod-42f50c26
spec:
  externalIPs: []
  ports:
    - port: 8080
      targetPort: 80
  selector:
    cdk8s.deployment: ChartDeploymentCFC2E30C
  type: LoadBalancer

Getting Started

❯ npm install cdk8s-plus-28 cdk8s constructs

import * as kplus from 'cdk8s-plus-28';
import * as cdk8s from 'cdk8s';

const app = new cdk8s.App();
const chart = new cdk8s.Chart(app, 'Chart');

new kplus.Deployment(chart, 'Deployment', {
  replicas: 3,
  containers: [{
    image: 'ubuntu',
  }],
});

app.synth();

❯ npm install cdk8s-plus-28 cdk8s constructs

const kplus = require('cdk8s-plus-28');
const cdk8s = require('cdk8s');

const app = new cdk8s.App();
const chart = new cdk8s.Chart(app, 'Chart');

new kplus.Deployment(chart, 'Deployment', {
  replicas: 3,
  containers: [{
    image: 'ubuntu',
  }],
});

app.synth();

❯ pip install cdk8s-plus-28 cdk8s

import cdk8s_plus_27 as kplus
import cdk8s

app = cdk8s.App()
chart = cdk8s.Chart(app, 'Chart')

kplus.Deployment(chart, 'Deployment',
  replicas=1,
  containers=[kplus.ContainerProps(image='ubuntu')]
)

app.synth()
<dependency>
  <groupId>org.cdk8s</groupId>
  <artifactId>cdk8s</artifactId>
  <version>2.5.21</version>
</dependency>
<dependency>
  <groupId>org.cdk8s</groupId>
  <artifactId>cdk8s-plus-28</artifactId>
  <version>2.0.0</version>
</dependency>
package com.mycompany.app;

import software.constructs.Construct;

import org.cdk8s.App;
import org.cdk8s.Chart;
import org.cdk8s.ChartProps;
import org.cdk8s.plus28.Deployment;
import org.cdk8s.plus28.ContainerProps;

import java.util.Arrays;

public class Main extends Chart 
{

    public Main(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public Main(final Construct scope, final String id, final ChartProps options) {
        super(scope, id, options);

        Deployment.Builder.create(this, "Deployment")
          .replicas(3)
          .containers(Arrays.asList(ContainerProps.builder()
            .image("ubuntu")
            .build()))
          .build();
    }

    public static void main(String[] args) {
        final App app = new App();
        final Chart chart = new Main(app, "Chart");
        app.synth();
    }
}
import (
  "github.com/aws/constructs-go/constructs/v10"
  "github.com/aws/jsii-runtime-go"
  "github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2"
  "github.com/cdk8s-team/cdk8s-plus-go/cdk8splus28"
)
app := cdk8s.NewApp(nil)
chart := cdk8s.NewChart(app, jsii.String("ubuntu"), nil)

cdk8splus28.NewDeployment(chart, jsii.String("Deployment"), &cdk8splus28.DeploymentProps{
  Replicas: jsii.Number(3),
  Containers: &[]*cdk8splus28.ContainerProps{{
    Image: jsii.String("ubuntu"),
  }},
})

app.Synth()

Overcoming Coverage Gaps

As mentioned, the APIs offered by cdk8s+ are hand-written by the team as well as the community. As such, you might encounter coverage gaps from time to time; that is, you are trying to configure something but the API doesn’t expose it. There are two kinds of gaps:

Missing Resource

When an entire resource is missing, you can supplement it by dropping to the L1 layer of constructs, which are available from within cdk8s+, so you don’t need to install an additional library, or import any resources. For example:

import * as kplus from 'cdk8s-plus-28';
import * as cdk8s from 'cdk8s';

const app = new cdk8s.App();
const chart = new cdk8s.Chart(app, 'Chart');

// a Deployment exists as a higher level objects,
// so we use it.
new kplus.Deployment(chart, 'Deployment', {
  replicas: 3,
  containers: [{
    image: 'ubuntu',
  }],
});

// a StorageClass doesn't, so we use the low level objects.
// notice the '.k8s.' addition.
new kplus.k8s.KubeStorageClass(chart, 'StorageClass', {
  provisioner: 'kubernetes.io/aws-ebs'
});

app.synth();

Missing Property

See Patching API objects behind higher level APIs