Kubernetes (K8s) has become the de facto standard for orchestrating containerized applications. It provides powerful primitives for deploying, scaling, and managing containerized workloads, making it a top choice for modern DevOps teams and cloud-native development.
In this blog series, we’ll explore how to set up a production-ready Kubernetes environment on AWS using Amazon Elastic Kubernetes Service (EKS) and Terraform, starting with the foundational infrastructure.
Why EKS?
Amazon EKS is a fully managed Kubernetes service that makes it easy to run Kubernetes on AWS without needing to install or operate your own control plane or nodes. EKS handles high availability, scalability, and patching of the Kubernetes control plane, so you can focus on running your applications instead of managing infrastructure.
Benefits of using EKS:
- Managed control plane: No need to run your own etcd or master nodes.
- Native AWS integration: IAM, VPC, CloudWatch, ALB, and more.
- Secure by default: Runs in a dedicated, isolated VPC.
- Scalable and production-ready.
Terraform AWS Modules by Anton Babenko
Rather than writing complex Terraform configurations from scratch, we leverage terraform-aws-modules maintained by the community and led by Anton Babenko. These modules follow AWS best practices, are actively maintained, and support production-grade deployments with minimal boilerplate.
In our setup:
- The VPC module creates a network with public and private subnets.
- The EKS module provisions the EKS control plane and worker nodes in private subnets.
Architecture Overview
Here’s how the setup works at a high level:
- VPC is created with 3 Availability Zones for high availability.
- Each AZ contains both a public and a private subnet.
- EKS worker nodes (EC2 instances) are launched in private subnets for better security.
- A NAT Gateway is provisioned in a public subnet to allow worker nodes in private subnets to pull images and updates from the internet (e.g., from ECR, Docker Hub).
- EKS control plane (managed by AWS) communicates with the worker nodes securely within the VPC.
This setup ensures that your nodes are not directly exposed to the internet while still having outbound internet access via the NAT gateway.
Step 1: Create the VPC
We start by provisioning a robust VPC with 3 private and 3 public subnets across different AZs:
####################################################################################
### VPC Module Configuration
####################################################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = var.vpc_name
cidr = var.vpc_cidr_block
azs = slice(data.aws_availability_zones.available_zones.names, 0, 3)
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_name
Environment = var.environment
Terraform = "true"
}
}
Step 2: Create the EKS Cluster
We use the terraform-aws-eks module to spin up the cluster. This will provision:
- A managed EKS control plane
- A node group with autoscaling enabled
- Nodes inside private subnets with internet access via NAT Gateway
####################################################################################
### EKS Cluster Module Configuration
####################################################################################
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = var.eks_cluster_name
cluster_version = "1.32"
cluster_endpoint_public_access = true
enable_cluster_creator_admin_permissions = true
eks_managed_node_groups = {
example = {
instance_types = ["t3.medium"]
min_size = 1
max_size = 5
desired_size = 2
}
}
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
tags = {
Name = var.eks_cluster_name
Environment = var.environment
Terraform = "true"
}
}
Terraform apply to create EKS Cluster:
Apply complete! Resources: 60 added, 0 changed, 0 destroyed.
Outputs:
cluster_endpoint = "https://EA6F63CF5CF44B594EA9533013CF21C4.gr7.us-east-1.eks.amazonaws.com"
cluster_name = "CT-EKS-Cluster"
Step 3: Update Your Kubeconfig
Once the cluster is created, run the following command to configure your local kubectl to connect with EKS:
aws eks update-kubeconfig --name CT-EKS-Cluster --region us-east-1
Step 4: Deploy a Sample Node.js App
Let’s deploy a simple Node.js application from a public ECR repository using a Kubernetes deployment and service YAML file.
---
apiVersion: v1
kind: Namespace
metadata:
name: simple-nodejs-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: simple-nodejs-app
name: deployment-nodejs-app
spec:
selector:
matchLabels:
app.kubernetes.io/name: nodejs-app
replicas: 5
template:
metadata:
labels:
app.kubernetes.io/name: nodejs-app
spec:
containers:
- image: public.ecr.aws/n4o6g6h8/simple-nodejs-app:latest
imagePullPolicy: Always
name: nodejs-app
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
namespace: simple-nodejs-app
name: service-nodejs-app
spec:
type: ClusterIP
ports:
- port: 80
name: http
targetPort: 8080
selector:
app.kubernetes.io/name: nodejs-app
Apply this manifest:
$ kubectl apply -f simple-nodejs-app.yaml
namespace/simple-nodejs-app created
deployment.apps/deployment-nodejs-app created
service/service-nodejs-app created
Check deployment status:
$ kubectl get pods -n simple-nodejs-app
NAME READY STATUS RESTARTS AGE
deployment-nodejs-app-55555bc798-9p2h2 1/1 Running 0 29s
deployment-nodejs-app-55555bc798-b68hn 1/1 Running 0 29s
deployment-nodejs-app-55555bc798-grx5w 1/1 Running 0 29s
deployment-nodejs-app-55555bc798-mdzqp 1/1 Running 0 29s
deployment-nodejs-app-55555bc798-xgfz5 1/1 Running 0 29s
$ kubectl get svc -n simple-nodejs-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-nodejs-app ClusterIP 172.20.83.36 <none> 80/TCP 54s
Step 5: Access the App via Port Forwarding
Since our pods are in private subnets, you can access them using:
$ kubectl port-forward -n simple-nodejs-app deployment/deployment-nodejs-app 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Now open your browser at http://localhost:8080
Cleanup
Once you're done experimenting, clean up resources to avoid charges:
Delete the app:
kubectl delete -f simple-nodejs-app.yaml
Delete the EKS cluster:
terraform destory -auto-approve
Conclusion
In this post, we successfully set up a production-grade Kubernetes cluster on AWS using EKS and Terraform AWS modules. We deployed a sample Node.js application using a public ECR image, managed its lifecycle with kubectl, and accessed it securely via port forwarding.
This is just the beginning. Kubernetes is a vast topic, and future posts will cover advanced concepts.
References
- Github Repo: https://github.com/chinmayto/terraform-aws-eks
- Terraform EKS modules: https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/18.14.0
Top comments (0)