A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-applications below:

Deploy an application with CDK for Terraform | Terraform

The Cloud Development Kit for Terraform (CDKTF) allows you to define infrastructure using familiar programming languages such as TypeScript, Python, or Go. This lets you take advantage of the existing library of providers and modules in the Terraform ecosystem.

Terraform configuration language is a declarative configuration language with functions and constructs that let you manage infrastructure. CDKTF allows you to generate Terraform configuration by writing code in your preferred programming language, with the same tools and workflows as your application code. Using CDKTF allows you to integrate your infrastructure management process with your testing and application deployment pipelines, which makes it easer for developers to work with their own infrastructure.

In this tutorial, you will use the CDK for Terraform to deploy an application on Kubernetes. First, you will use CDKTF to convert Terraform configuration into TypeScript code. Then, you will refactor your code to dynamically generate Terraform configuration to manage an example web application consisting of frontend and backend services. Next, you will create multiple stacks representing the application in different environments. Finally, you will use CDKTF to remove your application.

This tutorial assumes that you are familiar with the standard Terraform workflow. If you are new to Terraform, complete the Get Started tutorials first.

For this tutorial, you will need:

Launch Terminal

This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.

Clone the CDKTF Applications GitHub repository for this tutorial.

$ git clone https://github.com/hashicorp-education/learn-terraform-cdktf-applications

Change to the repository directory.

$ cd learn-terraform-cdktf-applications

This repository contains an example application with frontend and a backend services that you will deploy on Kubernetes as Docker containers. It also contains configuration files that you will use to create a local Kubernetes cluster with kind.

Configure local Kubernetes cluster

Start a local Docker registry to store the container images you will use in this tutorial.

$ docker run -d --restart always -p "127.0.0.1:5000:5000" --name local-registry registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
530afca65e2e: Already exists
d450d4da0343: Pull complete
96277bea17b6: Pull complete
470ad04e03fb: Pull complete
bd3d4dc6e66f: Pull complete
Digest: sha256:c631a581c6152f5a4a141a974b74cf308ab2ee660287a3c749d88e0b536c0c20
Status: Downloaded newer image for registry:2
2d9c6166d2ea3b1f6ef9d933afa6069eef5e2dbaece27ce1b235b89b7c5d374b

Use kind to create a Kubernetes cluster running in Docker on your local machine.

$ kind create cluster --name=cdktf-app --config kind-config.yaml
Creating cluster "cdktf-app" ...
 ✓ Ensuring node image (kindest/node:v1.25.0) đŸ–ŧ 
 ✓ Preparing nodes đŸ“Ļ
 ✓ Writing configuration 📜
 ✓ Starting control-plane đŸ•šī¸
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-cdktf-app"
You can now use your cluster with:

kubectl cluster-info --context kind-cdktf-app

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

Verify that your cluster exists by listing your kind clusters.

$ kind get clusters
cdktf-app

Then, use kubectl to print out information about your cluster. The context is kind- followed by the name of your cluster.

$ kubectl cluster-info --context=kind-cdktf-app
Kubernetes control plane is running at https://127.0.0.1:56821
CoreDNS is running at https://127.0.0.1:56821/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Create a kubeconfig file to allow access to your Kubernetes cluster.

$ kubectl config view --raw --context kind-cdktf-app > kubeconfig.yaml

Now, attach your local Docker registry to your kind cluster.

$ docker network connect kind local-registry

Configure your Kubernetes cluster to use the local registry.

$ kubectl apply -f local-registry-configmap.yaml --kubeconfig kubeconfig.yaml
configmap/local-registry-hosting created

Your Kubernetes cluster now uses the local Docker registry to store and retrieve Docker images. Next you will use CDKTF to generate images for your application and deploy them to your cluster.

Create a directory for your CDKTF application.

Now change into it.

Initialize your CDKTF application with the typescript template and a pre-built Kubernetes provider. For this tutorial, use the --local flag so that CDKTF stores your Terraform state locally, rather than in a remote backend such as HCP Terraform.

$ cdktf init --template=typescript \
             --project-name=learn-terraform-cdktf-applications \
             --project-description="Learn how to develop CDKTF applications" \
             --providers="kubernetes@~>2.14" \
             --local

When CDKTF prompts you, accept the defaults. CDKTF will initialize your application in the current directory, and print out a help message.

Note: By supplying '--local' option you have chosen local storage mode for storing the state of your stack.
This means that your Terraform state file will be stored locally on disk in a file 'terraform.<STACK NAME>.tfstate' in the root of your project.
? Do you want to start from an existing Terraform project? No
? Do you want to send crash reports to the CDKTF team? See
https://www.terraform.io/cdktf/create-and-deploy/configuration-file#enable-crash
-reporting-for-the-cli for more information Yes
##...
 Use Providers:

  You can add prebuilt providers (if available) or locally generated ones using the add command:

  cdktf provider add "aws@~>3.0" null kreuzwerker/docker

  You can find all prebuilt providers on npm: https://www.npmjs.com/search?q=keywords:cdktf
  You can also install these providers directly through npm:

  npm install @cdktf/provider-aws
  npm install @cdktf/provider-google
  npm install @cdktf/provider-azurerm
  npm install @cdktf/provider-docker
  npm install @cdktf/provider-github
  npm install @cdktf/provider-null

  You can also build any module or provider locally. Learn more https://cdk.tf/modules-and-providers

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

Checking whether pre-built provider exists for the following constraints:
  provider: kubernetes
  version : ~>2.14
  language: typescript
  cdktf   : 0.15.0

Found pre-built provider.
Adding package @cdktf/provider-kubernetes @ 3.0.12
Installing package @cdktf/provider-kubernetes @ 3.0.12 using npm.
Package installed.

CDKTF created your application from a template.

Now that you have initialized your CDKTF application, create a Kubernetes Deployment for your application's frontend.

Add Kubernetes Deployment

Convert example Terraform configuration that defines a Kubernetes Deployment into TypeScript.

$ cat ../k8s_deployment.tf | cdktf convert  --provider=kubernetes
/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as kubernetes from "./.gen/providers/kubernetes";
new kubernetes.deployment.Deployment(this, "myapp", {
  metadata: {
    labels: {
      app: "myapp",
      component: "frontend",
      environment: "dev",
    },
    name: "myapp-frontend-dev",
  },
  spec: {
    replicas: "1",
    selector: {
      matchLabels: {
        app: "myapp",
        component: "frontend",
        environment: "dev",
      },
    },
    template: {
      metadata: {
        labels: {
          app: "myapp",
          component: "frontend",
          environment: "dev",
        },
      },
      spec: {
        container: [
          {
            image: "nginx:latest",
            name: "myapp-frontend-dev",
          },
        ],
      },
    },
  },
});

The cdktf convert command converts Terraform configuration into code in the project's configured programming language. This command is still experimental, so this output requires some adjustment before you can use it in your project.

Open main.ts and add code to import the Kubernetes provider and path, and define a deployment.

main.ts

import { Construct } from "constructs"
import { App, TerraformStack } from "cdktf"
import * as kubernetes from "@cdktf/provider-kubernetes"
import * as path from "path"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new kubernetes.provider.KubernetesProvider(this, 'kind', {
      configPath: path.join(__dirname, '../kubeconfig.yaml'),
    })

    new kubernetes.deployment.Deployment(this, "myapp", {
      metadata: {
        labels: {
          app: 'myapp',
          component: 'frontend',
          environment: 'dev',
        },
        name: 'myapp',
      },
      spec: {
        replicas: '1',
        selector: {
          matchLabels: {
            app: 'myapp',
            component: 'frontend',
            environment: 'dev',
          },
        },
        template: {
          metadata: {
            labels: {
              app: 'myapp',
              component: 'frontend',
              environment: 'dev',
            },
          },
          spec: {
            container: [
              {
                image: 'nginx:latest',
                name: 'frontend',
              },
            ],
          },
        },
      },
    })
  }
}

const app = new App()
new MyStack(app, 'app')
app.synth()

This code adds a Kubernetes provider configured with the kubeconfig.yml file you created earlier, and a new kubernetes.Deployment object that represents a Kubernetes Deployment consisting of a single pod running the nginx:latest image.

Install dependencies

Install the path package with NPM.

$ npm install path

added 299 packages, and audited 357 packages in 3s

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

found 0 vulnerabilities
Synthesize your application

Use the cdktf synth command to generate Terraform configuration from your code.

$ cdktf synth
Generated Terraform code for the stacks: app

In your text editor, review the contents of app/cdktf.out/stacks/app/cdk.tf.json. This is Terraform configuration in JSON format that CDKTF generated based on your TypeScript code in main.ts.

Deploy your app

The cdktf deploy command will use Terraform to apply the configuration generated by cdktf synth.

Create your Kubernetes Deployment with cdktf deploy. Respond to the confirmation prompt with Approve.

$ cdktf deploy
app  Initializing the backend...
app
     Successfully configured the backend "local"! Terraform will automatically
     use this backend unless the backend configuration changes.
app  Initializing provider plugins...
app  - Finding hashicorp/kubernetes versions matching "2.14.0"...
app  - Using hashicorp/kubernetes v2.14.0 from the shared cache directory
##...
     Plan: 1 to add, 0 to change, 0 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_deployment.myapp (myapp): Creating...
app  kubernetes_deployment.myapp (myapp): Creation complete after 8s [id=default/myapp]
app
     Apply complete! Resources: 1 added, 0 changed, 0 destroyed.


No outputs found.

The deploy command synthesizes your Terraform configuration if needed, so you will skip cdktf synth for the rest of the tutorial.

List your deployments with kubectl.

$ kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
myapp   1/1     1            1           117s
Update replica count

As with Terraform, you can use CDKTF to update your existing resources.

In your editor, update main.ts to change the number of replicas for your deployment from 1 to 4.

main.ts

       spec: {
-        replicas: '1',
+        replicas: '4',
         selector: {

Deploy your application again. Respond to the confirmation prompt by choosing Approve.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!

##...

     Terraform will perform the following actions:
app    # kubernetes_deployment.myapp (myapp) will be updated in-place
       ~ resource "kubernetes_deployment" "myapp" {
             id               = "default/myapp"
             # (1 unchanged attribute hidden)

           ~ spec {
               ~ replicas                  = "1" -> "4"
                 # (4 unchanged attributes hidden)

                 # (3 unchanged blocks hidden)
             }

             # (1 unchanged block hidden)
         }

     Plan: 0 to add, 1 to change, 0 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
     terraform apply "plan"
Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_deployment.myapp (myapp): Modifying... [id=default/myapp]
app  kubernetes_deployment.myapp (myapp): Modifications complete after 4s [id=default/myapp]
app
     Apply complete! Resources: 0 added, 1 changed, 0 destroyed.


No outputs found.

After Kubernetes finishes applying this change, kubectl will report four instances of your applicaton.

$ kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
myapp   4/4     4            4           8m31s

CDKTF applications are made up of constructs which represent the infrastruture CDKTF will manage for you. You can extend the Construct class to define your own constructs. This way, you can create reusable components that will make up your application infrastructure.

Create construct

Refactor your Deployment using a CDKTF construct.

Create a new directory for your constructs inside the learn-terraform-cdktf-applications/app directory.

Inside the constructs directory, create a new file named kubernetes-web-app.ts for the Kubernetes web application constructs you will create in this tutorial.

constructs/kubernetes-web-app.ts

import { Construct } from "constructs"
import * as kubernetes from "@cdktf/provider-kubernetes"

export interface KubernetesWebAppDeploymentConfig {
  readonly image: string
  readonly replicas: number
  readonly app: string
  readonly component: string
  readonly environment: string
  readonly env?: Record<string, string>
}

export class KubernetesWebAppDeployment extends Construct {
  public readonly resource: kubernetes.deployment.Deployment

  constructor(
    scope: Construct,
    name: string,
    config: KubernetesWebAppDeploymentConfig
  ) {
    super(scope, name)

    this.resource = new kubernetes.deployment.Deployment(this, name, {
      metadata: {
        labels: {
          app: config.app,
          component: config.component,
          environment: config.environment,
        },
        name: `${config.app}-${config.component}-${config.environment}`,
      },
      spec: {
        replicas: config.replicas.toString(),
        selector: {
          matchLabels: {
            app: config.app,
            component: config.component,
            environment: config.environment,
          },
        },
        template: {
          metadata: {
            labels: {
              app: config.app,
              component: config.component,
              environment: config.environment,
            },
          },
          spec: {
            container: [
              {
                image: config.image,
                name: `${config.app}-${config.component}-${config.environment}`,
                env: Object.entries(config.env || {}).map(([name, value]) => ({
                  name,
                  value,
                })),
              },
            ],
          },
        },
      },
    })
  }
}

This code imports the Construct base class, as well as the Kubernetes provider library you installed earlier. Then, it defines an interface you will use to configure your web application deployments. Finally, it defines a class named KubernetesWebAppDeployment which extends the Construct base class to define and configure the Kubernetes Deployments you will use to manage the web application for this tutorial.

Create a file in the constructs directory named index.ts, with the following contents to export the classes and interfaces you define in kubernetes-web-app.ts.

constructs/index.ts

export * from "./kubernetes-web-app"
Add Deployment construct to CDKTF application

Open app/main.ts and import your new Construct near top of the file.

main.ts

import { Construct } from "constructs"
import { App, TerraformStack } from "cdktf"
import * as kubernetes from "@cdktf/provider-kubernetes"
import * as path from "path"

import { KubernetesWebAppDeployment } from "./constructs"

Replace the entire new kubernetes.deployment.Deployment(this, "myapp", { ... }); block with a new instance of your construct.

main.ts

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new kubernetes.provider.KubernetesProvider(this, 'kind', {
      configPath: path.join(__dirname, '../kubeconfig.yaml'),
    })

    new KubernetesWebAppDeployment(this, 'deployment', {
      image: 'nginx:latest',
      replicas: 2,
      app: 'myapp',
      component: 'frontend',
      environment: 'dev',
    })
  }
}

Tip

Be sure to replace the entire kubernetes.deployment.Deployment block. If you get stuck editing the file, replace the entire file with the code below.

Now main.ts will contain:

main.ts

import { Construct } from "constructs"
import { App, TerraformStack } from "cdktf"
import * as kubernetes from "@cdktf/provider-kubernetes"
import * as path from "path"

import { KubernetesWebAppDeployment } from "./constructs"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name)

    new kubernetes.provider.KubernetesProvider(this, 'kind', {
      configPath: path.join(__dirname, '../kubeconfig.yaml'),
    })

    new KubernetesWebAppDeployment(this, 'deployment', {
      image: 'nginx:latest',
      replicas: 2,
      app: 'myapp',
      component: 'frontend',
      environment: 'dev',
    })
  }
}

const app = new App()
new MyStack(app, 'app')
app.synth()

Deploy your application. This will reduce the number of replicas for your deployment from four to two, as specified in the arguments to KubernetesWebAppDeployment. Respond to the confirmation prompt by choosing Approve.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!
##...
     Plan: 1 to add, 0 to change, 1 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_deployment.myapp: Destroying... [id=default/myapp]
app  kubernetes_deployment.deployment_7B0B4E40 (deployment/deployment): Creating...
app  kubernetes_deployment.myapp: Destruction complete after 0s
app  kubernetes_deployment.deployment_7B0B4E40 (deployment/deployment): Creation complete after 8s [id=default/myapp-frontend-dev]
app
     Apply complete! Resources: 1 added, 0 changed, 1 destroyed.


No outputs found.

The name of your deployment resource now includes a unique suffix. CDKTF adds this suffix to resources defined inside of constructs to ensure that each resource in the Terraform configuration it generates has a unique name.

Use kubectl to report your deployment's status.

$ kubectl get deployments
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
myapp-frontend-dev   2/2     2            2           5m14s

Once Kubernetes deploys your app, kubectl will report two running replicas.

Add a test

Since your CDKTF application is written in TypeScript, you can use Jest to unit test it.

First, configure Jest to work with CDKTF. Create a new file called jest.setup.js in the app directory with the following contents:

jest.setup.js

const cdktf = require('cdktf')
cdktf.Testing.setupJest()

Note

This filename ends in .js, not .ts, as required by Jest.

Next, create a new file in the app/__tests__ directory named kubernetes-web-app-test.ts with the following contents.

__tests__/kubernetes-web-app-test.ts

import "cdktf/lib/testing/adapters/jest"
import { Testing } from "cdktf"
import * as kubernetes from "@cdktf/provider-kubernetes"
import { KubernetesWebAppDeployment } from "../constructs"

describe('Our CDKTF Constructs', () => {
  describe('KubernetesWebAppDeployment', () => {
    it('should contain a deployment resource', () => {
      expect(
        Testing.synthScope((scope) => {
          new KubernetesWebAppDeployment(scope, 'myapp-frontend-dev', {
            image: 'nginx:latest',
            replicas: 4,
            app: 'myapp',
            component: 'frontend',
            environment: 'dev',
          })
        })
      ).toHaveResource(kubernetes.deployment.Deployment)
    })
  })
})

In your terminal, run your new test from the app/ directory.

$ npm run test

> app@1.0.0 test
> jest

 PASS  __tests__/main-test.ts
 PASS  __tests__/kubernetes-web-app-test.ts

Test Suites: 2 passed, 2 total
Tests:       1 todo, 1 passed, 2 total
Snapshots:   0 total
Time:        5.109 s
Ran all test suites.

All of your tests should pass.

There are more example tests in main-test.ts. CDKTF generates this file when you run cdktf init.

The nginx:latest container is running in your deployment, but it is not accessible. Next you will add a NodePort Service to make it available on port 30001.

Add NodePort Service construct

In your editor, open app/constructs/kubernetes-web-app.ts.

Add a new interface for your NodePort Service right after the export interface KubernetesDeploymentConfig { ... } block.

constructs/kubernetes-web-app.ts

export interface KubernetesNodePortServiceConfig {
  readonly port: number
  readonly app: string
  readonly component: string
  readonly environment: string
}

Next, add a new KubernetesNodePortService construct after your KubernetesWebAppDeployment class, at the end of the file.

constructs/kubernetes-web-app.ts

export class KubernetesNodePortService extends Construct {
  public readonly resource: kubernetes.service.Service

  constructor(
    scope: Construct,
    name: string,
    config: KubernetesNodePortServiceConfig
  ) {
    super(scope, name)

    this.resource = new kubernetes.service.Service(this, name, {
      metadata: {
        name: `${config.app}-${config.component}-${config.environment}`,
      },
      spec: {
        type: 'NodePort',
        port: [
          {
            port: 80,
            targetPort: '80',
            nodePort: config.port,
            protocol: 'TCP',
          },
        ],
        selector: {
          app: config.app,
          component: config.component,
          environment: config.environment,
        },
      },
    })
  }
}
Test your construct

Now open app/__tests__/kubernetes-web-app-test.ts and add a test.

Near the top of the file, add your new KubernetesNodePortService to the import { ... } from '../constructs'; block, and include a comma after the KubernetesWebAppDeployment.

__tests__/kubernetes-web-app-test.ts

import {
  KubernetesWebAppDeployment,
  KubernetesNodePortService,
} from '../constructs'

Add the test, inside the describe("Our CDKTF Constructs", () => { ... }) block. This test must be on the same level as the describe("KubernetesWebAppDeployment", () => { ... }) block. There will be a final }) at the end of the file after the new test.

__tests__/kubernetes-web-app-test.ts

describe('KubernetesNodePortService', () => {
  it('should contain a Service resource', () => {
    expect(
      Testing.synthScope((scope) => {
        new KubernetesNodePortService(scope, 'myapp-frontend-dev', {
          app: 'myapp',
          component: 'frontend',
          environment: 'dev',
          port: 30001,
        })
      })
    ).toHaveResource(kubernetes.service.Service)
  })
})

Check to make sure your tests still pass. Run the tests in the app/ directory.

$ npm run test

> app@1.0.0 test
> jest

 PASS  __tests__/main-test.ts
 PASS  __tests__/kubernetes-web-app-test.ts

Test Suites: 2 passed, 2 total
Tests:       1 todo, 2 passed, 3 total
Snapshots:   0 total
Time:        5.094 s
Ran all test suites.

Jest should report that all of your tests pass.

In your editor, open app/main.ts, and a new import to the import { ... } from './constructs'; block near the top of the file with the following code, and include a comma after the KubernetesWebAppDeployment.

main.ts

import {
  KubernetesWebAppDeployment,
  KubernetesNodePortService,
} from './constructs'

Add a new KubernetesNodePortService after your KubernetesWebAppDeployment, inside the constructor( ... ) { ... } function block.

main.ts

new KubernetesNodePortService(this, 'service', {
  port: 30001,
  app: 'myapp',
  component: 'frontend',
  environment: 'dev',
})

Deploy your application. Respond to the confirmation prompt by choosing Approve.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!
##...
     Plan: 1 to add, 0 to change, 0 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_service.service_E7C408F2 (service/service): Creating...
app  kubernetes_service.service_E7C408F2 (service/service): Creation complete after 0s [id=default/myapp-frontend-dev]
app
     Apply complete! Resources: 1 added, 0 changed, 0 destroyed.


No outputs found.

Use kubectl to list your running services.

$ kubectl get services
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes           ClusterIP   10.96.0.1       <none>        443/TCP        101m
myapp-frontend-dev   NodePort    10.96.186.212   <none>        80:30001/TCP   22s

Kubectl reports two services, a ClusterIP service, and your new NodePort service.

In may take a minute or two for the frontend to become available. When it is, curl will respond with the NGINX welcome page.

$ curl http://localhost:30001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Open http://localhost:30001 in a new browser tab to review this page.

Now you will refactor your application with a new CDKTF construct. Before you do so, remove the existing application to prevent the new application from attempting to use the same ports as the current one.

Destroy app

Like Terraform, you can use CDKTF to destroy the resources it manages.

Before you refactor your code, destroy your application. Respond to the confirmation prompt by choosing Approve.

$ cdktf destroy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!
##...
     Plan: 0 to add, 0 to change, 2 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
app  kubernetes_service.service_E7C408F2 (service/service): Destroying... [id=default/myapp-frontend-dev]
app  kubernetes_deployment.deployment_7B0B4E40 (deployment/deployment): Destroying... [id=default/myapp-frontend-dev]
app  kubernetes_deployment.deployment_7B0B4E40 (deployment/deployment): Destruction complete after 0s
app  kubernetes_service.service_E7C408F2 (service/service): Destruction complete after 0s
app
     Destroy complete! Resources: 2 destroyed.
Refactor constructs

In your editor, open app/constructs/kubernetes-web-app.ts.

CDKTF provides classes and interfaces for built-in Terraform features, such as output values and functions. Add an import for Terraform outputs to the imports near the top of the file.

constructs/kubernetes-web-app.ts

import { TerraformOutput } from "cdktf"

At the end of the file, add a new construct to represent an application, including a Deployment and a NodePortService.

constructs/kubernetes-web-app.ts

export type SimpleKubernetesWebAppConfig = KubernetesWebAppDeploymentConfig &
  KubernetesNodePortServiceConfig

export class SimpleKubernetesWebApp extends Construct {
  public readonly deployment: KubernetesWebAppDeployment
  public readonly service: KubernetesNodePortService
  public readonly config: SimpleKubernetesWebAppConfig

  constructor(
    scope: Construct,
    name: string,
    config: SimpleKubernetesWebAppConfig
  ) {
    super(scope, name)

    this.config = config
    this.deployment = new KubernetesWebAppDeployment(this, 'deployment', {
      image: config.image,
      replicas: config.replicas,
      app: config.app,
      component: config.component,
      environment: config.environment,
      env: config.env,
    })

    this.service = new KubernetesNodePortService(this, 'service', {
      port: config.port,
      app: config.app,
      component: config.component,
      environment: config.environment,
    })

    new TerraformOutput(this, 'url', {
      value: `http://localhost:${config.port}`,
    })
  }
}

This new class also includes a Terraform output for the URL of your NodePort Service.

Add a test for the SimpleKubernetesWebApp construct

In your editor, add a test to app/__tests__/kubernetes-web-app-test.ts.

Add your SimpleKubernetesWebApp to the import { ... } from '../constructs'; block.

__tests__/kubernetes-web-app-test.ts

import {
  KubernetesWebAppDeployment,
  KubernetesNodePortService,
  SimpleKubernetesWebApp,
} from "../constructs"

Add two new tests inside the describe('Our CDKTF Constructs', () => { ... }); block, on the same level as the describe('KubernetesWebAppDeployment', () => { ... }); block.

__tests__/kubernetes-web-app-test.ts

describe('SimpleKubernetesWebApp', () => {
  it('should contain a Service resource', () => {
    expect(
      Testing.synthScope((scope) => {
        new SimpleKubernetesWebApp(scope, 'myapp-frontend-dev', {
          image: 'nginx:latest',
          replicas: 4,
          app: 'myapp',
          component: 'frontent',
          environment: 'dev',
          port: 30001,
        })
      })
    ).toHaveResource(kubernetes.service.Service)
  })
  it('should contain a Deployment resource', () => {
    expect(
      Testing.synthScope((scope) => {
        new SimpleKubernetesWebApp(scope, 'myapp-frontend-dev', {
          image: 'nginx:latest',
          replicas: 4,
          app: 'myapp',
          component: 'frontent',
          environment: 'dev',
          port: 30001,
        })
      })
    ).toHaveResource(kubernetes.deployment.Deployment)
  })
})

Run the tests from the app/ directory.

$ npm run test

> app@1.0.0 test
> jest

 PASS  __tests__/main-test.ts (6.019 s)
 PASS  __tests__/kubernetes-web-app-test.ts (8.059 s)

Test Suites: 2 passed, 2 total
Tests:       1 todo, 4 passed, 5 total
Snapshots:   0 total
Time:        8.556 s
Ran all test suites.

In your editor, update app/main.ts to import the SimpleKubernetesWebApp construct instead of the seperate Deployment and Service constructs.

Replace the entire import { ... } from "./constructs" block with the following.

main.ts

import { SimpleKubernetesWebApp } from "./constructs"

Remove the new KubernetesWebAppDeployment( ... ); and new KubernetesNodePortService( ... ); blocks from your constructor() function.

Replace the old constructs with the new one:

main.ts

new SimpleKubernetesWebApp(this, 'app_frontend', {
  image: 'nginx:latest',
  replicas: 3,
  port: 30001,
  app: 'myapp',
  component: 'frontend',
  environment: 'dev',
})

Deploy your application. Respond to the confirmation prompt by choosing Approve.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!
##...
     Plan: 2 to add, 0 to change, 0 to destroy.

     Changes to Outputs:
       + app_frontend_url_5DD99814 = "http://localhost:30001"

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
app  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Creating...
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Creating...
app  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Creation complete after 0s [id=default/myapp-frontend-dev]
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Creation complete after 4s [id=default/myapp-frontend-dev]
app
     Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

app
     Outputs:

     app_frontend_url_5DD99814 = "http://localhost:30001"

  app
    app_frontend
      url = http://localhost:30001

Notice that after you refactored your applications resources using constructs, CDKTF assigned the resources new names. Because the names have changed, Terraform will treat them as new resources. If you had not destroyed the old application first, then Terraform would attempt to provision the new resources before destroying the old ones. Since both services use the port 30001, this would result in a port conflict error from Kubernetes.

List your running services with the kubectl command.

$ kubectl get services
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes           ClusterIP   10.96.0.1      <none>        443/TCP        142m
myapp-frontend-dev   NodePort    10.96.216.88   <none>        80:30001/TCP   44s

After Kubernetes is finished deploying your application, curl will once again respond with the NGINX welcome page:

$ curl http://localhost:30001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Visit this URL in your web browser to confirm that your application still works as expected after refactoring your code.

Now you will deploy a custom image to your application's frontend.

Build frontend docker image

Navigate to the frontend directory.

This directory contains Terramino, a Terraform-skinned Tetris game, which you will deploy as a Docker container.

Build a Docker image for your web app:

$ docker build . -t nocorp-frontend
[+] Building 33.0s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                              0.0s
 => => transferring dockerfile: 295B                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                 0.0s
 => => transferring context: 67B                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/node:14                                                                        1.3s
 => [1/7] FROM docker.io/library/node:14@sha256:109b118e0d49dd12ca6f5b84a7a9a9c8a147f75567b3ad50620bdacaf5e6320d                 26.2s
## ...
 => [internal] load build context                                                                                                 0.0s
 => => transferring context: 718.44kB                                                                                             0.0s
 => [2/7] WORKDIR /usr/share/app                                                                                                  0.2s
 => [3/7] COPY package*.json ./                                                                                                   0.0s
 => [4/7] COPY frontend.js ./                                                                                                     0.0s
 => [5/7] COPY public/ ./public/                                                                                                  0.0s
 => [6/7] COPY views/ ./views/                                                                                                    0.0s
 => [7/7] RUN npm install                                                                                                         4.8s
 => exporting to image                                                                                                            0.2s
 => => exporting layers                                                                                                           0.2s
 => => writing image sha256:9dc1ce3668a79770f694ddeae6a5c2236527c381cd429d850eb4a37a8c565ce1                                      0.0s
 => => naming to docker.io/library/nocorp-frontend                                                                                0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Tag your image in the local Docker registry.

$ docker tag nocorp-frontend:latest localhost:5000/nocorp-frontend:latest

Push your image to the registry.

$ docker push localhost:5000/nocorp-frontend:latest
The push refers to repository [localhost:5000/nocorp-frontend]
72d21ab0ed02: Pushed
a831dca05db6: Pushed
66018b4c7669: Pushed
65f4bcfe9daa: Pushed
add6fa0cc975: Pushed
8628cf05347a: Pushed
85bfe2c7cf32: Pushed
28e2f0d3695c: Pushed
b892edc3d92e: Pushed
f1486e967e48: Pushed
5750262417ad: Pushed
9ed3c35b4335: Pushed
6f7f3f280040: Pushed
d6e0d602719c: Pushed
73c3e7ef7bc6: Pushed
latest: digest: sha256:f56babe32077523e891b24af0e38fe00026c7f8ed38b89d90a102aaeeb3d40b8 size: 3465

Now your application's frontend is available as a Docker container that your Kubernetes instance can deploy.

Use new frontend image

In your editor, open app/main.ts, and replace the image parameter with the location of your new image.

app/main.ts

    new SimpleKubernetesWebApp(this, 'app_frontend', {
      image: 'localhost:5000/nocorp-frontend:latest',
      replicas: 3,
      port: 30001,
      app: 'myapp',
      component: 'frontend',
      environment: 'dev',
    });

In your terminal, return to the app directory.

Deploy your new frontend with the new image. Respond to the confirmation prompt with a yes.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
app  - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0

     Terraform has been successfully initialized!
##...
app  Terraform used the selected providers to generate the following execution
     plan. Resource actions are indicated with the following symbols:
       ~ update in-place

     Terraform will perform the following actions:
app    # kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment) will be updated in-place
       ~ resource "kubernetes_deployment" "app_frontend_deployment_0EE98C72" {
             id               = "default/myapp-frontend-dev"
             # (1 unchanged attribute hidden)

           ~ spec {
                 # (5 unchanged attributes hidden)

               ~ template {

                   ~ spec {
                         # (11 unchanged attributes hidden)

                       ~ container {
                           ~ image                      = "nginx:latest" -> "localhost:5000/nocorp-frontend:latest"
                             name                       = "myapp-frontend-dev"
                             # (8 unchanged attributes hidden)
##...
     Plan: 0 to add, 1 to change, 0 to destroy.

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Modifying... [id=default/myapp-frontend-dev]
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Still modifying... [id=default/myapp-frontend-dev, 10s elapsed]
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Still modifying... [id=default/myapp-frontend-dev, 20s elapsed]
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Modifications complete after 25s [id=default/myapp-frontend-dev]
app
     Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

     Outputs:


app  app_frontend_url_5DD99814 = "http://localhost:30001"

  app
    app_frontend
      url = http://localhost:30001

Kubernetes will take a few minutes to deploy the new container image. Use kubectl to check your deployment's status.

$ kubectl get deployments
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
myapp-frontend-dev   3/3     3            3           43s

Once all three replicas are up-to-date, curl will respond with a Terramino page instead of the NGINX welcome page.

$ curl http://localhost:30001
<!DOCTYPE html><html><head><title>Terranimo</title></head><link rel="stylesheet" href="/styles/terramino.css"></html><body><div class="score" id="score"></div><div class="container"><div class="content"><h1>Terramino</h1><p class="error" id="errorMessage">Could not connect to server! <br/> Reload to try again.</p><p>Move: ← →  Rotate: ↑  Drop: ↓</p></div><div class="content"><canvas width="320" height="640" id="game"></canvas></div></div><script src="/scripts/terramino.js"></script><script>start('http://localhost:30002')</script></body>

Visit http://localhost:30001 to review the application. The app will display an error, because you have not yet deployed the backend server.

Now you will deploy a backend component to your application, using the SimpleKubernetesWebApp construct.

Build backend image

In your terminal, navigate to the backend directory.

Use NPM to build and push the Docker image for your application backend.

$ npm run deploy
> nocorp-backend-app@1.0.0 deploy /Users/<YOU>/code/learn-terraform-cdktf-applications/backend
> npm run build && npm run tag && npm run push


> nocorp-backend-app@1.0.0 build /Users/<YOU>/code/learn-terraform-cdktf-applications/backend
> docker build . -t nocorp-backend
## ...
The push refers to repository [localhost:5000/nocorp-backend]
676744aedcf3: Pushed
e64b93b2c9bd: Pushed
8628cf05347a: Mounted from nocorp-frontend
85bfe2c7cf32: Mounted from nocorp-frontend
28e2f0d3695c: Mounted from nocorp-frontend
b892edc3d92e: Mounted from nocorp-frontend
f1486e967e48: Mounted from nocorp-frontend
5750262417ad: Mounted from nocorp-frontend
9ed3c35b4335: Mounted from nocorp-frontend
6f7f3f280040: Mounted from nocorp-frontend
d6e0d602719c: Mounted from nocorp-frontend
73c3e7ef7bc6: Mounted from nocorp-frontend
latest: digest: sha256:845e60dd8350066a89757f1bdb200584f317e5b15d68cc9609a3c359f3736676 size: 2841

This command automatically ran the same build, tag, and push steps that you ran manually for your frontend image.

In your editor, open app/main.ts, and add a new SimpleKubernetesWebApp immediately before the new SimpleKubernetesWebApp(this, 'app_frontend', { ... }); block.

main.ts

const app_backend = new SimpleKubernetesWebApp(this, 'app_backend', {
  image: 'localhost:5000/nocorp-backend:latest',
  replicas: 1,
  port: 30002,
  app: 'myapp',
  component: 'backend',
  environment: 'dev',
})

Your application's frontend needs access to the backend's URL. Update the frontend block in app/main.ts to pass this as an environment variable.

main.ts

new SimpleKubernetesWebApp(this, 'app_frontend', {
  image: 'localhost:5000/nocorp-frontend:latest',
  replicas: 3,
  port: 30001,
  app: 'myapp',
  component: 'frontend',
  environment: 'dev',
  env: { BACKEND_APP_URL: `http://localhost:${app_backend.config.port}` },
})

In your terminal, return to the app directory.

Deploy your new backend. Respond to the confirmation prompt with a yes.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
     - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0

     Terraform has been successfully initialized!
##...
app  Terraform used the selected providers to generate the following execution
     plan. Resource actions are indicated with the following symbols:
       + create
       ~ update in-place

     Terraform will perform the following actions:
app    # kubernetes_deployment.app_backend_deployment_1A8B5520 (app_backend/deployment/deployment) will be created
       + resource "kubernetes_deployment" "app_backend_deployment_1A8B5520" {
##...
       # kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment) will be updated in-place
       ~ resource "kubernetes_deployment" "app_frontend_deployment_0EE98C72" {
             id               = "default/myapp-frontend-dev"
##...
                           + env {
                               + name  = "BACKEND_APP_URL"
                               + value = "http://localhost:30002"
                             }
##...
       # kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service) will be created
       + resource "kubernetes_service" "app_backend_service_EAD583EF" {
##...
     Plan: 2 to add, 1 to change, 0 to destroy.

     Changes to Outputs:
       + app_backend_url_CAA2B50B = "http://localhost:30002"

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Creating...
app  kubernetes_deployment.app_backend_deployment_1A8B5520 (app_backend/deployment/deployment): Creating...
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Modifying... [id=default/myapp-frontend-dev]
app  kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Creation complete after 0s [id=default/myapp-backend-dev]
app  kubernetes_deployment.app_backend_deployment_1A8B5520 (app_backend/deployment/deployment): Creation complete after 1s [id=default/myapp-backend-dev]
app  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Modifications complete after 3s [id=default/myapp-frontend-dev]
app
     Apply complete! Resources: 2 added, 1 changed, 0 destroyed.

     Outputs:

     app_backend_url_CAA2B50B = "http://localhost:30002"
     app_frontend_url_5DD99814 = "http://localhost:30001"

  app
    app_backend
      url = http://localhost:30002

    app_frontend
      url = http://localhost:30001

It may take Kubernetes a few minutes to deploy your new resources. Use kubectl to check the status.

$ kubectl get deployments
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
myapp-backend-dev    1/1     1            1           57s
myapp-frontend-dev   3/3     3            3           15m

Once your kubectl reports all four replicas as ready (three myapp-frontend-dev and one myapp-backend-dev), your application will be available.

Visit your application's frontend URL (http://localhost:30001) in your web browser. You may need to reload the page for the application to work.

You can also use curl to interact directly with the backend server:

$ curl http://localhost:30002/new
{"game":{"id":"pdSW6Cymw","state":"running","score":0,"tetrominoSequence":["I","J","Z","O","T","S"]},"tetromino":"L"}

CDKTF projects can support multiple application stacks. Each CDKTF Stack is a separate Terraform project that you can manage independently. So far in this tutorial, you have deployed your resources as a single stack. In this section, you will deploy a new copy of your application as a new stack representing a test environment.

Refactor application stack

Currently, your stack's frontend and backend services are configured using hard-coded values that you pass to SimpleKubernetesWebApp in main.ts.

Refactor your MyApp stack to accept your application's configuration as an argument instead of hard-coding it in the constructor() function.

In your editor, open app/main.ts, and add the SimpleKubernetesWebAppConfig interface to your list of imports near the top of the file.

main.ts

import {
  SimpleKubernetesWebApp,
  SimpleKubernetesWebAppConfig,
} from "./constructs"

Next, update the class's constructor to accept two SimpleKubernetesWebAppConfig objects, and pass them to your frontend and backend.

Replace the entire class MyStack extends TerraformStack { ... } block with the following.

main.ts

class MyStack extends TerraformStack {
  constructor(
    scope: Construct,
    name: string,
    config: {
      frontend: SimpleKubernetesWebAppConfig
      backend: SimpleKubernetesWebAppConfig
    }
  ) {
    super(scope, name)

    new kubernetes.provider.KubernetesProvider(this, 'kind', {
      configPath: path.join(__dirname, '../kubeconfig.yaml'),
    })

    const app_backend = new SimpleKubernetesWebApp(
      this,
      'app_backend',
      config.backend
    )
    new SimpleKubernetesWebApp(this, 'app_frontend', {
      ...config.frontend,
      env: { BACKEND_APP_URL: `http://localhost:${app_backend.config.port}` },
    })
  }
}

Finally, pass these configuration objects when you create your application object near the end of the file. Replace the current new MyStack... line with the following, immediately before the last line, app.synth();.

main.ts

const app = new App()
new MyStack(app, 'app', {
  frontend: {
    image: 'localhost:5000/nocorp-frontend:latest',
    replicas: 3,
    port: 30001,
    app: 'myapp',
    component: 'frontend',
    environment: 'dev',
  },
  backend: {
    image: 'localhost:5000/nocorp-backend:latest',
    replicas: 1,
    port: 30002,
    app: 'myapp',
    component: 'backend',
    environment: 'dev',
  },
})

app.synth()

In your terminal, run cdktf deploy. Since the Terraform configuration generated by CDKTF has not changed, there will be no changes to deploy.

$ cdktf deploy
app  Initializing the backend...
app  Initializing provider plugins...
     - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.14.0
app  Terraform has been successfully initialized!
##...
app  No changes. Your infrastructure matches the configuration.


app  Terraform has compared your real infrastructure against your configuration
     and found no differences, so no changes are needed.

  app
    app_frontend
      url = http://localhost:30001

    app_backend
      url = http://localhost:30002
Add a second stack

Now, add a second stack to main.ts to represent your test environment, immediately before the last line, app.synth();.

main.ts

new MyStack(app, 'app-test', {
  frontend: {
    image: 'localhost:5000/nocorp-frontend:latest',
    replicas: 4,
    port: 30003,
    app: 'myapp',
    component: 'frontend',
    environment: 'test',
  },
  backend: {
    image: 'localhost:5000/nocorp-backend:latest',
    replicas: 2,
    port: 30004,
    app: 'myapp',
    component: 'backend',
    environment: 'test',
  },
})

Now that your project contains more than one stack, you must specify a stack when you run CDKTF commands.

Deploy your new stack. Respond to the confirmation prompt by choosing Approve.

$ cdktf deploy app-test
app-test  Initializing the backend...
app-test
          Successfully configured the backend "local"! Terraform will automatically
          use this backend unless the backend configuration changes.
app-test  Initializing provider plugins...
          - Finding hashicorp/kubernetes versions matching "2.14.0"...
app-test  - Using hashicorp/kubernetes v2.14.0 from the shared cache directory
app-test  Terraform has created a lock file .terraform.lock.hcl to record the provider
          selections it made above. Include this file in your version control repository
          so that Terraform can guarantee to make the same selections by default when
          you run "terraform init" in the future.
##...
Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app-test  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Creating...
          kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Creating...
app-test  kubernetes_deployment.app_backend_deployment_1A8B5520 (app_backend/deployment/deployment): Creating...
app-test  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Creating...
app-test  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Creation complete after 0s [id=default/myapp-frontend-test]
app-test  kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Creation complete after 0s [id=default/myapp-backend-test]
app-test  kubernetes_deployment.app_frontend_deployment_0EE98C72 (app_frontend/deployment/deployment): Creation complete after 8s [id=default/myapp-frontend-test]
app-test  kubernetes_deployment.app_backend_deployment_1A8B5520 (app_backend/deployment/deployment): Creation complete after 8s [id=default/myapp-backend-test]
app-test
          Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

app-test
          Outputs:

          app_backend_url_CAA2B50B = "http://localhost:30004"
          app_frontend_url_5DD99814 = "http://localhost:30003"

  app-test
    app_backend
      url = http://localhost:30004

    app_frontend
      url = http://localhost:30003

Since each stack is a separate Terraform project with separate state, you can deploy, update, and destroy them independently.

Confirm that kubernetes has deployed your test stack's frontend and backend with kubectl.

$ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
myapp-backend-dev     1/1     1            1           46m
myapp-backend-test    2/2     2            2           71s
myapp-frontend-dev    3/3     3            3           60m
myapp-frontend-test   4/4     4            4           71s

This command will list both your "-dev" and "-test" deployments, since they are running on the same Kubernetes cluster.

In this section, you will delete your CDKTF application stacks, your kind cluster, and your local Docker image registry.

Destroy your app-test stack. Respond to the confirmation prompt by choosing Approve.

$ cdktf destroy app-test
app-test  Initializing the backend...
app-test  Initializing provider plugins...
          - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app-test  - Using previously-installed hashicorp/kubernetes v2.14.0
app-test  Terraform has been successfully initialized!
##...
          Plan: 0 to add, 0 to change, 4 to destroy.

app-test
          Changes to Outputs:
            - app_backend_url_CAA2B50B  = "http://localhost:30004" -> null
            - app_frontend_url_5DD99814 = "http://localhost:30003" -> null

          ─────────────────────────────────────────────────────────────────────────────

          Saved the plan to: plan

          To perform exactly these actions, run the following command to apply:
              terraform apply "plan"

Please review the diff output above for app-test
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app-test  kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Destruction complete after 0s
app-test  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Destruction complete after 0s
app-test
          Destroy complete! Resources: 4 destroyed.

Next, destroy your app stack. Respond to the confirmation prompt by choosing Approve.

$ cdktf destroy app
app  Initializing the backend...
app  Initializing provider plugins...
     - Reusing previous version of hashicorp/kubernetes from the dependency lock file
app  - Using previously-installed hashicorp/kubernetes v2.12.1
app  Terraform has been successfully initialized!
##...
     Plan: 0 to add, 0 to change, 4 to destroy.

     Changes to Outputs:
     - app_frontend_url_FE3D723A = "http://localhost:30001" -> null
     - app_backend_url_91B41C22   = "http://localhost:30002" -> null

     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
     terraform apply "plan"

Please review the diff output above for app
❯ Approve  Applies the changes outlined in the plan.
  Dismiss
  Stop
##...
app  kubernetes_service.app_backend_service_EAD583EF (app_backend/service/service): Destruction complete after 0s
app  kubernetes_service.app_frontend_service_C2863249 (app_frontend/service/service): Destruction complete after 0s
app
     Destroy complete! Resources: 4 destroyed.

Delete your kind cluster.

$ kind delete cluster --name=cdktf-app
Deleting cluster "cdktf-app" ...

Stop your local registry Docker container.

$ docker stop local-registry
local-registry

Remove the container.

$ docker rm local-registry
local-registry

In this tutorial, you used the CDK for Terraform to deploy an application to a local Kubernetes cluster. You used CDK Constructs to refactor your application, and deployed multiple copies of your entire application as separate stacks.

Learn more about how to use the CDK for Terraform to manage Terraform projects by reviewing the following resources:


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4