How to Deploy a Containerized Web App in a Kubernetes Cluster Using Amazon EKS

F. Raisa Iftekher
9 min readDec 4, 2022

--

Kubernetes is an open-source system that automates the deployment, scaling, and management of containerized applications and Amazon Elastic Kubernetes Service (EKS) is a service that you can use to run Kubernetes on AWS.

The following instructions will allow you to deploy a containerized web application on a Kubernetes cluster using AWS CDKv2, Amazon EKS, AWS IAM, AWS CloudFormation, Amazon ECR/ECS (if you decide to create your own containerized app instead of the sample app provided in this tutorial), kubectl-CLI, eksctl-CLI, and cdk8s-CLI. The programming language I will use in this tutorial when providing examples is Python.

Table of Contents

Get Started with EKS

You will need to install kubectl and eksctl. kubectl is the command line tool we will use to communicate with the Kubernetes API server and eksctl is the command line tool we will use to create and manage Kubernetes clusters on Amazon EKS. You can also follow along on Amazon’s EKS User Guide.

  1. Create an IAM user with necessary permissions (administrator-level access) — IAM User Guide instructions and configure aws to set up your AWS CLI. **
  2. Create your directories with the following structure:
- project
- eks
- cdk8s
- cluster

2. Install kubectl: Follow Amazon EKS’s User Guide instructions.

3. Install eksctl: Follow Amazon EKS’s User Guide instructions.

** Note: Though this is a basic task, it is crucial to not forget it as this can be the main cause for why some people encounter errors when trying to access their Kubernetes cluster on EKS in later steps.

Build AWS CDK Application, Create Your Cluster, and Deploy CDK Stack

Now, create your CDK project using your preferred language and deploy it. I will be using Python. These instructions assume you are using aws-cdk-lib>=2.0.0.

  1. Navigate to your eks/cluster project folder and use the following command to create your skeleton CDK app (replace python with your language of choice):
cdk init app --language=python

2. Then, if you’re using Python, make sure to create a virtual environment:

python3 -m venv .venv
  • After the init process completes and the virtual env is created, you can use the following command to activate your virtual env and install the AWS CDK core dependencies:
source .venv/bin/activate
python -m pip install -r reqirements.txt
  • Note: to deactivate your virtual environment:
deactivate

3. To verify that everything worked thus far, list the stacks in your app by running the following command and making sure that the output is ClusterStack:

cdk ls
ClusterStack is listed

4. Navigate to eks/cluster/app.py and uncomment line 23 to configure CDK to so that it knows which Account ID and region to use in order to deploy the CDK stack:

  • Change by replacing the strings for account and region:
# env=cdk.Environment(account='1234234234', region='region'),
  • to `account=os.getenv(‘CDK_DEFAULT_ACCOUNT’)` and `region=os.getenv(‘CDK_DEFAULT_REGION’)`
env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
  • Re-verify that everything is still working by making sure ClusterStack is still listed as an output when you run the following command:
cdk ls

5. Navigate to eks/cluster/cluster_stack.py and add the following code to look up the default VPN, define the IAM role, and create an EKS cluster (you can update the version from V1_20 to more recent versions if you wish to do so):

from aws_cdk import (
Stack,
aws_ec2 as ec2,
aws_eks as eks,
aws_iam as iam
)

from constructs import Construct

class ClusterStack(Stack):

def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

# The code that defines your stack goes here

# Look up the default VPC
vpc = ec2.Vpc.from_lookup(self, id="VPC", is_default=True)

# Create the master role for EKS Cluster
iam_role = iam.Role(self, id=f"{construct_id}-iam", role_name=f"{construct_id}-am", assumed_by=iam.AccountRootPrincipal())

# Creating cluster with EKS
eks_cluster = eks.Cluster(
self, id=f"{construct_id}-cluster",
cluster_name=f"{construct_id}-cluster",
vpc=vpc,
vpc_subnets=vpc.public_subnets,
masters_role=iam_role,
default_capacity_instance=ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
version=eks.KubernetesVersion.V1_20
)

6. Before you can use CDK, it needs to be bootstrapped. Bootstrapping is the process of provisioning resources for the AWS CDK before deploying AWS CDK apps into an AWS environment. Essentially, this command will create the necessary infrastructure for CDK to manage infrastructure in your account. Run the following command:

cdk bootstrap
  • You should eventually get a similar output to the following:

7. Now you are read to deploy the cluster by running the command:

cdk deploy
  • If your cluster was successfully deployed, then you should get some tables similar to the following:

8. CDK will prompt you before creating the infrastructure because it is creating infrastructure that changes security configuration (in our case, we created IAM roles and security groups).

  • Press y, hit enter to deploy and then wait for the deployment to finish.

9. When your cluster is successfully deployed, you should get a green checkmark next to your output and 2 commands:

  • ClusterStack.ClusterStackclusterConfigCommand
  • ClusterStack.ClusterStackclusterGetTokenCommand

10. Copy the ClusterStack.ClusterStackclusterConfigCommand and run the command. The command should start with `aws eks update-kubeconfig — name…`

  • This will allow your device to connect to and operate your cluster by updating your Kubernetes configuration (kubeconfig) to point to your cluster so that the kubectl command will work.
  • Your output should be something similar to the following:
Added ew context arn:aws:eks:eu-west-1:0123456789012:cluster/ClusterStack-cluster to /home/ubuntu/.kube/config

11. To confirm that everything is configured correctly, run the command:

kubectl get all
  • If everything was configured correctly, you should get a similar output as the following:

Install cdk8s-CLI

The following instructions are to set up AWS CDK for Kubernetes (cdk8s). cdk8s is a framework for defining Kubernetes apps and reusable abstractions. cdk8s is a separate tool which you will need to install in addition to CDK. cdk8s-CLI is the main tool that you will use to define service and deployment for the application you will deploy. The AWS cdk8s outputs a YAML file, a Kubernetes configuration file, that you can use to deploy applications using kubectl or AWS CDK.

  • Check out the cdk8s CLI Guide
  1. Install cdk8s:
  • You can use Homebrew:
brew install cdk8s
  • or use npm
npm install -g cdk8s-cli

2. If you are using Python, install pipenv. cdk8s uses pipenv for Python based projects to consolidate and simplify the Python development process to a single command line tool.

pip3 install pip3

3. Navigate to your eks/cdk8s project folder and create a CDK8s application to generate a project skeleton that includes a Python env and base libraries by running the command:

cdk8s init python-app
  • Once the files and folder structure is finished being created, you are now ready to define your app config in order to to deploy your app to the EKS cluster

Create Service & Deployment Configurations For Your App and Deploy Your App Into Your EKS Cluster

In the following instructions, you will use cdk8s constructs to define Kubernetes API Objects. These constructs can be used to create services and deployments configurations for your app and use the Kubernetes config file (a YAML file) that is generated to integrate it with the AWS CDK code from the previous steps to deploy your application.

You can deploy your own containerized app to ECR. However, for the purposes of this tutorial, the AWS Developer Center contains a sample containerized application that you can use.

The URL for this container image is: `public.ecr.aws/s9u7u6x1/sample_app_001:no-db`.

Note the cdk8s.k8s.yaml

There are 4 steps necessary to deploy your app:

  1. Import the k8s library to generate the strings for the YAML Kubernetes config file.

2. In the service lookup, define the label set to apply to the pods.

  • Labels are key/value pairs attached to Kubernetes objects.
  • You will be tagging the pods to run them as part of the deployment and then the ReplicationController will use the labels to select the pods when in step 4, it is creating the service to expose them to the load balancer.
  • The label you will use is : {"app”: “cdk8s”}

3. Define a deployment to spin up the container on the cluster but do not configure it to receive traffic from the load balancer.

4. Expose the deployment to the load balancer by defining a service.

  1. Navigate to the file eks/cdk8/main.py, add the following code to main.py:
# main.py

#!/usr/bin/env python
from constructs import Construct
from cdk8s import App, Chart
from imports import k8s

# super() is called 2x because it creates the YAML file with the specified name
# and appends any config generated after that point by calling the parent class
# constructor that this class inherits from
class MyChart(Chart):
def __init__(self, scope: Construct, id: str):
super().__init__(scope, id) # generates a file for the service config

# define resources here

# Label that will be used to tag pods
label = {"app": "cdk8s"}

k8s.KubeDeployment(self, 'deployment',
spec = k8s.DeploymentSpec(
replicas = 2,
selector = k8s.LabelSelector(match_labels = label),
template = k8s.PodTemplateSpec(
metadata = k8s.ObjectMeta(labels = label),
spec = k8s.PodSpec(containers = [
k8s.Container(
name = 'cdk8s',
image = 'public.ecr.aws/s9u7u6x1/sample_app_001:no-db', # update this line with your own app image
ports = [k8s.ContainerPort(container_port = 80)])]))))

# Create the service to expose the pods to traffic from teh load balancer
super().__init__(scope, f"{id}-service") # generates a 2nd file for the deployment

k8s.KubeService(self, 'service',
spec = k8s.ServiceSpec(
type = 'LoadBalancer',
ports = [k8s.ServicePort(port = 80, target_port = k8s.IntOrString.from_number(80))],
selector = label))

app = App()
MyChart(app, "cdk8s")

app.synth()

2. Run the command to generate the config files:

cdk8s synth
  • It should create 2 config files in your cdk8s/dist folder, the service and deployment config files:

3. Navigate to eks/cluster/cluster/cluster_stack.py and install pyyaml

pip install pyyaml

4. Then add the following code to your cluster_stack.py file:

from aws_cdk import (
Stack,
aws_ec2 as ec2,
aws_eks as eks,
aws_iam as iam
)
from constructs import Construct
import yaml

class ClusterStack(Stack):

def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

# Look up the default VPC
vpc = ec2.Vpc.from_lookup(self, id="VPC", is_default=True)

# Create the master role for EKS Cluster
iam_role = iam.Role(self, id=f"{construct_id}-iam", role_name=f"{construct_id}-am", assumed_by=iam.AccountRootPrincipal())

# Creating the Kubernetes cluster with EKS
eks_cluster = eks.Cluster(
self,
id=f"{construct_id}-cluster",
cluster_name=f"{construct_id}-cluster",
vpc=vpc,
vpc_subnets=vpc.public_subnets,
masters_role=iam_role,
default_capacity_instance=ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
version=eks.KubernetesVersion.V1_20
)

# Read deployment config YAML file and create deployment_yaml variable to pass to the cluster
with open("../cdk8s/dist/cdk8s.k8s.yaml", 'r') as stream:
deployment_yaml = yaml.load(stream, Loader=yaml.FullLoader)

# Read service config YAML file and create service_yaml variable to pass to the cluster
with open("../cdk8s/dist/cdk8s-service.k8s.yaml") as stream:
service_yaml = yaml.load(stream, Loader=yaml.FullLoader)

# Pass the YAML files to the cluster using the add_manifest method to apply the service and deployment Kubernetes manifests to the cluster
eks_cluster.add_manifest(f"{construct_id}-app-deployment", deployment_yaml)
eks_cluster.add_manifest(f"{construct_id}-app-service", service_yaml)

5. After adding this code, to make sure you have no errors in your codebase thus far, run the command and make sure your stack is outputted correctly:

cdk ls

6. Make sure you are in your eks/cluster directory and deploy your app by running the command:

cdk deploy

7. Once your app has been successfully deployed by AWS CDK, you can use the following command to make sure you’re using the correct context before using kubectl:

kubectl config current-context
  • Make sure the output is correct.

8. Check the status of the app in the pods by running the command:

kubectl get all
  • If everything has been configured correctly, you should get an output that is similar to the following:

If you’ve made it this far, congrats! You’re almost finished!

9. In your terminal after you ran the command kubectl get all , copy the EXTERNAL-IP address that corresponds with the LoadBalancer value and open it in your browser. It should be a mix of numbers and letters and look something like the following:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx–xxxxxxxxx.us-east-x.elb.amazonaws.com

10. Once your browser finishes loading, you should see your app!

  • If you used the sample URL provided (public.ecr.aws/s9u7u6x1/sample_app_001:no-db), then you should see the following app:
Sample containerized web app deployed into a Kubernetes cluster on Amazon EKS
  • Congrats! You just finished deploying your app into a Kubernetes cluster on Amazon EKS.

Cleaning Up Your AWS Environment so That You Don’t Get Charged

If you’re using a free tier account, then it costs about $75.00 USD / month to run this app or 10c USD per hour.

  1. To remove all the infrastructure you created during this tutorial, use the command and press y when prompted:
cdk destroy

--

--

F. Raisa Iftekher
F. Raisa Iftekher

No responses yet