Write Your Kubernetes Infrastructure as Go Code — Extend “cdk8s” With Custom Constructs | by Abhishek Gupta | Aug, 2022

Build a WordPress deployment as a cdk8s construct

Photo by Fikret tozak on Unsplash

CONSTRUCTIONS are the fundamental building block of cdk8s (Cloud Development Kit for Kubernetes) — an open-source framework (part of CNCF) with which you can define your Kubernetes applications using regular programming languages ​​(instead of yaml). In Getting started with cdk8s, you saw how to use the core cdk8s library.

You can also use the cdk8s-plus library (also covered this a previous blog) to reduce the amount of boilerplate code you need to write. With cdk8s-pluscreating a Kubernetes Deploymentspecifying it’s container (and other properties) and exposing it via a Service is three function calls away.

For example, to setup and access Nginxyou simply need this:

//...
deployment := cdk8splus22.NewDeployment(chart, jsii.String("deployment"), &cdk8splus22.DeploymentProps{Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String("nginx-deployment-cdk8s-plus")}})
deployment.AddContainer(&cdk8splus22.ContainerProps{
Name: jsii.String("nginx-container"),
Image: jsii.String("nginx"),
Port: jsii.Number(80)})
deployment.ExposeViaService(&cdk8splus22.DeploymentExposeViaServiceOptions{
Name: jsii.String("nginx-container-service"),
ServiceType: cdk8splus22.ServiceType_LOAD_BALANCER,
Ports: &[]*cdk8splus22.ServicePort{{Port: jsii.Number(9090), TargetPort: jsii.Number(80)}}})
//...

But things can get even better!

Instead of writing the same logic over and over, you can package it in the form of a reusable component that can be invoked just like other built-in cdk8s functions (eg NewDeployment, NewService etc.). Although it might not sound as useful for the simple application(s), this approach is invaluable for a large project, team or organization who want to scale their engineering efforts. In fact, there is already a pool of ready-to-use components available at constructs.dev. These include constructs contributed by the community, AWS and others as well, across multiple programming languages.

… let’s look at the code. I will continue to use WordPress as an example, like I did in the previous blog post. Here is a code snippet that shows how everything is wired together (with implementation walk-through in the next section):

You can refer to the complete code on Github

//...func NewMyChart(scope constructs.Construct, id string, props *MyChartProps) cdk8s.Chart {
//....
NewWordpressStack(chart, jsii.String("wordpress-stack"), &WordpressProps{//....)
return chart
}
func main() {
app := cdk8s.NewApp(nil)
NewMyChart(app, "wordpress-custom-stack", nil)
app.Synth()
}
  • NewWordpressStack gives us a construct that represents an entire WordPress installation (single line of code!)
  • We simply configure it as per our requirements (with WordPressProps)
  • Include this as part of a cdk8s.Chart which is then included in the cdk8s.App (as with any other cdk8s application)

There is a lot of flexibility in terms of how you want to build a custom construct, depending on your requirements. But, at its very core, the basic concept is to define a way to create a new construct.Construct. You would want to provide a way to add metadata to further configure/refine your Construct – typically, thats done through properties (cdk8s.ChartProps).

First we define WordPressProps – this encapsulates/externalises the attributes of the WordPress installation. Since this is just an example, I have provided limited attributes such as MySQL/Wordpress Docker images, MySQL password, and required storage.

type WordPressProps struct {
MySQLImage *string
MySQLPassword *string
MySQLStorage *float64
WordPressImage *string
WordPressStorage *float64
}

Then we have a function that will allow other charts/constructs to instantiate WordPress. This is where the entire implementation resides.

func NewWordpressStack(scope constructs.Construct, id *string, props *WordpressProps) constructs.Construct {
...
}

The props *WordpressProps parameter allows other constructs to influence the WordPress stack creation eg you can define how much storage you need, maybe use a different Docker image for WordPress/MySQL. The actual code for this function is similar to the one you saw here (with required adjustments), so there is no point repeating it here. I will simply highlight the important bits – specifically the ones that use the props to configure the required components.

This sample construct used cdk8splus22 library. The reason for this naming convention is because each cdk8s-plus library is separately vended to target a specific Kubernetes version – the 22 at the end signifies that this dependency will work with Kubernetes 1.22. You can use the library corresponding to your Kubernetes version and refer to the FAQs for more info.

We use the MySQL password from props and use that to create the Secret.

//...
password := props.MySQLPassword
mysqlSecret := cdk8splus22.NewSecret(wordpressConstruct, jsii.String("mysql-secret"),
&cdk8splus22.SecretProps{
Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String(secretName)}})
secretKey := "password"
mysqlSecret.AddStringData(jsii.String(secretKey), password)
//...

The container images for MySQL and WordPress are referenced via their respective Deployments:

//...
containerImage := props.MySQLImage
mysqlContainer := dep.AddContainer(&cdk8splus22.ContainerProps{
Name: jsii.String("mysql-container"),
Image: containerImage,
Port: jsii.Number(3306),
})
//...
wordpressContainer := wordPressDeployment.AddContainer(&cdk8splus22.ContainerProps{
Name: jsii.String("wordpress-container"),
Image: props.WordpressImage,
Port: jsii.Number(80),
})

We also use the passed in storage as well — this is used to configure the PersistentVolumeClaim request.

...
mysqlPVC := cdk8splus22.NewPersistentVolumeClaim(wordpressConstruct, jsii.String("mysql-pvc"), &cdk8splus22.PersistentVolumeClaimProps{
AccessModes: &[]cdk8splus22.PersistentVolumeAccessMode{cdk8splus22.PersistentVolumeAccessMode_READ_WRITE_ONCE},
Storage: cdk8s.Size_Gibibytes(props.MySQLStorage)})
...
wordpressPVC := cdk8splus22.NewPersistentVolumeClaim(wordpressConstruct, jsii.String("wordpress-pvc"), &cdk8splus22.PersistentVolumeClaimProps{
AccessModes: &[]cdk8splus22.PersistentVolumeAccessMode{cdk8splus22.PersistentVolumeAccessMode_READ_WRITE_ONCE},
Storage: cdk8s.Size_Gibibytes(props.WordpressStorage)})

Finally, we call NewWordpressStack from another cdk8s.Chart and pass in the attributes we want to configure.

func NewMyChart(scope constructs.Construct, id string, props *MyChartProps) cdk8s.Chart {
var cprops cdk8s.ChartProps
if props != nil {
cprops = props.ChartProps
}
chart := cdk8s.NewChart(scope, jsii.String(id), &cprops)
NewWordpressStack(chart, jsii.String("wordpress-stack"), &WordpressProps{
MySQLImage: jsii.String("mysql"),
MySQLPassword: jsii.String("Password123"),
MySQLStorage: jsii.Number(3),
WordPressImage: jsii.String("wordpress:4.8-apache"),
WordPressStorage: jsii.Number(2)})
return chart
}

To test it locally…

… you can use minikube, kind, etc.

# make sure cluster is running
minikube start
git clone https://github.com/abhirockzz/cdk8s-for-go-developers
cd part4-custom-construct

Create manifest and inspect all the resources (see dist directory):

cdk8s synth

To deploy them:

kubectl apply -f dist/# output (might differ in your case)secret/mysql-pass created
deployment.apps/mysql-mysql-deployment-cdk8splus-c83762d9 created
persistentvolumeclaim/mysql-mysql-pvc-c8799bba created
service/mysql-service created
deployment.apps/wordpress-wordpress-deployment-cdk8splus-c8252da7 created
service/wordpress-service created
persistentvolumeclaim/wordpress-wordpress-pvc-c8334a29 created

Check the Kubernetes Service (called wordpress-service) which exposes the wordpress Deployment.

kubectl get svc wordpress-service

If you’re using minikubein a different terminal run (if not already running):

minikube tunnel

Use your browser to navigate to http://localhost:80. You should see the familiar WordPress installation screen.

WordPress on Kubernetes is ready

Go ahead, finish the installation and log into your WordPress instance. Feel free to experiment with it.

Leave a Comment