3 Tier Application On EKS
Building a three-tier application—frontend, backend, and database—on Amazon Elastic Kubernetes Service (EKS) sounds like a textbook exercise until you hit the r...
Listen to Article
PlayingClick play to listen to audio narration
Table of Contents
- •3 Tier Application On EKS
- •Introduction
- •Why This Matters
- •How It Works
- •Core Concepts
- •Tier Mapping
- •Service Discovery
- •Ingress Routing
- •Stateful vs Stateless
- •Examples & Code Walkthrough
- •Backend Deployment Manifest
- •Internal Service Configuration
- •Ingress Resource
- •Best Practices
- •Common Mistakes & Anti-Patterns
- •1. Running Containers as Root
- •2. No Resource Limits
- •3. Exposing Internal Services
- •4. Ignoring Health Checks
- •Performance Considerations
- •Real-World Usage
- •Frequently Asked Questions (FAQ)
- •Conclusion
3 Tier Application On EKS
Introduction
Building a three-tier application—frontend, backend, and database—on Amazon Elastic Kubernetes Service (EKS) sounds like a textbook exercise until you hit the real-world constraints of networking, scaling, and state management. The classic model doesn’t translate directly to containerized microservices. You end up wrestling with pod-to-pod communication, service discovery, persistent storage, and ingress routing. This isn’t just about deploying containers; it’s about architecting a resilient, observable system that scales without breaking.
Why This Matters
If you’re running anything beyond a hobby project, the decisions you make in structuring your tiers on EKS have cascading effects. Poorly configured ingress can lead to downtime during deployments. Misconfigured stateful sets can cause data loss. Ignoring resource limits turns your cluster into a noisy neighbor. In production, these aren’t theoretical concerns—they’re on-call alerts at 3 AM. A well-architected 3-tier setup on EKS gives you the foundation to scale horizontally, isolate failures, and manage traffic with precision.
How It Works
At its core, a 3-tier application on EKS maps each logical tier to Kubernetes primitives. The presentation tier (frontend) is served by pods exposed via an Ingress controller. The application tier (backend APIs) runs as Deployments with internal Services. The data tier (database) can be an external managed service like Amazon RDS or an in-cluster StatefulSet for smaller workloads. Traffic flows from the client through the Ingress, hits the frontend Service, which routes to the backend Service, and finally reaches the database or cache layer.
Here’s how the components interact:
flowchart TD
subgraph Client
User[User Browser]
end
subgraph EdgeSecurity
WAF[AWS WAF]
end
subgraph Networking
ALB[Application Load Balancer]
Ingress[Ingress Controller]
end
subgraph EKSCluster[EKS Cluster]
subgraph WebTier[Presentation Tier]
WebPod1[Web Pod 1]
WebPod2[Web Pod 2]
end
subgraph AppTier[Application Tier]
ApiPod1[API Pod 1]
ApiPod2[API Pod 2]
end
subgraph DataAccess[Data Access Layer]
Proxy[Connection Pooler]
end
end
subgraph ExternalServices
RDS[Aurora PostgreSQL]
Cache[ElastiCache Redis]
S3[S3 Bucket]
end
User --> WAF
WAF --> ALB
ALB --> Ingress
Ingress --> WebTier
WebTier --> AppTier
AppTier --> Proxy
Proxy --> RDS
AppTier --> Cache
AppTier --> S3
Traffic enters through AWS WAF for DDoS protection, then hits the ALB, which forwards to the NGINX Ingress Controller. The controller routes requests to the correct Service based on path rules. Frontend pods handle static assets and client-side routing, while backend pods process business logic. The backend communicates with RDS for persistence and ElastiCache for session caching. S3 stores media files, keeping them out of the database.
Core Concepts
Tier Mapping
Each tier in the traditional architecture maps to a specific Kubernetes construct:
- Presentation Tier: Served by Deployments or DaemonSets, exposed via Services and routed through an Ingress resource.
- Application Tier: Backend APIs running as Deployments with ClusterIP Services. Communication is typically over HTTP/gRPC.
- Data Tier: Managed services like RDS or self-hosted databases using StatefulSets with PersistentVolumeClaims.
Service Discovery
Services provide stable endpoints for pods. A Service abstracts away the ephemeral nature of pods, load-balancing traffic across healthy replicas. For inter-service communication, DNS-based discovery (api-service.namespace.svc.cluster.local) is used.
Ingress Routing
The Ingress resource defines HTTP(S) routing rules. Combined with an Ingress controller (like NGINX or ALB), it manages TLS termination, path-based routing, and can integrate with AWS Load Balancer Controller for direct ALB provisioning.
Stateful vs Stateless
Frontend and application tiers are stateless and scale horizontally. The data tier is inherently stateful, requiring persistent storage and careful handling of backups and failover strategies.
Examples & Code Walkthrough
Let’s look at a practical implementation for the backend API tier.
Backend Deployment Manifest
This manifest deploys a secure, scalable API service with resource constraints and observability hooks.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
labels:
app.kubernetes.io/name: api-gateway
app.kubernetes.io/tier: backend
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: public.ecr.aws/myorg/api-gateway:v1.2.0
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
envFrom:
- secretRef:
name: api-secrets
- configMapRef:
name: api-config
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Key points:
securityContext: Enforces non-root execution and uses a read-only root filesystem by default.resources: Prevents resource starvation and enables accurate autoscaling.probes: Ensures traffic only goes to healthy pods.rollingUpdate: Guarantees zero-downtime deployments.
Internal Service Configuration
The backend service should never be publicly accessible. It uses a ClusterIP to keep traffic internal.
apiVersion: v1
kind: Service
metadata:
name: api-service
labels:
app: api-gateway
spec:
type: ClusterIP
selector:
app: api-gateway
ports:
- protocol: TCP
port: 80
targetPort: 8080
sessionAffinity: None
externalTrafficPolicy: Local
Ingress Resource
This routes external traffic to the frontend service, with TLS termination handled by the Ingress controller.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
Best Practices
- Use Namespaces Liberally: Isolate environments (
dev,staging,prod) and teams into separate namespaces. - Implement Resource Quotas: Prevent any single team or namespace from consuming all cluster resources.
- Leverage Horizontal Pod Autoscaler (HPA): Automatically scale based on CPU/memory or custom metrics.
- Enforce Network Policies: Control pod-to-pod traffic to minimize lateral movement in case of compromise.
- Centralize Logging and Monitoring: Use tools like Prometheus, Grafana, and Loki for unified observability.
Common Mistakes & Anti-Patterns
1. Running Containers as Root
Running as root is a major security risk. Always set runAsNonRoot: true and define a runAsUser.
Fix:
securityContext:
runAsNonRoot: true
runAsUser: 1000
2. No Resource Limits
Without limits, a misbehaving pod can starve others. Always specify requests and limits.
Fix:
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
3. Exposing Internal Services
Using LoadBalancer for internal services exposes them unnecessarily. Use ClusterIP.
Fix:
type: ClusterIP
4. Ignoring Health Checks
Missing liveness/readiness probes leads to traffic being sent to unhealthy pods.
Fix:
livenessProbe:
httpGet:
path: /healthz
port: 8080
Performance Considerations
- CPU Overhead: Kubernetes adds minimal overhead, but excessive sidecars or init containers can impact performance.
- Memory Usage: Pod overhead, kubelet, and monitoring agents consume memory. Plan accordingly.
- Network Latency: Pod-to-pod latency within a node is negligible. Cross-AZ traffic incurs cloud provider charges and slight latency increases.
- Autoscaling Limits: HPA reacts to metrics with a delay. For critical workloads, consider predictive or custom autoscaling solutions.
Real-World Usage
Companies like Netflix and Uber have built highly available, scalable platforms on Kubernetes, often leveraging similar 3-tier architectures. They use advanced features like service meshes (Istio) for fine-grained traffic control, custom schedulers for specialized workloads, and chaos engineering to validate resilience. While their setups are more complex, the foundational principles of tier separation, observability, and automated scaling remain the same.
Frequently Asked Questions (FAQ)
Q: Can I run my database inside EKS? A: Yes, using StatefulSets with PersistentVolumes. However, for production, managed services like Amazon RDS are strongly recommended for durability and operational simplicity.
Q: How do I handle secrets in EKS? A: Use AWS Secrets Manager or HashiCorp Vault integrated with Kubernetes via External Secrets Operator. Never store secrets in plain text.
Q: What’s the difference between Ingress and Service?
A: A Service provides a stable endpoint for pods within the cluster. An Ingress manages external access to services, typically HTTP/S, and relies on an Ingress controller.
Q: How does scaling work for the data tier? A: Scaling the data tier is complex. For databases, vertical scaling or read replicas are common. For caches like Redis, clustering or sharding is used.
Conclusion
Deploying a 3-tier application on EKS requires more than just containerizing your apps. It demands a deep understanding of Kubernetes primitives, networking, and cloud-native security. By mapping each tier correctly, enforcing best practices, and avoiding common pitfalls, you can build a robust, scalable architecture. Start simple, measure constantly, and iterate. The goal isn’t perfection on day one—it’s a system that grows with your needs and survives the inevitable production challenges.
Written by Staff DevOps & Infrastructure Engineer
Editorial staff persona specializing in container orchestration, CI/CD pipeline automation, log aggregation, and real-time monitoring infrastructure.