Kubernetes Practicals: E Commerce Platform Deployment Assignment Overview
Duration: 4-6 hours Group Size: 3 people Difficulty: Intermediate Prerequisites: Completed Kubernetes installation, understanding of pods, deployments, replica sets, taints, tolerations, node selectors, affinity, scheduling, and static pods.
Your team has been hired to deploy a microservices-based e-commerce platform on Kubernetes. The platform consists of multiple services with different requirements:
- Frontend - Customer-facing web application
- Backend API - Business logic and data processing
- Database - PostgreSQL database
- Cache - Redis cache layer
- Monitoring - Custom monitoring agent (static pod)
Each service has specific placement, scaling, and resource requirements that you must implement using Kubernetes scheduling features.
By completing this assignment, you will:
- Deploy a multi-tier application on Kubernetes
- Implement node affinity and anti-affinity rules
- Use taints and tolerations for specialized workloads
- Configure node selectors for environment separation
- Create and manage static pods
- Scale deployments and understand replica sets
- Troubleshoot common Kubernetes issues
- Use kubectl effectively for cluster management
minutes)
Using the kubernetes-installation-guide.md , set up a 3-node Kubernetes cluster on AWS: 1 Control Plane node 2 Worker nodes
# Verify all nodes are Ready
kubectl get nodes
# Expected output:
# NAME STATUS ROLES AGE VERSION
# control-plane Ready control-plane 10m v1.35.x
# worker-node-1 Ready <none> 5m v1.35.x
# worker-node-2 Ready <none> 5m v1.35.x
Label your worker nodes to simulate different environments and hardware capabilities:
# Label worker-node-1 as production with SSD storage
kubectl label nodes <worker-node-1> environment=production
kubectl label nodes <worker-node-1> storage=ssd
kubectl label nodes <worker-node-1> tier=frontend
# Label worker-node-2 as production with HDD storage
kubectl label nodes <worker-node-2> environment=production
kubectl label nodes <worker-node-2> storage=hdd
kubectl label nodes <worker-node-2> tier=backend
Verification:
kubectl get nodes --show-labels
Apply taints to control workload placement:
# Taint worker-node-1 for frontend workloads only
kubectl taint nodes <worker-node-1> workload=frontend:NoSchedule
# Taint worker-node-2 for backend workloads only
kubectl taint nodes <worker-node-2> workload=backend:NoSchedule
Verification:
kubectl describe node <worker-node-1> | grep -i taint
kubectl describe node <worker-node-2> | grep -i taint
Screenshot of kubectl get nodes --show-labels
Screenshot of taint verification commands
Create a dedicated namespace for the application:
kubectl create namespace ecommerce
Set this as your default namespace:
kubectl config set-context --current --namespace=ecommerce
##Task 2.2: Deploy PostgreSQL Database
Create a file named postgres-deployment.yaml :
Requirements:
Use postgres:16 image
Set environment variable POSTGRES_PASSWORD=secure_password
Use node selector to run on the backend tier
Add toleration for backend taint
Set resource requests: CPU 500m, Memory 512Mi
Set resource limits: CPU 1000m, Memory 1Gi
Create a single replica
Expose port 5432
Hints:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: ecommerce
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
# Add node selector here
# Add tolerations here
containers:
- name: postgres
image: postgres:14
# Complete the restCreate postgres-service.yaml :
Requirements:
- Service type: ClusterIP
- Port: 5432
- Selector: app=postgres
# Apply the manifests
kubectl apply -f postgres-deployment.yaml
kubectl apply -f postgres-service.yaml
# Verify deployment
kubectl get pods -o wide
kubectl get svc
kubectl describe pod <postgres-pod-name>
- On which node did the postgres pod get scheduled? Why?
- What happens if you remove the toleration? Test it and explain.
- Can you access the database from outside the cluster? Why or why not?
postgres-deployment.yamlfilepostgres-service.yamlfile- Screenshot showing pod running on the correct node
- Answers to the questions
Create redis-deployment.yaml :
- Use
redis:7-alpineimage - Create 2 replicas for high availability
- Use pod anti-affinity to ensure replicas run on different nodes
- Add toleration to run on both frontend and backend nodes
- Set resource requests: CPU 250m, Memory 256Mi
- Expose port 6379
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"Create redis-service.yaml :
Requirements:
- Service type: ClusterIP
- Port: 6379
- Selector: app=redis
kubectl apply -f redis-deployment.yaml
kubectl apply -f redis-service.yaml
# Check which nodes the pods are running on
kubectl get pods -o wide -l app=redis
- Are the Redis pods running on different nodes? Why?
- What would happen if you tried to scale to 3 replicas but only have 2 worker nodes?
- Change the anti-affinity rule to
preferredDuringSchedulingIgnoredDuringExecution. What's the difference?
redis-deployment.yaml file
redis-service.yaml file
Screenshot showing pods on different nodes
Answers to the questions
Create backend-deployment.yaml :
- Use
nginx:alpineimage (as a placeholder for your backend) - Create 3 replicas
- Use node affinity to prefer nodes with SSD storage but require backend tier
- Add toleration for backend workload
- Set resource requests: CPU 200m, Memory 256Mi
- Expose port 80
- Add environment variables:
- DATABASE_HOST=postgres
- REDIS_HOST=redis
Node Affinity Example:
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: tier operator: In values: - backend preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: storage operator: In values: - ssd
Create backend-service.yaml :
- Service type: ClusterIP
- Port: 8080 (target port 80)
- Selector: app=backend
# Create a test pod to verify connectivity
kubectl run test-pod --image=curlimages/curl --rm -it --restart=Never -- sh
# Inside the pod, test connections:
curl http://backend:8080
nslookup postgres
nslookup redis
- On which node(s) are the backend pods scheduled? Why?
- What's the difference between requiredDuringScheduling and preferredDuringScheduling ?
- Can the backend pods communicate with postgres and redis? Demonstrate.
backend-deployment.yamlfilebackend-service.yamlfile- Screenshot of connectivity tests
- Answers to the questions
Create frontend-deployment.yaml :
Use nginx:alpine image
Create 4 replicas
Use node selector for frontend tier
Add toleration for frontend workload
Set resource requests: CPU 100m, Memory 128Mi
Expose port 80
Add environment variable: BACKEND_URL=http://backend:8080
Create frontend-service.yaml :
Service type: NodePort Port: 80 NodePort: 30080 Selector: app=frontend
# Get the external IP of any worker node
kubectl get nodes -o wide
# Access the frontend (replace with your node IP)
curl http://<NODE_IP>:30080
# Scale frontend to 6 replicas
kubectl scale deployment frontend --replicas=6
# Watch the scaling process
kubectl get pods -l app=frontend -w
- Where are all the frontend pods scheduled? Why?
- What happens if you try to scale to 10 replicas on a single node?
- How does the NodePort service distribute traffic across replicas?
- Access the application from your browser using http://<NODE_IP>:30080. Does it work?
frontend-deployment.yamlfilefrontend-service.yamlfile- Screenshot showing 6 replicas running
- Screenshot of accessing the application
- Answers to the questions
Static pods are managed directly by the kubelet on a specific node, not by the API server.
SSH into worker-node-1:
ssh ubuntu@<worker-node-1-ip>
Find the kubelet static pod path:
# Check kubelet configuration
sudo cat /var/lib/kubelet/config.yaml | grep staticPodPath
# Usually it's /etc/kubernetes/manifests
Create a static pod manifest:
sudo mkdir -p /etc/kubernetes/manifests
sudo nano /etc/kubernetes/manifests/monitoring-agent.yaml
apiVersion: v1
kind: Pod
metadata:
name: monitoring-agent
namespace: kube-system
labels:
app: monitoring
component: agent
spec:
containers:
- name: agent
image: busybox:latest
command:
- sh
- -c
- |
while true; do
echo "$(date): Monitoring node $(hostname)"
sleep 30
done
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi# On the control plane
kubectl get pods -n kube-system -o wide | grep monitoring
# Check logs
kubectl logs -n kube-system monitoring-agent-<node-name>
# Try to delete it
kubectl delete pod monitoring-agent-<node-name> -n kube-system
# What happens? Check again
kubectl get pods -n kube-system | grep monitoring
- What happens when you try to delete a static pod? Why?
- How would you actually remove a static pod?
- Where does the static pod show up when you run kubectl get pods -A ?
- What are the use cases for static pods?
- Screenshot of static pod running
- Screenshot showing pod recreation after deletion attempt
- Static pod manifest file
- Answers to the questions
kubectl drain <worker-node-1> --ignore-daemonsets --delete-emptydir-data
Observe:
# Watch pod rescheduling
kubectl get pods -o wide --watch
- What happened to the pods running on worker-node-1?
- Did all pods get rescheduled? Which ones didn't and why?
- What happened to the static pod?
kubectl uncordon <worker-node-1>
Create a intentionally broken deployment:
# Create broken-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: broken-app
namespace: ecommerce
spec:
replicas: 3
selector:
matchLabels:
app: broken
template:
metadata:
labels:
app: broken
spec:
nodeSelector:
tier: frontend
tolerations:
- key: workload
operator: Equal
value: frontend
effect: NoSchedule
containers:
- name: app
image: nginx:alpine
resources:
requests:
cpu: 5000m # Intentionally too high!
memory: 10Gikubectl apply -f broken-app.yaml
# Investigate why pods aren't scheduling
kubectl get pods -l app=broken
kubectl describe pods -l app=broken
- Why aren't the pods scheduling?
- What kubectl commands did you use to diagnose the issue?
- How did you fix it?
- Screenshot of drained node
- Screenshot of pods after rescheduling
- Explanation of troubleshooting steps
- Fixed broken-app.yaml
- Answers to the questions
Create priority classes:
# Create high-priority.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "High priority class for critical workloads"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 100
globalDefault: false
description: "Low priority class for non-critical workloads"Update your frontend deployment to use high-priority:
spec:
template:
spec:
priorityClassName: high-priority
# ... rest of the specCreate a low-priority workload:
# Create batch-job-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: batch-processor
namespace: ecommerce
spec:
replicas: 5
selector:
matchLabels:
app: batch
template:
metadata:
labels:
app: batch
spec:
priorityClassName: low-priority
tolerations:
- key: workload
operator: Equal
value: backend
effect: NoSchedule
containers:
- name: processor
image: busybox
command: ["sh", "-c", "sleep 3600"]
resources:
requests:
cpu: 200m
memory: 256Mi