DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • KubeVirt Implementation: Who Needs It and Why?
  • Service Mesh Unleashed: A Riveting Dive Into the Istio Framework
  • Platform Engineering Trends in Cloud-Native: Q&A With Ville Aikas
  • Integrating Google Cloud Platform Services With Kubernetes

Trending

  • How to Ensure Cross-Time Zone Data Integrity and Consistency in Global Data Pipelines
  • Secure by Design: Modernizing Authentication With Centralized Access and Adaptive Signals
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Write Your Kubernetes Infrastructure as Go Code — Cdk8s-Plus in Action!

Write Your Kubernetes Infrastructure as Go Code — Cdk8s-Plus in Action!

Reducing boilerplate code with cdk8s-plus library.

By 
Abhishek Gupta user avatar
Abhishek Gupta
DZone Core CORE ·
Jul. 18, 22 · Analysis
Likes (2)
Comment
Save
Tweet
Share
34.0K Views

Join the DZone community and get the full member experience.

Join For Free

One of my previous blog posts covered how to get started with cdk8s (Cloud Development Kit for Kubernetes), which is an open-source framework (part of CNCF) using which you can define your Kubernetes applications using regular programming languages (instead of yaml).

You were able to set up a simple nginx Deployment and accessed it via a Service - all this was done using Go, which was then converted to yaml (using cdk8s synth) and submitted to the cluster using kubectl. This was a good start. However, since the core cdk8s library is pretty low-level (for a good reason!) the code involved a lot of boilerplate (you can refer to the code here).

cdk8s-plus leverages building blocks from cdk8s core library, thereby helping reduce verbosity and complexity by providing higher level abstractions/APIs for all Kubernetes objects such as Deployments, Services, etc. In this blog, we will see cdk8s-plus in action and even deploy WordPress on Kubernetes with it!

Let’s Start by Revamping the Nginx Deployment

To witness how cdk8s-plus works, it's best to look at the code.

It is available on Github.

I will walk you through the code as we go along.

Go
 
func NewNginxChart(scope constructs.Construct, id string, props *NginxChartProps) cdk8s.Chart {
    var cprops cdk8s.ChartProps
    if props != nil {
        cprops = props.ChartProps
    }
    chart := cdk8s.NewChart(scope, jsii.String(id), &cprops)

    dep := cdk8splus22.NewDeployment(chart, jsii.String("deployment"), &cdk8splus22.DeploymentProps{Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String("nginx-deployment-cdk8s-plus")}})

    dep.AddContainer(&cdk8splus22.ContainerProps{
        Name:  jsii.String("nginx-container"),
        Image: jsii.String("nginx"),
        Port:  jsii.Number(80)})

    dep.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)}}})

    return chart
}

We start by creating a Deployment, then add a container and finally expose it using a Service. This is quite intuitive and user-friendly.

The container details could have been provided via DeploymentProps but using AddContainer seemed more natural (at least to me).

To generate Kubernetes manifest, simply run cdk8s synth. This will generate a yaml in the dist folder. Here is an example (some of the names, labels, etc. will be different in your case):

YAML
 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-cdk8s-plus
spec:
  minReadySeconds: 0
  progressDeadlineSeconds: 600
  replicas: 1
  selector:
    matchLabels:
      cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
    spec:
      automountServiceAccountToken: true
      containers:
        - image: nginx
          imagePullPolicy: Always
          name: nginx-container
          ports:
            - containerPort: 80
          securityContext:
            privileged: false
            readOnlyRootFilesystem: false
            runAsNonRoot: false
      dnsPolicy: ClusterFirst
      securityContext:
        fsGroupChangePolicy: Always
        runAsNonRoot: false
      setHostnameAsFQDN: false
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-container-service
spec:
  externalIPs: []
  ports:
    - port: 9090
      targetPort: 80
  selector:
    cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e
  type: LoadBalancer

Both the Deployment and Service are present in the same manifest, since they were declared in the same Chart.

It's worth noting that there was no need to specify any Pod label selectors, template labels (in Deployment code) or Service selector. cdk8s-plus took care of it by auto-generating cdk8s.io/metadata.addr: nginx-cdk8s-plus-deployment-c84b388e, which was used in spec.selector.matchLabels and spec.template.metadata.labels, along with the Service selector in nginx-container-service

A note on dependencies

go.mod lists all the modules:

Go
 
require (
    github.com/aws/constructs-go/constructs/v10 v10.1.42
    github.com/aws/jsii-runtime-go v1.61.0
    github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.3.31
    github.com/cdk8s-team/cdk8s-plus-go/cdk8splus22/v2 v2.0.0-rc.23
)

Note that we are using cdk8splus22. The reason for this naming convention is because each cdk8s-plus library is separately vented to target a specific Kubernetes version - the 22 at the end signifies that this dependency will work with Kubernetes 1.22

I would recommend reading the FAQs to get further clarity

To test this locally...

... you can use minikube, kind, etc.

Shell
 
git clone https://github.com/abhirockzz/cdk8s-for-go-developers
cd part2-cdk8s-plus-in-action/nginx-example

# make sure cluster is running
minikube start

# create the resources
kubectl apply -f dist/
kubectl get pods -w

Once Pod is running, check the Service:

Shell
 
kubectl get svc

In a terminal, run this command (it runs as a separate process):

Shell
 
minikube tunnel

To access the nginx server, navigate to the external IP (as per the Service). In the case of minikube, you can simply use localhost:9090 or 127.0.0.0:9090

How About a WordPress Installation on Kubernetes?

I like this example - it's not overly complex but realistic enough because it has multiple moving parts that include a combination of stateless, stateful components, different kinds of services, etc.

This post is not a deep dive into Wordpress and loosely inspired by this article in the Kubernetes documentation, which I assume folks might be familiar with.

The main function will give you a sense of what lies ahead:

Go
 
func main() {
    app := cdk8s.NewApp(nil)

    mySQLChart := NewMySQLChart(app, "mysql", nil)
    wordpressChart := NewWordpressChart(app, "wordpress", nil)

    wordpressChart.AddDependency(mySQLChart)

    app.Synth()
}

So far, we have dealt with a single chart. Our WordPress cdk8s application has two separate charts - one for MySQL database and the other one for WordPress. This will result in two different manifests being created as a result of cdk8s synth process.

Let's look at the MySQL chart first

some code has been omitted for brevity

We start by defining a Kubernetes Secret to store MySQL password (with NewSecret):

Go
 
func NewMySQLChart(scope constructs.Construct, id string, props *MyChartProps) cdk8s.Chart {
    //....
    secretName := "mysql-pass"
    password := "Password123"

    mysqlSecret := cdk8splus22.NewSecret(chart, jsii.String("mysql-secret"),
        &cdk8splus22.SecretProps{
            Metadata: &cdk8s.ApiObjectMetadata{Name: jsii.String(secretName)}})

    secretKey := "password"
    mysqlSecret.AddStringData(jsii.String(secretKey), jsii.String(password))

MySQL password has been declared in the code - not a best practice by any means, just for demo. Do not do this in production!

Then we create the Deployment and provide container details. Notice how the Secret has been added as an environment variable to the container:

  • First, we got an EnvValue using EnvValue_FromSecretValue
  • That was added to the container using Env#AddVariable
Go
 
   dep := cdk8splus22.NewDeployment(chart, jsii.String("mysql-deployment-cdk8splus"), &cdk8splus22.DeploymentProps{})

    containerImage := "mysql"

    mysqlContainer := dep.AddContainer(&cdk8splus22.ContainerProps{
        Name:  jsii.String("mysql-container"),
        Image: jsii.String(containerImage),
        Port:  jsii.Number(3306),
    })

    envValFromSecret := cdk8splus22.EnvValue_FromSecretValue(&cdk8splus22.SecretValue{Key: jsii.String(secretKey), Secret: mysqlSecret}, &cdk8splus22.EnvValueFromSecretOptions{Optional: jsii.Bool(false)})

    mySQLPasswordEnvName := "MYSQL_ROOT_PASSWORD"

    mysqlContainer.Env().AddVariable(jsii.String(mySQLPasswordEnvName), envValFromSecret)

For durable storage, we create a PersistentVolumeClaim, use that to define a Volume, and mount it onto the container at the path /var/lib/mysql.

Go
 
   mysqlPVC := cdk8splus22.NewPersistentVolumeClaim(chart, jsii.String("mysql-pvc"), &cdk8splus22.PersistentVolumeClaimProps{
        AccessModes: &[]cdk8splus22.PersistentVolumeAccessMode{cdk8splus22.PersistentVolumeAccessMode_READ_WRITE_ONCE},
        Storage:     cdk8s.Size_Gibibytes(jsii.Number(2))})

    mysqlVolumeName := "mysql-persistent-storage"
    mysqlVolume := cdk8splus22.Volume_FromPersistentVolumeClaim(chart, jsii.String("mysql-vol-pvc"), mysqlPVC, &cdk8splus22.PersistentVolumeClaimVolumeOptions{Name: jsii.String(mysqlVolumeName)})

    mySQLVolumeMountPath := "/var/lib/mysql"
    mysqlContainer.Mount(jsii.String(mySQLVolumeMountPath), mysqlVolume, &cdk8splus22.MountOptions{})

Finally, we create a Service:

Go
 
 mySQLServiceName := "mysql-service"
    clusterIPNone := "None"

    cdk8splus22.NewService(chart, jsii.String("mysql-service"), &cdk8splus22.ServiceProps{
        Metadata:  &cdk8s.ApiObjectMetadata{Name: jsii.String(mySQLServiceName)},
        Selector:  dep,
        ClusterIP: jsii.String(clusterIPNone),
        Ports:     &[]*cdk8splus22.ServicePort{{Port: jsii.Number(3306)}},
    })

Unlike previous example, we create a Service explicitly and then refer to Deployment object in the service selector.

WordPress Chart - Except for minor differences, it's the same as the MySQL chart with WordPress-specific configuration obviously. So I won't repeat it here - feel free to explore the code.

Time to Kick off WordPress on Kubernetes!

Rinse and repeat - cdk8s synth to create the manifest and apply it with kubectl:

Shell
 
cd part2-cdk8s-plus-in-action/wordpress

#create manifests
cdk8s synth

#apply them
kubectl apply -f dist/

#output - you will see something similar to:

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

In a different terminal run (if not already running):

Shell
 
minikube tunnel

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

Go ahead, finish the installation and log into your WordPress instance. Feel free to experiment. Maybe try deleting the MySQL deployment and re-create it. Thanks to the PersistentVolume, MySQL data should be recovered and WordPress will continue to work.

Conclusion

Awesome! In this blog, you saw the expressiveness of cdk8s-plus. We started off with a compact and less verbose version of the Nginx deployment and ended up with a full-fledged WordPress instance - all using Go.

Happy coding!

Infrastructure Kubernetes

Published at DZone with permission of Abhishek Gupta, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • KubeVirt Implementation: Who Needs It and Why?
  • Service Mesh Unleashed: A Riveting Dive Into the Istio Framework
  • Platform Engineering Trends in Cloud-Native: Q&A With Ville Aikas
  • Integrating Google Cloud Platform Services With Kubernetes

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!