Guide to Creating Resources on EKS Using AWS CDK Manifest

Introduction

In the previous article, I guided you through using AWS EKS including 2 steps: using AWS CDK to create the cluster and permissions, then using yml files to apply and create k8s resources. This method is useful in case you are familiar with K8s and have many K8s resources that need to be managed in a separate project.

If you want to only use AWS CDK, it still supports creating K8s Manifests directly within the project, so that after creating the cluster, K8s resources can be created immediately, helping the deployment process take place seamlessly and limiting mutual dependencies.

Detail

Create file lib/eks-manifest-stack.ts:

import { KubectlV34Layer } from "@aws-cdk/lambda-layer-kubectl-v34"
import * as cdk from "aws-cdk-lib"
import * as ec2 from "aws-cdk-lib/aws-ec2"
import * as eks from "aws-cdk-lib/aws-eks"
import * as iam from "aws-cdk-lib/aws-iam"
import { Construct } from "constructs"

export class EksManifestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    const vpc = new ec2.Vpc(this, "EksVpc", {
      maxAzs: 2,
      natGateways: 0,
      subnetConfiguration: [
        { name: "PublicSubnet", subnetType: ec2.SubnetType.PUBLIC },
      ],
    })

    const cluster = new eks.Cluster(this, "MyEksCluster", {
      vpc,
      vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }],
      defaultCapacity: 0,
      version: eks.KubernetesVersion.V1_34,
      kubectlLayer: new KubectlV34Layer(this, "KubectlLayer"),
      authenticationMode: eks.AuthenticationMode.API_AND_CONFIG_MAP,
      bootstrapClusterCreatorAdminPermissions: true,
    })

    const nodeGroup = cluster.addNodegroupCapacity("PublicNodeGroup", {
      instanceTypes: [new ec2.InstanceType("t3.small")],
      minSize: 1,
      maxSize: 2,
      subnets: { subnetType: ec2.SubnetType.PUBLIC },
      amiType: eks.NodegroupAmiType.AL2023_X86_64_STANDARD,
    })

    const s3Policy = new iam.PolicyStatement({
      actions: ["s3:PutObject", "s3:GetObject", "s3:ListBucket"],
      resources: ["*"],
    })

    const serviceAccount = cluster.addServiceAccount("NestJsServiceAccount", {
      name: "nestjs-s3-sa",
      namespace: "default",
    })
    serviceAccount.addToPrincipalPolicy(s3Policy)

    const nestJsAppResource = new eks.KubernetesManifest(
      this,
      "NestJsAppResources",
      {
        cluster,
        manifest: [
          {
            apiVersion: "apps/v1",
            kind: "Deployment",
            metadata: { name: "nestjs-app", namespace: "default" },
            spec: {
              replicas: 1,
              selector: { matchLabels: { app: "nestjs" } },
              template: {
                metadata: { labels: { app: "nestjs" } },
                spec: {
                  serviceAccountName: serviceAccount.serviceAccountName,
                  containers: [
                    {
                      name: "nestjs-container",
                      image:
                        "758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/cdk-hnb659fds-container-assets-758222924841-ap-southeast-1:35e3cfdaa7395cdc051c3a6eb6c8390d0c1ffde54debfa49d6c60865ae98a60d",
                      ports: [{ containerPort: 3000 }],
                      env: [
                        { name: "REGION", value: "ap-southeast-1" },
                        { name: "BUCKET", value: "bucket-public-cb2a91d6" },
                      ],
                    },
                  ],
                },
              },
            },
          },
          {
            apiVersion: "v1",
            kind: "Service",
            metadata: { name: "nestjs-service", namespace: "default" },
            spec: {
              type: "LoadBalancer",
              selector: { app: "nestjs" },
              ports: [{ protocol: "TCP", port: 80, targetPort: 3000 }],
            },
          },
        ],
      },
    )

    nestJsAppResource.node.addDependency(nodeGroup)

    const lbAddress = cluster.getServiceLoadBalancerAddress("nestjs-service", {
      namespace: "default",
    })

    new cdk.CfnOutput(this, "LoadBalancerAddress", {
      value: lbAddress,
      description: "DNS to access Load Balancer",
    })
    new cdk.CfnOutput(this, "ClusterName", {
      value: cluster.clusterName,
    })
  }
}

  • The creation of vpc, cluster, nodeGroup, s3Policy, and serviceAccount is similar to the previous article I mentioned, you can review it to grasp the information; the only note is that now we do not need grantAccess anymore because there is no need to authorize for manual yml file application
  • KubernetesManifest: this is the content of the k8s.yml file used directly here
  • addDependency: ensures the k8s manifest is only applied after the node group is ready
  • lbAddress: this is the external IP used to access the Load Balancer


Use in file bin/aws-cdk.ts

#!/usr/bin/env node
import * as cdk from "aws-cdk-lib/core"
import { EksManifestStack } from "../lib/eks/eks-manifest-stack"

const app = new cdk.App()
new EksManifestStack(app, "EksManifestStack")


After successful deployment

 EksManifestStack
 Deployment time: 934.09s

Outputs:
EksManifestStack.LoadBalancerAddress = a7189d29831a940d5b198ad8e5ca52ec-752782290.ap-southeast-1.elb.amazonaws.com
EksManifestStack.ClusterName = MyEksCluster83497DF9-708a95f5070f4074a752016f2af065c2

 Total time: 968.52s


Cluster created on AWS Console


Please use LoadBalancerAddress to test the api

Happy coding!

See more articles here.

Comments

Popular posts from this blog

All practice series

Deploying a NodeJS Server on Google Kubernetes Engine

Setting up Kubernetes Dashboard with Kind

Using Kafka with Docker and NodeJS

Monitoring with cAdvisor, Prometheus and Grafana on Docker

Kubernetes Practice Series

Kubernetes Deployment for Zero Downtime

Practicing with Google Cloud Platform - Google Kubernetes Engine to deploy nginx

NodeJS Practice Series

Helm for beginer - Deploy nginx to Google Kubernetes Engine