Day 67: Deploying Multi-Tier Guestbook Application on Kubernetes 📚

Welcome back! 👋 Day 67 of the 100 Days Cloud DevOps Challenge, and today we're deploying a complete multi-tier application on Kubernetes! This is real-world microservices architecture - frontend, backend, and data layer working together. Let's orchestrate! 🎯
🎯 The Mission - Deploy Guestbook Application
📋 TASK TICKET #DEV-8067 - Multi-Tier Application Deployment
Priority: HIGH | Type: Production Application Stack
Server: Jump Host | Cluster: Kubernetes
APPLICATION:
- Guestbook (visitor management)
- Multi-tier architecture
- Redis backend + PHP frontend
- Ready for production deployment
ARCHITECTURE:
┌─────────────────────────────────────┐
│ FRONTEND TIER │
│ PHP Application (3 replicas) │
│ NodePort Service (Port 30009) │
└─────────────────┬───────────────────┘
↓
┌─────────────────────────────────────┐
│ BACKEND TIER │
│ Redis Master (1 replica) │
│ Redis Slaves (2 replicas) │
│ ClusterIP Services │
└─────────────────────────────────────┘
BACKEND TIER:
1. Redis Master Deployment
- Name: redis-master
- Replicas: 1
- Container: master-redis-devops
- Image: redis
- Resources: 100m CPU, 100Mi Memory
- Port: 6379
2. Redis Master Service
- Name: redis-master
- Port: 6379
3. Redis Slave Deployment
- Name: redis-slave
- Replicas: 2
- Container: slave-redis-devops
- Image: gcr.io/google_samples/gb-redisslave:v3
- Resources: 100m CPU, 100Mi Memory
- Env: GET_HOSTS_FROM=dns
- Port: 6379
4. Redis Slave Service
- Name: redis-slave
- Port: 6379
FRONTEND TIER:
1. Frontend Deployment
- Name: frontend
- Replicas: 3
- Container: php-redis-devops
- Image: gcr.io/google-samples/gb-frontend@sha256:...
- Resources: 100m CPU, 100Mi Memory
- Env: GET_HOSTS_FROM=dns
- Port: 80
2. Frontend Service
- Name: frontend
- Type: NodePort
- Port: 80
- NodePort: 30009
SUCCESS CRITERIA:
- All deployments running
- Services created
- Frontend accessible via port 30009
- Guestbook functional
This is production microservices deployment! 🏗️
🤔 Why This Architecture?
Multi-Tier Benefits:
✅ Separation of Concerns:
- Frontend: User interface (PHP)
- Backend: Data layer (Redis)
- Clear boundaries, easy to maintain
✅ Scalability:
- Frontend: 3 replicas (high traffic)
- Redis Master: 1 (write operations)
- Redis Slaves: 2 (read operations)
- Scale each tier independently
✅ High Availability:
- Multiple frontend replicas
- Redis replication (master-slave)
- Service discovery (DNS)
- Fault tolerance
✅ Performance:
- Read/write separation
- Load balancing
- Caching layer
- Optimized resource allocation
🛠️ Complete Implementation
Step 1: Access Jump Host
# SSH to jump host
ssh user@jump_host
# Verify kubectl
kubectl version --short
kubectl get nodes
Environment ready! ✅
Step 2: Create Backend - Redis Master
# Create Redis Master Deployment
cat > redis-master-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-master
labels:
app: redis
role: master
tier: backend
spec:
replicas: 1
selector:
matchLabels:
app: redis
role: master
tier: backend
template:
metadata:
labels:
app: redis
role: master
tier: backend
spec:
containers:
- name: master-redis-devops
image: redis
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
EOF
# Apply deployment
kubectl apply -f redis-master-deployment.yaml
Expected output:
deployment.apps/redis-master created
Create Redis Master Service:
cat > redis-master-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
app: redis
role: master
tier: backend
spec:
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
role: master
tier: backend
EOF
# Apply service
kubectl apply -f redis-master-service.yaml
Expected output:
service/redis-master created
Step 3: Create Backend - Redis Slave
# Create Redis Slave Deployment
cat > redis-slave-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-slave
labels:
app: redis
role: slave
tier: backend
spec:
replicas: 2
selector:
matchLabels:
app: redis
role: slave
tier: backend
template:
metadata:
labels:
app: redis
role: slave
tier: backend
spec:
containers:
- name: slave-redis-devops
image: gcr.io/google_samples/gb-redisslave:v3
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 6379
EOF
# Apply deployment
kubectl apply -f redis-slave-deployment.yaml
Expected output:
deployment.apps/redis-slave created
Create Redis Slave Service:
cat > redis-slave-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
app: redis
role: slave
tier: backend
spec:
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
role: slave
tier: backend
EOF
# Apply service
kubectl apply -f redis-slave-service.yaml
Expected output:
service/redis-slave created
Step 4: Create Frontend
# Create Frontend Deployment
cat > frontend-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
app: guestbook
tier: frontend
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
containers:
- name: php-redis-devops
image: gcr.io/google-samples/gb-frontend@sha256:a908df8486ff66f2c4daa0d3d8a2fa09846a1fc8efd65649c0109695c7c5cbff
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
EOF
# Apply deployment
kubectl apply -f frontend-deployment.yaml
Expected output:
deployment.apps/frontend created
Create Frontend Service:
cat > frontend-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30009
selector:
app: guestbook
tier: frontend
EOF
# Apply service
kubectl apply -f frontend-service.yaml
Expected output:
service/frontend created
Step 5: Verify All Deployments
# Check all deployments
kubectl get deployments
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
redis-master 1/1 1 1 2m
redis-slave 2/2 2 2 2m
frontend 3/3 3 3 1m
All deployments ready! ✅
Check all pods:
kubectl get pods
Expected output:
NAME READY STATUS RESTARTS AGE
redis-master-7d6c9f8b9c-x7k2m 1/1 Running 0 2m
redis-slave-5d8c7b9f8c-abc12 1/1 Running 0 2m
redis-slave-5d8c7b9f8c-def34 1/1 Running 0 2m
frontend-6d4c8f9b5c-ghi56 1/1 Running 0 1m
frontend-6d4c8f9b5c-jkl78 1/1 Running 0 1m
frontend-6d4c8f9b5c-mno90 1/1 Running 0 1m
6 pods running (1 master + 2 slaves + 3 frontend)! ✅
Check all services:
kubectl get services
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
redis-master ClusterIP 10.96.100.50 <none> 6379/TCP 2m
redis-slave ClusterIP 10.96.100.51 <none> 6379/TCP 2m
frontend NodePort 10.96.100.52 <none> 80:30009/TCP 1m
All services created! ✅
Step 6: Verify Pod Labels
# Check Redis master labels
kubectl get pods -l role=master --show-labels
Expected output:
NAME READY STATUS RESTARTS AGE LABELS
redis-master-7d6c9f8b9c-x7k2m 1/1 Running 0 3m app=redis,role=master,tier=backend
Check Redis slave labels:
kubectl get pods -l role=slave --show-labels
Expected output:
NAME READY STATUS RESTARTS AGE LABELS
redis-slave-5d8c7b9f8c-abc12 1/1 Running 0 3m app=redis,role=slave,tier=backend
redis-slave-5d8c7b9f8c-def34 1/1 Running 0 3m app=redis,role=slave,tier=backend
Check frontend labels:
kubectl get pods -l tier=frontend --show-labels
Expected output:
NAME READY STATUS RESTARTS AGE LABELS
frontend-6d4c8f9b5c-ghi56 1/1 Running 0 2m app=guestbook,tier=frontend
frontend-6d4c8f9b5c-jkl78 1/1 Running 0 2m app=guestbook,tier=frontend
frontend-6d4c8f9b5c-mno90 1/1 Running 0 2m app=guestbook,tier=frontend
All labels correct! ✅
Step 7: Verify Redis Connectivity
# Test Redis master
kubectl exec -it $(kubectl get pod -l role=master -o jsonpath='{.items[0].metadata.name}') -- redis-cli ping
Expected output:
PONG
Test Redis slave:
kubectl exec -it $(kubectl get pod -l role=slave -o jsonpath='{.items[0].metadata.name}') -- redis-cli ping
Expected output:
PONG
Check Redis replication:
kubectl exec -it $(kubectl get pod -l role=master -o jsonpath='{.items[0].metadata.name}') -- redis-cli info replication
Expected output:
# Replication
role:master
connected_slaves:2
slave0:ip=10.244.1.6,port=6379,state=online
slave1:ip=10.244.2.5,port=6379,state=online
Replication working! ✅
Step 8: Test Application
# Get node IP
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
echo "Guestbook URL: http://$NODE_IP:30009"
# Test frontend access
curl -I http://$NODE_IP:30009
Expected output:
HTTP/1.1 200 OK
Server: nginx/1.10.0
Content-Type: text/html; charset=utf-8
Frontend accessible! ✅
Test write operation:
# Post message to guestbook
curl -X POST http://$NODE_IP:30009/guestbook.php -d "message=Hello from Kubernetes"
# Verify it was stored
curl http://$NODE_IP:30009/guestbook.php | grep "Hello from Kubernetes"
Application functional! ✅
Step 9: Complete Verification
# Comprehensive verification script
cat > verify-guestbook.sh << 'EOF'
#!/bin/bash
echo "=== Guestbook Application Verification ==="
echo ""
echo "1. Backend Tier - Redis Master:"
kubectl get deployment redis-master
kubectl get service redis-master
echo ""
echo "2. Backend Tier - Redis Slave:"
kubectl get deployment redis-slave
kubectl get service redis-slave
echo ""
echo "3. Frontend Tier:"
kubectl get deployment frontend
kubectl get service frontend
echo ""
echo "4. All Pods Status:"
kubectl get pods -o wide
echo ""
echo "5. Pod Count Verification:"
MASTER_COUNT=$(kubectl get pods -l role=master --no-headers | wc -l)
SLAVE_COUNT=$(kubectl get pods -l role=slave --no-headers | wc -l)
FRONTEND_COUNT=$(kubectl get pods -l tier=frontend --no-headers | wc -l)
echo " Redis Master Pods: $MASTER_COUNT (expected: 1)"
echo " Redis Slave Pods: $SLAVE_COUNT (expected: 2)"
echo " Frontend Pods: $FRONTEND_COUNT (expected: 3)"
echo ""
echo "6. Service Endpoints:"
echo " Redis Master:"
kubectl get endpoints redis-master
echo " Redis Slave:"
kubectl get endpoints redis-slave
echo " Frontend:"
kubectl get endpoints frontend
echo ""
echo "7. Resource Requests:"
echo " Redis Master:"
kubectl get pod -l role=master -o jsonpath='{.items[0].spec.containers[0].resources.requests}'
echo ""
echo " Redis Slave:"
kubectl get pod -l role=slave -o jsonpath='{.items[0].spec.containers[0].resources.requests}'
echo ""
echo " Frontend:"
kubectl get pod -l tier=frontend -o jsonpath='{.items[0].spec.containers[0].resources.requests}'
echo ""
echo ""
echo "8. Environment Variables:"
SLAVE_ENV=$(kubectl get pod -l role=slave -o jsonpath='{.items[0].spec.containers[0].env[0].value}')
FRONTEND_ENV=$(kubectl get pod -l tier=frontend -o jsonpath='{.items[0].spec.containers[0].env[0].value}')
echo " Redis Slave GET_HOSTS_FROM: $SLAVE_ENV"
echo " Frontend GET_HOSTS_FROM: $FRONTEND_ENV"
echo ""
echo "9. Redis Connectivity:"
MASTER_POD=$(kubectl get pod -l role=master -o jsonpath='{.items[0].metadata.name}')
REDIS_PING=$(kubectl exec $MASTER_POD -- redis-cli ping 2>/dev/null)
echo " Redis Master PING: $REDIS_PING"
echo ""
echo "10. Frontend Access:"
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://$NODE_IP:30009)
echo " URL: http://$NODE_IP:30009"
echo " HTTP Response: $HTTP_CODE"
echo ""
echo "11. Verification Summary:"
if [ "$MASTER_COUNT" = "1" ] && [ "$SLAVE_COUNT" = "2" ] && [ "$FRONTEND_COUNT" = "3" ] && [ "$HTTP_CODE" = "200" ]; then
echo " ✓ Redis Master: 1 replica running"
echo " ✓ Redis Slave: 2 replicas running"
echo " ✓ Frontend: 3 replicas running"
echo " ✓ All services created"
echo " ✓ Frontend accessible on NodePort 30009"
echo " ✓ Redis connectivity verified"
echo " ✓ ALL CHECKS PASSED"
else
echo " ✗ Some checks failed"
fi
EOF
chmod +x verify-guestbook.sh
./verify-guestbook.sh
Expected output:
=== Guestbook Application Verification ===
1. Backend Tier - Redis Master:
NAME READY UP-TO-DATE AVAILABLE AGE
redis-master 1/1 1 1 5m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-master ClusterIP 10.96.100.50 <none> 6379/TCP 5m
2. Backend Tier - Redis Slave:
NAME READY UP-TO-DATE AVAILABLE AGE
redis-slave 2/2 2 2 5m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-slave ClusterIP 10.96.100.51 <none> 6379/TCP 5m
3. Frontend Tier:
NAME READY UP-TO-DATE AVAILABLE AGE
frontend 3/3 3 3 4m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend NodePort 10.96.100.52 <none> 80:30009/TCP 4m
4. All Pods Status:
NAME READY STATUS RESTARTS AGE IP
redis-master-7d6c9f8b9c-x7k2m 1/1 Running 0 5m 10.244.1.5
redis-slave-5d8c7b9f8c-abc12 1/1 Running 0 5m 10.244.1.6
redis-slave-5d8c7b9f8c-def34 1/1 Running 0 5m 10.244.2.5
frontend-6d4c8f9b5c-ghi56 1/1 Running 0 4m 10.244.1.7
frontend-6d4c8f9b5c-jkl78 1/1 Running 0 4m 10.244.2.6
frontend-6d4c8f9b5c-mno90 1/1 Running 0 4m 10.244.3.4
5. Pod Count Verification:
Redis Master Pods: 1 (expected: 1)
Redis Slave Pods: 2 (expected: 2)
Frontend Pods: 3 (expected: 3)
6. Service Endpoints:
Redis Master:
NAME ENDPOINTS AGE
redis-master 10.244.1.5:6379 5m
Redis Slave:
NAME ENDPOINTS AGE
redis-slave 10.244.1.6:6379,10.244.2.5:6379 5m
Frontend:
NAME ENDPOINTS AGE
frontend 10.244.1.7:80,10.244.2.6:80,10.244.3.4:80 4m
7. Resource Requests:
Redis Master:
{"cpu":"100m","memory":"100Mi"}
Redis Slave:
{"cpu":"100m","memory":"100Mi"}
Frontend:
{"cpu":"100m","memory":"100Mi"}
8. Environment Variables:
Redis Slave GET_HOSTS_FROM: dns
Frontend GET_HOSTS_FROM: dns
9. Redis Connectivity:
Redis Master PING: PONG
10. Frontend Access:
URL: http://172.17.0.2:30009
HTTP Response: 200
11. Verification Summary:
✓ Redis Master: 1 replica running
✓ Redis Slave: 2 replicas running
✓ Frontend: 3 replicas running
✓ All services created
✓ Frontend accessible on NodePort 30009
✓ Redis connectivity verified
✓ ALL CHECKS PASSED
All systems operational! 🎊
🔍 Understanding the Architecture
Multi-Tier Application Flow
User Request Flow:
1. User → Browser → http://NodeIP:30009
2. NodePort Service (frontend) → Load balances to one of 3 frontend pods
3. Frontend Pod (PHP) → Queries Redis
├─ Write → redis-master service → Redis Master pod
└─ Read → redis-slave service → One of 2 Redis Slave pods
4. Redis Master → Replicates data to → Redis Slaves
5. Response → Back through frontend → User
Data Flow:
Write: Frontend → Master → Slaves (async replication)
Read: Frontend → Slaves (load balanced)
Service Discovery with DNS
GET_HOSTS_FROM=dns explained:
Without DNS (hardcoded IPs):
frontend → connects to → 10.244.1.5:6379
Problem: IP changes on pod restart!
With DNS (service names):
frontend → resolves → redis-master.default.svc.cluster.local
→ gets → Current pod IP
→ connects → Always works!
DNS Records Created:
- redis-master → Points to master pod
- redis-slave → Load balances to slave pods
- frontend → Load balances to frontend pods
Application Code:
GET_HOSTS_FROM=dns tells app:
"Use service DNS names, not IPs"
Redis Master-Slave Replication
Architecture:
Redis Master (1 replica)
├─ Handles: ALL writes
├─ Replicates to: Both slaves
└─ High availability: No (single point)
Redis Slaves (2 replicas)
├─ Handle: Read operations
├─ Replicate from: Master
├─ Load balanced: Yes (via service)
└─ High availability: Yes (2 replicas)
Benefits:
✓ Read scaling (2x capacity)
✓ Write consistency (single master)
✓ Data redundancy
✓ Fault tolerance (slaves)
Limitations:
✗ Master is SPOF for writes
✗ Async replication (slight delay)
Resource Allocation
100m CPU + 100Mi Memory per container:
Total Resources Used:
Backend:
- Redis Master: 100m CPU, 100Mi RAM
- Redis Slave 1: 100m CPU, 100Mi RAM
- Redis Slave 2: 100m CPU, 100Mi RAM
Backend Total: 300m CPU, 300Mi RAM
Frontend:
- Frontend 1: 100m CPU, 100Mi RAM
- Frontend 2: 100m CPU, 100Mi RAM
- Frontend 3: 100m CPU, 100Mi RAM
Frontend Total: 300m CPU, 300Mi RAM
Application Total:
600m CPU (0.6 cores)
600Mi RAM
Cluster Requirement:
Minimum: 1 node with 1 core + 1GB RAM
Recommended: 3 nodes for HA
Labels and Selectors
Label strategy:
# Redis Master
labels:
app: redis
role: master
tier: backend
# Redis Slave
labels:
app: redis
role: slave
tier: backend
# Frontend
labels:
app: guestbook
tier: frontend
Usage:
kubectl get pods -l tier=backend # All Redis pods
kubectl get pods -l role=master # Master only
kubectl get pods -l app=guestbook # Frontend only
💡 Key Takeaways
✨ Multi-tier architecture separates concerns
✨ Master-slave replication enables read scaling
✨ Service discovery via DNS for flexibility
✨ Load balancing distributes traffic automatically
✨ Resource requests guarantee minimum resources
✨ Multiple replicas provide high availability
✨ NodePort service enables external access
✨ Labels organize and select resources
🎓 Quick Interview Questions
Q: Why 3 frontend replicas but only 1 Redis master? A: Frontend is stateless (can scale horizontally easily). Redis master handles writes (needs consistency, can't have multiple writers). Reads scale via slaves. Different scaling patterns for different tiers.
Q: What happens if Redis master fails? A: Writes fail. Reads continue (slaves still work). Need Redis Sentinel or Redis Cluster for automatic failover. StatefulSet + Sentinel promotes slave to master automatically.
Q: How does GET_HOSTS_FROM=dns work? A: App queries Kubernetes DNS for service names (redis-master, redis-slave). DNS returns current pod IPs. App connects without knowing specific IPs. Survives pod restarts/rescheduling.
Q: Why use specific image SHA256 for frontend? A: SHA256 guarantees exact image version (immutable). Tags like :latest can change. Production needs reproducibility. SHA256 ensures same image every time.
Q: How would you make this production-ready? A: Add: (1) StatefulSet for Redis with persistent storage, (2) Redis Sentinel for failover, (3) Ingress instead of NodePort, (4) Resource limits, (5) Health checks, (6) HPA for autoscaling, (7) Network policies.
🚀 Production Enhancements
1. Add Health Checks
# Frontend
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
# Redis
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
periodSeconds: 10
2. Add Resource Limits
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
3. Use Ingress Instead of NodePort
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: guestbook-ingress
spec:
rules:
- host: guestbook.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
4. Add Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: frontend-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
5. Redis StatefulSet with Persistence
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-master
spec:
serviceName: redis-master
replicas: 1
template:
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
6. Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-policy
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 6379
🔧 Troubleshooting
Issue 1: Frontend can't connect to Redis
# Check service DNS
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup redis-master
# Check endpoints
kubectl get endpoints redis-master redis-slave
# Test connectivity from frontend
kubectl exec -it $(kubectl get pod -l tier=frontend -o jsonpath='{.items[0].metadata.name}') -- curl redis-master:6379
Issue 2: Redis slaves not replicating
# Check master replication status
kubectl exec -it $(kubectl get pod -l role=master -o jsonpath='{.items[0].metadata.name}') -- redis-cli info replication
# Check slave logs
kubectl logs -l role=slave
# Verify environment variables
kubectl exec $(kubectl get pod -l role=slave -o jsonpath='{.items[0].metadata.name}') -- env | grep GET_HOSTS_FROM
Issue 3: NodePort not accessible
# Check service
kubectl describe service frontend
# Verify NodePort
kubectl get service frontend -o jsonpath='{.spec.ports[0].nodePort}'
# Check firewall
sudo firewall-cmd --list-ports
sudo firewall-cmd --add-port=30009/tcp --permanent
sudo firewall-cmd --reload
Issue 4: Pods not starting
# Check pod status
kubectl get pods
kubectl describe pod <pod-name>
# Check events
kubectl get events --sort-by='.lastTimestamp'
# Check logs
kubectl logs <pod-name>
📊 Monitoring
Monitor application:
# Watch all pods
kubectl get pods --watch
🎉 Final Thoughts
You've successfully deployed a complete multi-tier application on Kubernetes! This is production microservices architecture.
What you accomplished: ✅ Deployed 3-tier application (frontend + backend)
✅ Implemented Redis master-slave replication
✅ Configured service discovery with DNS
✅ Set up load balancing across tiers
✅ Exposed application via NodePort
✅ Verified end-to-end functionality
Real-world impact:
Scalability: Independent tier scaling
High Availability: Multiple replicas per tier
Performance: Read/write separation
Maintainability: Clear separation of concerns
Production Ready: Complete stack deployment
Skills acquired:
Multi-tier architecture design
Service mesh basics
Master-slave patterns
DNS-based service discovery
Complete application deployment
This is cloud-native microservices! 💪
Day: 67/100
Challenge: KodeKloud Cloud DevOps
Date: January 11, 2025
Topic: Multi-Tier Guestbook Application
How do you architect multi-tier applications? Share your microservices patterns! 📚




