Deploying Microservices on AWS EKS with Private VPC and Subnets Using Terraform
Modern cloud-native applications often follow the microservices architecture, enabling scalable, modular, and maintainable systems. In this blog, we’ll walk through deploying three microservices — user-service
, transaction-service
, and notification-service
—on Amazon Elastic Kubernetes Service (EKS) using Terraform. We'll leverage a private VPC and private subnets to ensure a secure deployment.
For the complete source code and configuration files, visit the GitHub repository: 3-tier-microservices-k8s-terraform.
Why Private VPC and Private Subnets?
Deploying microservices within a private VPC enhances security by limiting access to the services from the internet. With private subnets:
- Worker nodes and services are not directly exposed.
- Access to the internet for updates or external communication is achieved through a NAT Gateway.
- Services communicate internally via ClusterIP and private DNS names.
Prerequisites
Before proceeding, ensure the following:
- AWS CLI and kubectl are installed and configured.
- Terraform (v1.0+) is installed.
- A basic understanding of Kubernetes and Terraform.
- Permissions to create resources like VPCs, subnets, and EKS clusters in your AWS account.
Architecture Overview
Our infrastructure will consist of:
- A VPC with both private and public subnets.
- An EKS Cluster with worker nodes deployed in private subnets.
- Microservices (
user-service
,transaction-service
,notification-service
) deployed as Kubernetes workloads. - Internal communication between services using Kubernetes ClusterIP and DNS names.
- Optionally, an Application Load Balancer (ALB) to expose services securely.
Step-by-Step Deployment
1. Terraform & Manifests File Structure
Here is the recommended file structure:
├── eks
│ ├── manifests
│ │ ├── transaction-service.yaml
│ │ ├── notification-service.yaml
│ │ └── user-service.yaml
│ └── terraform
│ ├── eks-cluster.tf
│ ├── main.tf
│ ├── outputs.tf
│ └── vpc.tf
├── frontend
├── notification-service
├── transaction-service
└── user-service
2. VPC Configuration (terraform/vpc.tf
)
Create a VPC with private and public subnets and a NAT Gateway for internet access from private subnets.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.19"
name = "private-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.3.0/24", "10.0.4.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
tags = {
Name = "private-vpc"
}
}
3. EKS Cluster Configuration (terraform/eks-cluster.tf
)
Deploy an EKS cluster with worker nodes in private subnets.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 18.0"
cluster_name = "microservices-cluster"
cluster_version = "1.27"
subnets = module.vpc.private_subnets
vpc_id = module.vpc.vpc_id
node_groups = {
private-nodes = {
desired_capacity = 2
max_capacity = 3
min_capacity = 1
instance_types = ["t3.medium"]
subnets = module.vpc.private_subnets
}
}
tags = {
Name = "eks-cluster"
}
}
4. Kubernetes Services YAML Files
Deploy the microservices as Kubernetes deployments and services.
user-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 2
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: gcr.io/devops-demo-440509/user-service:latest
ports:
- containerPort: 8000
env:
- name: ALLOWED_ORIGINS
value: "http://frontend"
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
type: ClusterIP
selector:
app: user-service
ports:
- protocol: TCP
port: 8000
targetPort: 8000
Repeat similar configurations for transaction-service and notification-service.
5. Terraform Main File (main.tf
)
Integrate VPC, EKS, and Kubernetes resources.
provider "aws" {
region = "us-east-1"
}
module "vpc" {
source = "./eks/vpc.tf"
}
module "eks" {
source = "./eks/eks-cluster.tf"
}
resource "kubernetes_namespace" "default" {
metadata {
name = "default"
}
}
provider "kubernetes" {
host = module.eks.cluster_endpoint
cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)
token = module.eks.token
}
resource "kubernetes_deployment" "user_service" {
metadata {
name = "user-service"
namespace = "default"
}
spec {
replicas = 2
selector {
match_labels = {
app = "user-service"
}
}
template {
metadata {
labels = {
app = "user-service"
}
}
spec {
container {
image = "gcr.io/devops-demo-440509/user-service:latest"
name = "user-service"
port {
container_port = 8000
}
}
}
}
}
}
6. Apply the Terraform Plan
# Initialize Terraform
terraform init
# Validate configuration
terraform validate# Plan the deployment
terraform plan# Deploy the infrastructure
terraform apply
7. Testing the Microservices
- Use Kubernetes
port-forward
for testing private services locally.
kubectl port-forward service/user-service 8000:8000
curl http://ip:8000/api/users/
- Alternatively, use a bastion host to test from within the private network.
8. Optional: Add an API Gateway
To expose your services securely, consider deploying an API Gateway (e.g., AWS ALB Ingress or NGINX Ingress) for routing requests.
Conclusion
By deploying microservices on AWS EKS with a private VPC and private subnets, you achieve:
- Enhanced security by isolating services from direct internet exposure.
- Scalable and modular architecture using Kubernetes.
- Improved management with Terraform for infrastructure-as-code.
For the complete source code and YAML files, visit the GitHub repository: 3-tier-microservices-k8s-terraform.
This setup ensures your microservices are production-ready, secure, and easy to maintain. Happy deploying!