Deploying a 3-Tier App on AWS EKS Made Simple

Pratiksha kadamPratiksha kadam
5 min read
  • Overview

    • 🧱 What Is a 3-Tier App?

      A 3-tier architecture splits your application into:

      1. Presentation Layer (Frontend) – The UI users interact with.

      2. Logic Layer (Backend) – Handles API requests, business logic.

      3. Data Layer (Database) – Stores persistent data.

This architecture is scalable, secure, and modularβ€”perfect for cloud-native apps.


βš™οΈ AWS Services We'll Use

  • EKS for container orchestration

  • RDS (PostgreSQL) for managed databases

  • ALB Controller for exposing services

  • Route 53 for custom domains

  • 🌐 Why Use AWS EKS?

    • Benefits of Kubernetes on AWS

      • Scalable orchestration

      • Declarative configuration

      • CI/CD ready

EKS Options Explained

  • Managed Node Groups: AWS manages worker nodes for you.

  • Fargate: Serverless, but no native support for persistent volumes.

  • Self-Managed Nodes: Full control, higher complexity.

Why Use Managed Node Groups?

  • πŸ”§ Prerequisites

    • Install Required Tools

    •   brew install awscli kubectl eksctl helm
      
    • aws cli login

    •   aws configure
      
  • πŸ—οΈ Creating an EKS Cluster :

      eksctl create cluster \
        --name my-cluster \
        --region eu-west-1 \
        --version 1.31 \
        --nodegroup-name managed-nodes \
        --node-type t3.medium \
        --nodes 2 \
        --nodes-min 1 \
        --nodes-max 3 \
        --managed
    
    • Update kubeconfig

    •   aws eks update-kubeconfig --name my-cluster --region eu-west-1 kubectl get nodes
      
  • πŸ—„οΈ Setting Up PostgreSQL on RDS

    • πŸ—„οΈ Setting Up PostgreSQL on RDS

      Get VPC ID and Subnets

        VPC_ID=$(aws eks describe-cluster --name my-cluster \
          --region eu-west-1 \
          --query "cluster.resourcesVpcConfig.vpcId" --output text)
      

      Get private subnets (assuming 3 AZs):

        SUBNET_IDS=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" \
          --query "Subnets[?MapPublicIpOnLaunch==\`false\`].SubnetId" --output text)
      

      Create DB Subnet Group

        aws rds create-db-subnet-group \
          --db-subnet-group-name postgres-subnet-group \
          --description "Private subnets for RDS" \
          --subnet-ids $SUBNET_IDS \
          --region eu-west-1
      

      Create RDS Security Group

        RDS_SG_ID=$(aws ec2 create-security-group \
          --group-name rds-sg \
          --description "RDS SG" \
          --vpc-id $VPC_ID \
          --query GroupId --output text)
      

      Authorize EKS Access to PostgreSQL

      Get EKS worker SG:

        EKS_SG_ID=$(aws ec2 describe-security-groups --filters "Name=vpc-id,Values=$VPC_ID" \
          "Name=group-name,Values=*nodes*" --query "SecurityGroups[0].GroupId" --output text)
      

      Allow access:

        aws ec2 authorize-security-group-ingress \
          --group-id $RDS_SG_ID \
          --protocol tcp \
          --port 5432 \
          --source-group $EKS_SG_ID
      

      Create PostgreSQL Instance

        aws rds create-db-instance \
          --db-instance-identifier my-postgres \
          --db-instance-class db.t3.small \
          --engine postgres \
          --engine-version 15 \
          --allocated-storage 20 \
          --master-username postgresadmin \
          --master-user-password 'YourStrongPassword123!' \
          --db-subnet-group-name postgres-subnet-group \
          --vpc-security-group-ids $RDS_SG_ID \
          --no-publicly-accessible \
          --region eu-west-1
      

      πŸ” Kubernetes Secrets & Configs

      Create Namespace

        # namespace.yaml
        apiVersion: v1
        kind: Namespace
        metadata:
          name: 3-tier-app-eks
      
        kubectl apply -f namespace.yaml
      

      Create Secrets

        # secrets.yaml
        apiVersion: v1
        kind: Secret
        metadata:
          name: db-secret
          namespace: 3-tier-app-eks
        type: Opaque
        data:
          username: cG9zdGdyZXNhZG1pbg==  # postgresadmin (base64)
          password: WW91clN0cm9uZ1Bhc3N3b3JkMTIzIQ==  # YourStrongPassword123!
      

      Create ConfigMap

        # config.yaml
        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: app-config
          namespace: 3-tier-app-eks
        data:
          DATABASE_HOST: your-rds-endpoint.rds.amazonaws.com
      

      πŸ” Run DB Migrations Using Kubernetes Job

        # migration-job.yaml
        apiVersion: batch/v1
        kind: Job
        metadata:
          name: db-migrate
          namespace: 3-tier-app-eks
        spec:
          template:
            spec:
              containers:
              - name: migrate
                image: my-backend-image
                command: ["flask", "db", "upgrade"]
                envFrom:
                - secretRef:
                    name: db-secret
                - configMapRef:
                    name: app-config
              restartPolicy: OnFailure
      
        kubectl apply -f migration-job.yaml
        kubectl logs job/db-migrate -n 3-tier-app-eks
      

      πŸ–₯️ Deploy Backend and Frontend

      Flask Backend

        # backend.yaml
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: backend
          namespace: 3-tier-app-eks
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: backend
          template:
            metadata:
              labels:
                app: backend
            spec:
              containers:
              - name: backend
                image: my-backend-image
                ports:
                - containerPort: 8000
                envFrom:
                - secretRef:
                    name: db-secret
                - configMapRef:
                    name: app-config
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: backend
          namespace: 3-tier-app-eks
        spec:
          selector:
            app: backend
          ports:
            - port: 8000
              targetPort: 8000
      

      React Frontend

        # frontend.yaml
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: frontend
          namespace: 3-tier-app-eks
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: frontend
          template:
            metadata:
              labels:
                app: frontend
            spec:
              containers:
              - name: frontend
                image: my-frontend-image
                ports:
                - containerPort: 80
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: frontend
          namespace: 3-tier-app-eks
        spec:
          selector:
            app: frontend
          ports:
            - port: 80
              targetPort: 80
      

      🌐 Expose via AWS Load Balancer Controller

      Associate OIDC

        eksctl utils associate-iam-oidc-provider --cluster my-cluster --approve
      

      Create IAM Policy

        curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.11.0/docs/install/iam_policy.json
      
        aws iam create-policy \
          --policy-name AWSLoadBalancerControllerIAMPolicy \
          --policy-document file://iam_policy.json
      

      IAM Service Account

        eksctl create iamserviceaccount \
          --cluster=my-cluster \
          --namespace=kube-system \
          --name=aws-load-balancer-controller \
          --attach-policy-arn=arn:aws:iam::[ACCOUNT_ID]:policy/AWSLoadBalancerControllerIAMPolicy \
          --approve
      

      Install ALB Controller

        helm repo add eks https://aws.github.io/eks-charts
        helm repo update
      
        helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
          -n kube-system \
          --set clusterName=my-cluster \
          --set serviceAccount.create=false \
          --set serviceAccount.name=aws-load-balancer-controller \
          --set region=eu-west-1 \
          --set vpcId=$VPC_ID
      

      🌍 Create Ingress

        # ingress.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:
          name: 3-tier-app-ingress
          namespace: 3-tier-app-eks
          annotations:
            alb.ingress.kubernetes.io/scheme: internet-facing
        spec:
          ingressClassName: alb
          rules:
          - http:
              paths:
              - path: /api
                pathType: Prefix
                backend:
                  service:
                    name: backend
                    port:
                      number: 8000
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: frontend
                    port:
                      number: 80
      
        kubectl apply -f ingress.yaml
      

      Tag Public Subnets

        aws ec2 create-tags --resources subnet-xxxx subnet-yyyy \
          --tags Key=kubernetes.io/role/elb,Value=1
      

      🧭 Custom Domain Setup

      1. Go to Route 53

      2. Create a new Hosted Zone

      3. Add Alias Record for the ALB DNS

      4. Update domain registrar nameservers


βœ… Wrapping Up

You’ve successfully deployed a modern, cloud-native 3-tier application using Kubernetes on AWS EKS. Along the way, you:

  • Created a secure PostgreSQL database

  • Managed secrets and configs

  • Handled database migrations

  • Exposed your app publicly via ALB and custom domain


❓ FAQs

1. How do I scale the application?
Use kubectl scale deployment backend --replicas=5 to scale pods.

2. Can I use EFS for shared storage?
Yes, EFS is supported via Persistent Volumes on EKS.

3. Is this setup production-ready?
Yes, with minor enhancements like SSL termination, monitoring, and autoscaling.

4. Can I CI/CD this setup?
Definitely. Use GitHub Actions or AWS CodePipeline with Helm and kubectl.

5. How do I handle updates?
Use rolling updates in Deployment specs or Helm upgrades.

0
Subscribe to my newsletter

Read articles from Pratiksha kadam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pratiksha kadam
Pratiksha kadam