Welcome to the ultimate hands-on guide for deploying a production-ready three-tier web application! This comprehensive tutorial will walk you through the ThreeTierAppChallenge - a modern todo application built with React, Node.js, and MongoDB, complete with Docker containerization, Kubernetes manifests, and CI/CD pipelines. Whether you're looking to learn modern DevOps practices or deploy a scalable web application, this guide has everything you need to get started and master the deployment pipeline.
The ThreeTierAppChallenge is a full-stack todo application featuring a React frontend, Node.js backend, and MongoDB database, designed for cloud-native deployment on AWS EKS. Clone the repo, run with Docker Compose in under 5 minutes, or dive deep into Kubernetes deployment with included manifests and Jenkins CI/CD pipelines. Perfect for learning modern DevOps practices including containerization, orchestration, and infrastructure as code.
Prerequisites
Before diving in, make sure you have these tools installed:
- Git (for version control)
- Node.js (v18+ recommended) and npm
- Docker and Docker Compose (for containerization)
- Optional but recommended:
π Quick Local Setup
Let's get the application running on your local machine in just a few minutes!
Step 1: Clone the Repository
git clone https://github.com/YOUR_USERNAME/TWSThreeTierAppChallenge.git
cd TWSThreeTierAppChallenge
Step 2: Run with Docker Compose (Fastest Method)
The easiest way to get everything running is with Docker Compose:
docker-compose up --build
Sample Output:
[+] Building 45.2s (23/23) FINISHED
[+] Running 3/3
β Container mongodb Started
β Container backend Started
β Container frontend Started
After a few minutes, you'll have:
- Frontend: http://localhost:3000
- Backend API: http://localhost:3500
- MongoDB: localhost:27017
Step 3: Manual Setup (Development Mode)
For development, you might want to run services individually:
Backend Setup:
cd Application-Code/backend
npm install
npm start
Frontend Setup (in a new terminal):
cd Application-Code/frontend
npm install
npm start
Sample Output:
Compiled successfully!
You can now view client in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.1.100:3000
webpack compiled with 0 errors
Step 4: Run Tests
# Backend tests
cd Application-Code/backend
npm test
# Frontend tests
cd Application-Code/frontend
npm test
π¦ Annotated Dockerfile Analysis
Let's examine the frontend Dockerfile to understand the containerization strategy:
FROM node:18-alpine AS build # 1. Multi-stage build for optimization
WORKDIR /app
COPY package*.json ./ # 2. Copy package files first for layer caching
RUN npm install # 3. Install dependencies
COPY . . # 4. Copy application source code
RUN npm run build # 5. Build the React production bundle
FROM nginx:alpine # 6. Use lightweight Nginx for serving
COPY --from=build /app/build /usr/share/nginx/html # 7. Copy built assets
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] # 8. Start Nginx in foreground
Key Benefits:
- Multi-stage build reduces final image size by ~70%
- Layer caching speeds up subsequent builds
- Nginx provides production-grade static file serving
- Alpine base minimizes security attack surface
ποΈ Architecture Overview
Diagram Description:
Create a diagram showing three tiers connected vertically:
βββββββββββββββββββ
β Frontend β β React.js (Port 3000)
β (React) β
βββββββββββ¬ββββββββ
β HTTP/REST API
βββββββββββΌββββββββ
β Backend β β Node.js/Express (Port 3500)
β (Node.js) β
βββββββββββ¬ββββββββ
β MongoDB Connection
βββββββββββΌββββββββ
β Database β β MongoDB (Port 27017)
β (MongoDB) β
βββββββββββββββββββ
Additional Infrastructure:
- Docker Containers around each tier
- Kubernetes Pods for orchestration
- AWS EKS for managed Kubernetes
- Jenkins for CI/CD automation
π‘ Three Essential Code Snippets
1. Smart Database Connection with Fallback
// Application-Code/backend/db.js
module.exports = async () => {
// Skip MongoDB connection if not using MongoDB
if (process.env.USE_MONGODB !== 'true') {
console.log("Using file-based storage (MongoDB disabled)");
return;
}
try {
const connectionParams = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
const useDBAuth = process.env.USE_DB_AUTH || false;
if(useDBAuth){
connectionParams.user = process.env.MONGO_USERNAME;
connectionParams.pass = process.env.MONGO_PASSWORD;
}
await mongoose.connect(process.env.MONGO_CONN_STR, connectionParams);
console.log("Connected to MongoDB database.");
} catch (error) {
console.log("Could not connect to database. Falling back to file-based storage...");
process.env.USE_MONGODB = 'false';
}
};
Line-by-line breakdown:
- Lines 3-6: Check environment variable to enable/disable MongoDB
- Lines 8-16: Configure connection parameters with optional authentication
- Lines 18: Attempt MongoDB connection using environment variables
- Lines 21-23: Graceful fallback to file-based storage on connection failure
This pattern ensures your application works in any environment!
2. Frontend API Service with Error Handling
// Application-Code/frontend/src/services/taskServices.js
export const taskService = {
getTasks: async (filters = {}) => {
const params = new URLSearchParams();
Object.entries(filters).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
params.append(key, value);
}
});
const response = await axios.get(`${apiUrl}?${params}`);
return response.data;
},
addTask: async (taskData) => {
try {
const response = await axios.post(apiUrl, taskData);
return response.data;
} catch (error) {
console.error('Add task error:', error);
throw error;
}
}
};
What's happening:
- Lines 3-9: Dynamic query parameter building for flexible filtering
- Lines 11-12: Clean GET request with constructed parameters
- Lines 15-22: POST request with comprehensive error handling
- Lines 19-20: Proper error logging and re-throwing for upstream handling
3. Production-Ready Kubernetes Deployment
# Kubernetes-Manifests-file/Backend/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: three-tier
spec:
replicas: 2 # High availability with 2 instances
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Allow 1 extra pod during updates
maxUnavailable: 25% # Maximum 25% downtime during updates
template:
spec:
containers:
- name: api
env:
- name: MONGO_CONN_STR
value: mongodb://mongodb-svc:27017/todo?directConnection=true
ports:
- containerPort: 3500
livenessProbe: # Health check configuration
httpGet:
path: /ok
port: 3500
initialDelaySeconds: 2
periodSeconds: 5
Deployment highlights:
- Line 8: Two replicas ensure high availability
- Lines 10-13: Rolling updates with zero downtime
- Lines 18-19: Service discovery using Kubernetes DNS
- Lines 22-27: Health checks for automatic recovery
π¨ Common Issues & Solutions
Issue 1: Backend Using File Storage Instead of MongoDB
Symptoms: API works but data doesn't persist between container restarts
Solution:
# Check backend logs
docker logs backend
# Set environment variable
echo "USE_MONGODB=true" >> Application-Code/backend/.env
echo "MONGO_CONN_STR=mongodb://mongodb:27017/todo" >> Application-Code/backend/.env
# Restart containers
docker-compose restart backend
Issue 2: Frontend Can't Connect to Backend
Symptoms: Frontend loads but no tasks appear, network errors in browser console
Solution:
# Check if backend is running
curl http://localhost:3500/health
# Verify environment variable
echo "REACT_APP_BACKEND_URL=http://localhost:3500/api/tasks" >> Application-Code/frontend/.env
# Rebuild frontend
docker-compose build frontend
Issue 3: Port Conflicts
Symptoms: docker-compose up
fails with "port already in use"
Solution:
# Check what's using the ports
lsof -i :3000,3500,27017
# Kill conflicting processes or change ports in docker-compose.yml
# Example: Change frontend port to 3001
ports:
- "3001:3000"
π CI/CD Pipeline Overview
The project includes comprehensive Jenkins pipelines for both frontend and backend:
Pipeline Features:
- Code Quality: SonarQube analysis
- Security: OWASP dependency checks and Trivy scanning
- Containerization: Docker image building and ECR pushing
- GitOps: Automatic deployment manifest updates
To set up CI/CD:
- Deploy Jenkins using the provided Terraform scripts
- Configure the provided Jenkinsfile pipelines
- Set up AWS ECR repositories for image storage
- Watch automated deployments to EKS!
βΈοΈ Kubernetes Deployment
Ready to go production? Deploy to Kubernetes:
# Create namespace
kubectl create namespace three-tier
# Deploy database
kubectl apply -f Kubernetes-Manifests-file/Database/
# Create MongoDB credentials
kubectl create secret generic mongo-sec \
--from-literal=username=admin \
--from-literal=password=password123 \
-n three-tier
# Deploy backend and frontend
kubectl apply -f Kubernetes-Manifests-file/Backend/
kubectl apply -f Kubernetes-Manifests-file/Frontend/
# Set up ingress (requires AWS Load Balancer Controller)
kubectl apply -f Kubernetes-Manifests-file/ingress.yaml
# Check deployment status
kubectl get all -n three-tier
π What's Next?
Congratulations! You've successfully deployed a modern three-tier application. Here are your next steps:
π― Immediate Next Steps:
- Explore the Code: Dive deeper into the Application-Code directory
- Try Kubernetes: Use the manifests in Kubernetes-Manifests-file
- Set Up CI/CD: Implement the Jenkins pipelines from Jenkins-Pipeline-Code
π Learning Opportunities:
- Infrastructure as Code: Study the Terraform scripts in Jenkins-Server-TF
- Security: Implement the security best practices shown in the code
- Monitoring: Add Prometheus and Grafana (hint: it's in the challenge levels!)
π€ Community & Contributing:
- Report Issues: Found a bug? Open an issue
- Contribute: Check the contribution guidelines
- Share Your Experience: Write about your deployment journey!
π Challenge Yourself:
- Bronze: Get the basic deployment working β
- Silver: Add SSL/TLS and monitoring
- Gold: Implement GitOps with ArgoCD
- Platinum: Add your own innovative features!
π Quick Reference Commands
# Development
npm start # Start local development
docker-compose up # Run full stack locally
kubectl get pods -n three-tier # Check Kubernetes status
# Troubleshooting
docker logs container_name # Check container logs
kubectl describe pod pod_name # Debug Kubernetes issues
curl localhost:3500/health # Test backend health
# Cleanup
docker-compose down # Stop local environment
kubectl delete namespace three-tier # Remove Kubernetes deployment
Recommended dev.to tags: tutorial
, javascript
, react
, nodejs
, docker
, kubernetes
, devops
, aws
, ci-cd
, mongodb
Ready to build something amazing? Clone the repo and start your DevOps journey today! π
Have questions or run into issues? Drop a comment below or reach out to the community. Happy coding!
Top comments (0)