Skip to main content

Command Palette

Search for a command to run...

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

Published
14 min read
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! 📚

More from this blog

🚀 DevOps Challenge- KodeKloud Solutions

73 posts