Skip to content

arpithaoncloud9/eks-microservices

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

End‑to‑End Cloud‑Native Microservices Deployment on AWS EKS with Terraform

Step 1: provisioning an EKS cluster with Terraform

Repository structure
eks-microservices/
  infra/
    network/
      main.tf
      variables.tf
      outputs.tf
    eks/
      main.tf
      variables.tf
      outputs.tf
  env/
    backend.tf        # Terraform remote state (optional)
    provider.tf       # AWS + Kubernetes providers
    variables.tf
    terraform.tfvars  # Your environment values (region, names)
  README.md

1. Set up your local environment

  1. Install Terraform, AWS CLI, kubectl, and Docker if not already installed.
  2. Configure AWS CLI with your credentials:
   aws configure //(Enter your Access Key, Secret Key, region, and output format.)

2. Create your project folders

  1. In your workspace, create the structure:
  mkdir -p eks-microservices/infra/network
  mkdir -p eks-microservices/infra/eks
  mkdir -p eks-microservices/env
  cd eks-microservices
  1. Add empty files:
   touch infra/network/{main.tf,variables.tf,outputs.tf}
   touch infra/eks/{main.tf,variables.tf,outputs.tf}
   touch env/{backend.tf,provider.tf,variables.tf,terraform.tfvars,main.tf}
   touch README.md

3. Fill in the Terraform code

1. In infra/network/main.tf //VPC & Networking
module "vpc" {
 source  = "terraform-aws-modules/vpc/aws"
 version = "~> 5.0"

 name = "${var.cluster_name}-vpc"
 cidr = "10.0.0.0/16"

 azs             = var.azs
 private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
 public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

 enable_nat_gateway = true
 single_nat_gateway = true

 # Tags for Kubernetes load balancers
 public_subnet_tags = {
   "kubernetes.io/role/elb" = "1"
 }

 private_subnet_tags = {
   "kubernetes.io/role/internal-elb" = "1"
 }

 tags = {
   Project = "eks-microservices"
 }
}
2. In infra/network/variables.tf
variable "cluster_name" {
 type = string
}

variable "azs" {
 type = list(string)
}
3. In infra/network/outputs.tf
output "vpc_id" {
 value = module.vpc.vpc_id
}

output "private_subnets" {
 value = module.vpc.private_subnets
}

output "public_subnets" {
 value = module.vpc.public_subnets
}
4. In infra/eks/main.tf // EKS Cluster
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}

module "eks" {
 source  = "terraform-aws-modules/eks/aws"
 version = "~> 20.0"

 cluster_name    = var.cluster_name
 cluster_version = "1.29"

 # Networking from your VPC module
 vpc_id     = var.vpc_id
 subnet_ids = var.private_subnets

 # Enable IAM Roles for Service Accounts (IRSA)
 enable_irsa = true

 # Managed node groups
 eks_managed_node_groups = {
   ng-default = {
     instance_types = ["t3.micro"]
     desired_size   = 2
     min_size       = 2
     max_size       = 4
     capacity_type  = "ON_DEMAND"
   }
 }

 tags = {
   Project = "eks-microservices"
 }
}

data "aws_eks_cluster_auth" "this" {
 name = module.eks.cluster_name
}

output "cluster_name" {
 value = module.eks.cluster_name
}

output "cluster_endpoint" {
 value = module.eks.cluster_endpoint
}

output "cluster_certificate_authority_data" {
 value = module.eks.cluster_certificate_authority_data
}
5. In infra/eks/variables.tf
variable "cluster_name" {
  type = string
}

variable "vpc_id" {
  type = string
}

variable "private_subnets" {
  type = list(string)
}
6. In infra/eks/outputs.tf
  output "eks_managed_node_groups" {
  description = "Details of the managed node groups"
  value       = module.eks.eks_managed_node_groups
}
7. In env/main.tf
locals {
  project = "eks-microservices"
}

# Call the network module
module "network" {
  source       = "../infra/network"
  cluster_name = var.cluster_name
  azs          = var.azs
}

# Call the EKS module, passing outputs from network
module "eks" {
  source          = "../infra/eks"
  cluster_name    = var.cluster_name
  vpc_id          = module.network.vpc_id
  private_subnets = module.network.private_subnets
}
8. In env/provider.tf
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.25"
    }
  }
}

provider "aws" {
  region = var.region
}

# Kubernetes provider (optional, used after cluster is created)
provider "kubernetes" {
  host                   = module.eks.cluster_endpoint
  cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)
  token                  = data.aws_eks_cluster_auth.this.token
}
9. In env/terraform.tfvars, set values like:
# AWS region where you want to deploy
region       = "us-east-1"

# Name of your EKS cluster
cluster_name = "eks-microservices"

# Availability Zones (AZs) for subnets
azs          = ["us-east-1a", "us-east-1b"]
10. In env/variables.tf
variable "region" {
  description = "AWS region to deploy resources"
  type        = string
}

variable "cluster_name" {
  description = "Name of the EKS cluster"
  type        = string
}

variable "azs" {
  description = "Availability Zones for the VPC subnets"
  type        = list(string)
}
11. In env/outputs.tf
output "cluster_name" {
  value = module.eks.cluster_name
}

output "cluster_endpoint" {
  value = module.eks.cluster_endpoint
}

output "cluster_certificate_authority_data" {
  value = module.eks.cluster_certificate_authority_data
}

output "vpc_id" {
  value = module.network.vpc_id
}

output "private_subnets" {
  value = module.network.private_subnets
}

4. Initialize and apply Terraform

1. Go into the env folder:
terraform init
terraform plan
terraform apply -auto-approve

5. Connect kubectl to your cluster //

aws eks update-kubeconfig --name eks-microservices --region us-east-1
kubectl get nodes
If you get access denied error:
Create the Access Entry // This creates the access entry for your IAM user
aws eks create-access-entry \
  --cluster-name eks-microservices \
  --principal-arn arn:aws:iam::96974892****:user/Maria \
  --type STANDARD \
  --region us-east-1
Associate Kubernetes Group // This gives your IAM user full admin access (system:masters) via the managed policy.
aws eks associate-access-policy \
--cluster-name eks-microservices \
--principal-arn arn:aws:iam::96974892***:user/Maria \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=cluster \
--region us-east-1
Update kubeconfig
aws eks update-kubeconfig --region us-east-1 --name eks-microservices
Test access
kubectl get nodes
// This screenshot confirms your EKS cluster is fully up and running:
EKS_cluster_ready

Step 2: Dockerize Your Application

1. Create a Dockerfile for each microservice

1.1 Frontend Service (React + Vite)

Folder Structure for frontend service/

frontend/ ├── Dockerfile ├── package.json ├── package-lock.json # auto-generated after npm install ├── index.html # Vite entry point ├── src/ # React source code │ ├── main.jsx

1.1.1. Create Dockerfile
# Frontend Service (React + Vite + Nginx)
FROM node:18-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY dist/ /usr/share/nginx/html
EXPOSE 80
  • Run
npm init -y    //  This will create a default `package.json` file inside `frontend/`.
1.1.2. Create package.json
{

"name": "frontend",

"version": "1.0.0",

"private": true,

"scripts": {

"start": "vite",

"build": "vite build",

"preview": "vite preview"

},

"dependencies": {

"react": "^18.2.0",

"react-dom": "^18.2.0"

},

"devDependencies": {

"vite": "^7.2.7"

}

}
1.1.3. Create main.jsx
import React from 'react'

import ReactDOM from 'react-dom/client'

  

function App() {

return <h1>Hello, Maria’s EKS Microservices Frontend 🚀</h1>

}

  

ReactDOM.createRoot(document.getElementById('root')).render(<App />)
1.1.4. Create index.html**
<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Frontend</title>

</head>

<body>

<div id="root"></div>

<script type="module" src="/src/main.jsx"></script>

</body>

</html>
<title>Frontend</title>
<script type="module" src="/src/main.jsx"></script>
1.1.5. Install Dependencies
  1. Depending on your frontend stack:React (CRA)
npm install //  run inside frontend folder
npm audit fix --force
  1. Verify Locally
npm run build //  run inside frontend folder → This should generate a `dist/` folder.
1.1.6. Build Docker Image
docker build -t frontend:latest ./frontend

1.2 User/Auth Service (Python Flask)

Folder Structure for user-service/

user-service/ ├── Dockerfile ├── requirements.txt └── user_service.py

1.2.1. Create Dockerfile
FROM python:3.9-slim  
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "user_service.py"]
1.2.2. Create requirements.txt:
flask==2.3.2
1.2.3. Build Docker Image
docker build -t user-service:latest ./user-service 

1.3 Order Service (Node.js + Express)

Folder Structure for frontend service/

order-service/ ├── Dockerfile ├── package.json ├── package-lock.json └── order_service.js

1.3.1. Create Dockerfile
FROM node:18-alpine  
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "order_service.js"]
1.3.2 Create package.json
  • Run // inside the order-service folder
npm init -y
npm install express
1.3.3. Build the image locally:
docker build -t frontend:latest ./frontend
docker build -t user-service:latest ./user-service
docker build -t order-service:latest ./order-service

All three microservices images (frontend, user-service, order-service) were successfully built

image

Test Locally:

docker images

docker run -p 3000:80 frontend:latest
docker run -p 5000:5000 user-service:latest
docker run -p 4000:4000 order-service:latest

2. Create Dedicated Amazon ECR Repositories

  • Using AWS CLI:
aws ecr create-repository --repository-name frontend --region us-east-1
aws ecr create-repository --repository-name user-service --region us-east-1
aws ecr create-repository --repository-name order-service --region us-east-1
  • This will return JSON output with the repository URI, e.g.:
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-east-1:12345678***:repository/frontend",
        "repositoryUri": "12345678***.dkr.ecr.us-east-1.amazonaws.com/frontend"
    }
}

3. Authenticate Docker with ECR

  • Run:
    aws ecr get-login-password --region us-east-1 \
      | docker login --username AWS \
      --password-stdin <account_id>.dkr.ecr.us-east-1.amazonaws.com

4. Tag & Push Your Image

  • Tag the local image with the ECR URI:
docker tag frontend:latest 96974*****.dkr.ecr.us-east-1.amazonaws.com/frontend
docker tag user-service:latest 1234567****.dkr.ecr.us-east-1.amazonaws.com/user-service:latest
docker tag order-service:latest 12345678****.dkr.ecr.us-east-1.amazonaws.com/order-service:latest
  • Push it:
docker push 12345678****.dkr.ecr.us-east-1.amazonaws.com/frontend:latest
docker push 12345678****.dkr.ecr.us-east-1.amazonaws.com/user-service:latest
docker push 12345678****.dkr.ecr.us-east-1.amazonaws.com/order-service:latest

Step 3: Deploy to EKS

1. Update Kubernetes Manifests // Install kubectl

eks-microservices/ └── k8s/ ├── frontend-deployment.yaml ├── user-service-deployment.yaml ├── order-service-deployment.yaml └── services.yaml

  • In each Deployment.yaml, reference the ECR image URI:

1.1. Frontend Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: 346748929****.dkr.ecr.us-east-1.amazonaws.com/frontend:latest
          ports:
            - containerPort: 80
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10

1.2. User Service Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
        - name: user-service
          image: 879748929***.dkr.ecr.us-east-1.amazonaws.com/user-service:latest
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              value: "your-db-url-here"
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10

1.3. Order Service Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: 56697489****.dkr.ecr.us-east-1.amazonaws.com/order-service:latest
          ports:
            - containerPort: 4000
          env:
            - name: DATABASE_URL
              value: "your-db-url-here"
          livenessProbe:
            httpGet:
              path: /health
              port: 4000
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /ready
              port: 4000
            initialDelaySeconds: 5
            periodSeconds: 10

1.4. Services YAML

# Frontend Service (public)
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80

---
# User Service (internal)
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  type: ClusterIP
  selector:
    app: user-service
  ports:
    - port: 5000
      targetPort: 5000

---
# Order Service (internal)
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  type: ClusterIP
  selector:
    app: order-service
  ports:
    - port: 4000
      targetPort: 4000
  • Ensure your EKS node IAM role has permissions to pull from ECR (AmazonEC2ContainerRegistryReadOnly policy).

2. Apply Manifests

  • Run
kubectl apply -f frontend-deployment.yaml
kubectl apply -f user-service-deployment.yaml
kubectl apply -f order-service-deployment.yaml
kubectl apply -f services.yaml

3. Validate Pods

  • Wait for the frontend LoadBalancer to get an EXTERNAL-IP:
kubectl get pods
kubectl get deployments
kubectl get svc frontend-service
  • Pods should pull images from ECR and enter Running ✅ state.
  • Once it’s ready, open that IP in your browser — it should load your React frontend and connect to the backend services internally.
image

Step 4: Automated CI/CD Pipeline on EKS

1. Create Workflow Directory

  • In your repo root, create a folder:
    .github/workflows/
    
  • This is where GitHub looks for workflow files.

2. Add Workflow File

  • Inside .github/workflows/, create a file named: (deploy.yml)
  name: CI/CD to EKS

on:
  push:
    branches: [ "main" ]

jobs:
  frontend:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    - name: Login to Amazon ECR
      run: |
        aws ecr get-login-password --region us-east-1 \
        | docker login --username AWS --password-stdin *****456789.dkr.ecr.us-east-1.amazonaws.com
    - name: Build and push Docker image
      run: |
        IMAGE_URI=*****456789.dkr.ecr.us-east-1.amazonaws.com/frontend:latest
        docker build -t $IMAGE_URI -f frontend/Dockerfile ./frontend
        docker push $IMAGE_URI
    - name: Deploy to EKS
      run: |
        aws eks --region us-east-1 update-kubeconfig --name eks-microservices
        kubectl apply -f k8s/frontend-deployment.yaml

  user-service:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    - name: Login to Amazon ECR
      run: |
        aws ecr get-login-password --region us-east-1 \
        | docker login --username AWS --password-stdin *****456789.dkr.ecr.us-east-1.amazonaws.com
    - name: Build and push Docker image
      run: |
        IMAGE_URI=*****456789.dkr.ecr.us-east-1.amazonaws.com/user-service:latest
        docker build -t $IMAGE_URI -f user-service/Dockerfile ./user-service
        docker push $IMAGE_URI
    - name: Deploy to EKS
      run: |
        aws eks --region us-east-1 update-kubeconfig --name eks-microservices
        kubectl apply -f k8s/user-service-deployment.yaml

  order-service:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    - name: Login to Amazon ECR
      run: |
        aws ecr get-login-password --region us-east-1 \
        | docker login --username AWS --password-stdin *****456789.dkr.ecr.us-east-1.amazonaws.com
    - name: Build and push Docker image
      run: |
        IMAGE_URI=*****456789.dkr.ecr.us-east-1.amazonaws.com/order-service:latest
        docker build -t $IMAGE_URI -f order-service/Dockerfile ./order-service
        docker push $IMAGE_URI
    - name: Deploy to EKS
      run: |
        aws eks --region us-east-1 update-kubeconfig --name eks-microservices
        kubectl apply -f k8s/order-service-deployment.yaml 

3. Define Trigger

  • At the top of deploy.yml, specify when the workflow should run:
      name: CI/CD to EKS
      
      on:
        push:
       branches: [ "main" ]
    
  • This ensures the pipeline runs automatically on every push to main.

4. Set Up Jobs

  • Define jobs for each microservice (frontend, user-service, order-service):
    jobs:
      frontend:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout code
            uses: actions/checkout@v3
  • Repeat for user-service and order-service.

5 Add Secrets in GitHub

  • In your GitHub repo, go to: Settings → Secrets and variables → Actions → New repository secret →Add
    • AWS_ACCESS_KEY_ID → paste the key ID
    • AWS_SECRET_ACCESS_KEY → paste the secret key
    • (Optional) AWS_REGION → e.g., us-east-1

6. Login to Amazon ECR

  • Add:
    - name: Login to Amazon ECR
      run: |
        aws ecr get-login-password --region us-east-1 \
        | docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.us-east-1.amazonaws.co

7. Build & Push Docker Image

  • Example for frontend:
    - name: Build and push Docker image
      run: |
        IMAGE_URI=<your-account-id>.dkr.ecr.us-east-1.amazonaws.com/frontend:latest
        docker build -t $IMAGE_URI -f frontend/Dockerfile ./frontend
        docker push $IMAGE_URI

8. Deploy to EKS

  • Apply manifests automatically:
    - name: Deploy to EKS
      run: |
        aws eks --region us-east-1 update-kubeconfig --name eks-microservices
        kubectl apply -f k8s/frontend-deployment.yaml

Every push to main now triggers build → push → deploy → validate across all microservices

image

Outcome

  • Delivered a scalable, resilient Kubernetes foundation to host microservices and accelerate cloud‑native delivery. 
  • All microservices containerized and stored in Amazon ECR
  • Microservices successfully deployed on Amazon EKS
  • Secure integration between ECR and EKS established
  • Frontend accessible externally, backend services communicating internally
  • Solid foundation for scaling, monitoring, and CI/CD integration
  • Achieved a fully automated CI/CD pipeline for multi‑service architecture on Amazon EKS
  • Every push to main now triggers build → push → deploy → validate across all microservices
  • Reduced manual effort, improved reliability, and ensured production‑ready deployments with rollback safety

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors