DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

JumpToTech — Weekend Kubernetes Production Lab

Restaurant Company: Deploy, Configure, Scale & Troubleshoot

Goal

You are the DevOps Engineer responsible for deploying the Restaurant Company application into Kubernetes.

By the end of this lab, you should be able to explain and demonstrate:

EKS Cluster
    ↓
Worker Node
    ↓
Deployment
    ↓
ReplicaSet
    ↓
Pods
    ↓
Service
    ↓
Application

ConfigMap + Secret
        ↓
       Pods

Resources
CPU + Memory
        ↓
       Pods

Metrics Server
        ↓
       HPA
        ↓
Deployment replicas
Enter fullscreen mode Exit fullscreen mode

Do not simply copy commands. After every section, understand what Kubernetes created and why.


Part 1 — Verify Your Kubernetes Environment

Before creating anything:

kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

Your Worker Node should show:

STATUS
Ready
Enter fullscreen mode Exit fullscreen mode

Run:

kubectl get nodes -o wide
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe node <NODE-NAME>
Enter fullscreen mode Exit fullscreen mode

Find:

Capacity:
  cpu:
  memory:
  pods:

Allocatable:
  cpu:
  memory:
  pods:
Enter fullscreen mode Exit fullscreen mode

Questions

Students must answer:

  1. What is a Worker Node?
  2. What is Capacity?
  3. What is Allocatable?
  4. Why can Allocatable be smaller than Capacity?
  5. How many Pods can your Node run?
  6. Is Pod capacity the same thing as CPU capacity?
  7. What does 1930m CPU mean if you see it?

Remember:

1000m = 1 CPU core
500m  = 0.5 CPU
100m  = 0.1 CPU
Enter fullscreen mode Exit fullscreen mode

Part 2 — Create Your Working Directory

mkdir -p ~/weekend-k8s-lab
cd ~/weekend-k8s-lab
Enter fullscreen mode Exit fullscreen mode

Verify:

pwd
Enter fullscreen mode Exit fullscreen mode

You will create:

weekend-k8s-lab/
├── configmap.yaml
├── secret.yaml
├── deployment.yaml
├── service.yaml
└── hpa.yaml
Enter fullscreen mode Exit fullscreen mode

Part 3 — ConfigMap

Create:

nano configmap.yaml
Enter fullscreen mode Exit fullscreen mode

Add:

apiVersion: v1
kind: ConfigMap
metadata:
  name: restaurant-config
data:
  APP_ENV: "production"
  APP_NAME: "restaurant-company"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f configmap.yaml
Enter fullscreen mode Exit fullscreen mode

Verify:

kubectl get configmap
Enter fullscreen mode Exit fullscreen mode
kubectl describe configmap restaurant-config
Enter fullscreen mode Exit fullscreen mode

Understand

A ConfigMap stores non-sensitive configuration separately from the application image.

Examples:

APP_ENV
APP_NAME
LOG_LEVEL
API_URL
Enter fullscreen mode Exit fullscreen mode

Interview Question

Why shouldn't configuration always be hard-coded into the Docker image?


Part 4 — Secret

Create:

nano secret.yaml
Enter fullscreen mode Exit fullscreen mode

For this classroom lab only:

apiVersion: v1
kind: Secret
metadata:
  name: restaurant-secret
type: Opaque
stringData:
  DB_PASSWORD: "JumpToTech123"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f secret.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get secret
Enter fullscreen mode Exit fullscreen mode
kubectl describe secret restaurant-secret
Enter fullscreen mode Exit fullscreen mode

You may see:

Data
====
DB_PASSWORD:  13 bytes
Enter fullscreen mode Exit fullscreen mode

Important

Do not commit real production passwords to GitHub.

Also remember:

Kubernetes Secret is not automatically "safe because it is a Secret." Base64 encoding is not encryption.


Part 5 — Deployment

Create:

nano deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Use:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment

spec:
  replicas: 2

  selector:
    matchLabels:
      app: nginx

  template:
    metadata:
      labels:
        app: nginx

    spec:
      containers:
        - name: nginx
          image: nginx:1.27

          ports:
            - containerPort: 80

          env:
            - name: APP_ENV
              valueFrom:
                configMapKeyRef:
                  name: restaurant-config
                  key: APP_ENV

            - name: APP_NAME
              valueFrom:
                configMapKeyRef:
                  name: restaurant-config
                  key: APP_NAME

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: restaurant-secret
                  key: DB_PASSWORD

          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"

            limits:
              cpu: "500m"
              memory: "256Mi"

          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10

          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 20
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get deployment
Enter fullscreen mode Exit fullscreen mode
kubectl get pods
Enter fullscreen mode Exit fullscreen mode
kubectl get rs
Enter fullscreen mode Exit fullscreen mode

Students should understand:

Deployment
     ↓
ReplicaSet
     ↓
Pods
Enter fullscreen mode Exit fullscreen mode

Part 6 — Prove Self-Healing

Get Pods:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Delete ONE Deployment-managed Pod:

kubectl delete pod <POD-NAME>
Enter fullscreen mode Exit fullscreen mode

Immediately:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Observe

You deleted one Pod.

But the desired state says:

replicas: 2
Enter fullscreen mode Exit fullscreen mode

Therefore:

Desired = 2
Actual  = 1

ReplicaSet
    ↓
creates replacement
    ↓
Actual = 2
Enter fullscreen mode Exit fullscreen mode

Interview Question

Why did Kubernetes recreate the Pod?

Do not answer:

"Because Kubernetes always recreates Pods."

Correct idea:

The Pod is managed by a ReplicaSet created by the Deployment, and the controller reconciles actual state with desired state.


Part 7 — Verify ConfigMap and Secret Injection

Find a Pod:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Check ConfigMap values:

kubectl exec -it <POD-NAME> -- printenv APP_ENV
Enter fullscreen mode Exit fullscreen mode

Expected:

production
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl exec -it <POD-NAME> -- printenv APP_NAME
Enter fullscreen mode Exit fullscreen mode

Expected:

restaurant-company
Enter fullscreen mode Exit fullscreen mode

For this classroom lab only, verify that the Secret variable exists:

kubectl exec -it <POD-NAME> -- printenv DB_PASSWORD
Enter fullscreen mode Exit fullscreen mode

Do not expose production secrets this way.


Part 8 — Understand Resources

Your Deployment contains:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"

  limits:
    cpu: "500m"
    memory: "256Mi"
Enter fullscreen mode Exit fullscreen mode

Requests

Requests are especially important to scheduling.

New Pod
   ↓
CPU request = 100m
Memory request = 128Mi
   ↓
Scheduler
   ↓
Find suitable Node
Enter fullscreen mode Exit fullscreen mode

Limits

Limits constrain resource usage.

CPU limit exceeded
→ CPU may be throttled

Memory limit exceeded
→ Container may be terminated / OOMKilled
Enter fullscreen mode Exit fullscreen mode

Verify:

kubectl describe pod <POD-NAME>
Enter fullscreen mode Exit fullscreen mode

Find:

Requests:
  cpu:     100m
  memory:  128Mi

Limits:
  cpu:     500m
  memory:  256Mi
Enter fullscreen mode Exit fullscreen mode

Calculation

If there are 6 Pods:

6 × 100m CPU = 600m requested CPU

6 × 128Mi = 768Mi requested Memory
Enter fullscreen mode Exit fullscreen mode

Part 9 — Readiness Probe

Your Deployment contains:

readinessProbe:
  httpGet:
    path: /
    port: 80
Enter fullscreen mode Exit fullscreen mode

Readiness asks:

Should Kubernetes send normal Service traffic to this Pod?

Now intentionally break it.

Change:

path: /
Enter fullscreen mode Exit fullscreen mode

to:

path: /wrong-page
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

You may see:

READY
0/1
Enter fullscreen mode Exit fullscreen mode

But:

STATUS
Running
Enter fullscreen mode Exit fullscreen mode

Important Interview Question

Can a Pod be Running but not Ready?

YES.

Investigate:

kubectl describe pod <POD-NAME>
Enter fullscreen mode Exit fullscreen mode

Look at:

Events:
Enter fullscreen mode Exit fullscreen mode

Now fix:

path: /
Enter fullscreen mode Exit fullscreen mode

Apply again:

kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Expected eventually:

1/1 Running
Enter fullscreen mode Exit fullscreen mode

Part 10 — Liveness Probe

Your Deployment contains:

livenessProbe:
  httpGet:
    path: /
    port: 80
Enter fullscreen mode Exit fullscreen mode

Remember:

Readiness
→ Should this workload receive traffic?

Liveness
→ Is this workload healthy enough to keep running?
Enter fullscreen mode Exit fullscreen mode

Repeated liveness failures can cause kubelet to restart the Container.

Interview Question

Explain the difference between:

Readiness Probe
vs
Liveness Probe
Enter fullscreen mode Exit fullscreen mode

in your own words.


Part 11 — Create a Service

Create:

nano service.yaml
Enter fullscreen mode Exit fullscreen mode

Add:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service

spec:
  selector:
    app: nginx

  ports:
    - port: 80
      targetPort: 80

  type: ClusterIP
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl get endpoints nginx-service
Enter fullscreen mode Exit fullscreen mode

You should see Pod IP addresses.

Architecture:

Client
   ↓
nginx-service
   ↓
selector:
app: nginx
   ↓
matching Pods
Enter fullscreen mode Exit fullscreen mode

Important

The Service does not randomly discover your application.

It uses:

selector:
  app: nginx
Enter fullscreen mode Exit fullscreen mode

and finds Pods with:

labels:
  app: nginx
Enter fullscreen mode Exit fullscreen mode

Part 12 — Break the Service

Now create a real incident.

Change:

selector:
  app: nginx
Enter fullscreen mode Exit fullscreen mode

to:

selector:
  app: wrong-label
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get endpoints nginx-service
Enter fullscreen mode Exit fullscreen mode

You may see no endpoints.

Now troubleshoot:

kubectl get pods --show-labels
Enter fullscreen mode Exit fullscreen mode

Compare:

Service selector
vs
Pod labels
Enter fullscreen mode Exit fullscreen mode

Fix:

selector:
  app: nginx
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f service.yaml
Enter fullscreen mode Exit fullscreen mode

Check again:

kubectl get endpoints nginx-service
Enter fullscreen mode Exit fullscreen mode

Interview Scenario

Pods are Running, but the Service has no endpoints. What do you check?

Answer:

Pod labels and Service selector.


Part 13 — Metrics Server

Now check resource usage:

kubectl top nodes
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl top pods
Enter fullscreen mode Exit fullscreen mode

Understand the difference:

REQUEST
100m CPU

≠

ACTUAL USAGE
for example 5m CPU

≠

LIMIT
500m CPU
Enter fullscreen mode Exit fullscreen mode

Three different concepts.

Architecture:

Pod / Container
      ↓
    kubelet
      ↓
Metrics Server
      ↓
Kubernetes Metrics API
      ↓
HPA
Enter fullscreen mode Exit fullscreen mode

Part 14 — Create HPA

Create:

nano hpa.yaml
Enter fullscreen mode Exit fullscreen mode

Add:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa

spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment

  minReplicas: 2
  maxReplicas: 8

  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f hpa.yaml
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl get hpa
Enter fullscreen mode Exit fullscreen mode

You may see:

TARGETS
cpu: 0%/50%
Enter fullscreen mode Exit fullscreen mode

Interpret:

0% = current CPU utilization
50% = target
Enter fullscreen mode Exit fullscreen mode

Also:

kubectl describe hpa nginx-hpa
Enter fullscreen mode Exit fullscreen mode

Part 15 — Understand HPA Before Testing It

HPA does not simply mean:

CPU reaches exactly 50%
→ create exactly 1 Pod
Enter fullscreen mode Exit fullscreen mode

Instead, HPA evaluates the metric and calculates a desired replica count.

Also:

HPA
 ↓
changes desired replicas
 ↓
Deployment / ReplicaSet
 ↓
Pods are created/removed
Enter fullscreen mode Exit fullscreen mode

For this CPU-utilization HPA, your CPU request matters:

requests:
  cpu: "100m"
Enter fullscreen mode Exit fullscreen mode

Very simplified example:

CPU request = 100m

actual usage 10m
≈ 10%

actual usage 50m
≈ 50%

actual usage 80m
≈ 80%
Enter fullscreen mode Exit fullscreen mode

Part 16 — Generate Traffic

Make sure:

kubectl get svc nginx-service
Enter fullscreen mode Exit fullscreen mode

Then create a load generator:

kubectl run load-generator \
  --image=busybox:1.36 \
  --restart=Never \
  -- /bin/sh -c \
  'for i in 1 2 3 4 5 6 7 8 9 10; do while true; do wget -q -O- http://nginx-service > /dev/null; done & done; wait'
Enter fullscreen mode Exit fullscreen mode

Now use multiple terminals.

Terminal 1

kubectl get hpa -w
Enter fullscreen mode Exit fullscreen mode

Terminal 2

kubectl get pods -w
Enter fullscreen mode Exit fullscreen mode

Terminal 3

kubectl top pods
Enter fullscreen mode Exit fullscreen mode

Observe:

Load Generator
      ↓
HTTP requests
      ↓
Service
      ↓
Nginx Pods
      ↓
CPU utilization ↑
      ↓
Metrics Server
      ↓
HPA
      ↓
Desired replicas ↑
      ↓
Deployment / ReplicaSet
      ↓
More Pods
Enter fullscreen mode Exit fullscreen mode

Do not expect exact CPU percentages or exact scaling timing to be identical on every student's cluster.


Part 17 — Stop Traffic

Delete:

kubectl delete pod load-generator
Enter fullscreen mode Exit fullscreen mode

Watch:

kubectl get hpa -w
Enter fullscreen mode Exit fullscreen mode

CPU usage should decrease.

Eventually HPA can reduce replicas toward:

minReplicas = 2
Enter fullscreen mode Exit fullscreen mode

Scale-down may not happen immediately.


Part 18 — Production Incident: Pending Pods

This is based on a real incident from class.

Suppose:

Node Pod Capacity = 29
Current Pods      = 29
Enter fullscreen mode Exit fullscreen mode

HPA wants additional replicas.

What happens?

HPA
 ↓
Desired replicas ↑
 ↓
ReplicaSet creates new Pod objects
 ↓
Scheduler tries to place them
 ↓
Node = 29/29 Pods
 ↓
No available Pod slot
 ↓
Pending
Enter fullscreen mode Exit fullscreen mode

Investigate:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Then:

kubectl describe pod <PENDING-POD>
Enter fullscreen mode Exit fullscreen mode

Look at:

Events:
Enter fullscreen mode Exit fullscreen mode

You might find:

FailedScheduling
Too many pods
Enter fullscreen mode Exit fullscreen mode

Important

This is different from:

Insufficient cpu
Enter fullscreen mode Exit fullscreen mode

or:

Insufficient memory
Enter fullscreen mode Exit fullscreen mode

Do not guess the root cause.

Read the Events.


Part 19 — Kubernetes Troubleshooting Challenge

For each scenario, write:

1. Symptom
2. Command used
3. Root cause
4. Fix
5. Verification
Enter fullscreen mode Exit fullscreen mode

Incident A

Pod = Pending
Enter fullscreen mode Exit fullscreen mode

Start:

kubectl describe pod <POD>
Enter fullscreen mode Exit fullscreen mode

Check Events.

Incident B

Pod = Running
READY = 0/1
Enter fullscreen mode Exit fullscreen mode

Think:

Readiness Probe
Enter fullscreen mode Exit fullscreen mode

Incident C

Pods Running
Service exists
Endpoints = none
Enter fullscreen mode Exit fullscreen mode

Think:

Service selector
vs
Pod labels
Enter fullscreen mode Exit fullscreen mode

Incident D

Container keeps restarting
Enter fullscreen mode Exit fullscreen mode

Start:

kubectl describe pod <POD>
kubectl logs <POD>
Enter fullscreen mode Exit fullscreen mode

Consider liveness failures, application errors, OOMKilled, etc.

Incident E

HPA shows unknown CPU metric
Enter fullscreen mode Exit fullscreen mode

Check:

kubectl top pods
kubectl describe hpa nginx-hpa
Enter fullscreen mode Exit fullscreen mode

Also verify the workload has appropriate CPU requests for utilization-based CPU HPA.


Part 20 — Commands You Must Know

By Monday, students should be comfortable with:

kubectl get nodes
kubectl describe node <NODE>

kubectl get pods
kubectl get pods -o wide
kubectl get pods -w
kubectl describe pod <POD>
kubectl logs <POD>

kubectl get deployment
kubectl describe deployment nginx-deployment
kubectl get rs

kubectl get svc
kubectl describe svc nginx-service
kubectl get endpoints nginx-service

kubectl get configmap
kubectl get secret

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

kubectl top nodes
kubectl top pods

kubectl get hpa
kubectl get hpa -w
kubectl describe hpa nginx-hpa
Enter fullscreen mode Exit fullscreen mode

Final Architecture — Draw This Yourself

Every student must be able to draw and explain this without notes:

                    USERS
                      ↓
              Load Balancer
                      ↓
                   Service
                      ↓
          ┌───────────┼───────────┐
          ↓           ↓           ↓
        Pod 1       Pod 2       Pod 3
          │           │           │
          └──── Deployment ───────┘
                    ↓
               ReplicaSet


ConfigMap ─────────→ Pods
Secret ────────────→ Pods


Worker Node
├── CPU
├── Memory
├── Storage
├── Networking
└── Pod Capacity


Pods
 ↓
CPU / Memory usage
 ↓
kubelet
 ↓
Metrics Server
 ↓
HPA
 ↓
Deployment desired replicas
 ↓
ReplicaSet
 ↓
More / Fewer Pods
Enter fullscreen mode Exit fullscreen mode

Weekend Interview Questions

Students should prepare answers to these without reading from notes:

  1. What is Kubernetes?
  2. What is Amazon EKS?
  3. What is the Control Plane?
  4. What is a Worker Node?
  5. What is a Pod?
  6. What is a Container?
  7. What does kubectl do?
  8. What does the Scheduler do?
  9. What is a Deployment?
  10. What is a ReplicaSet?
  11. What does replicas: 3 mean?
  12. What is desired state?
  13. What is reconciliation?
  14. What happens when you delete a Deployment-managed Pod?
  15. Why can Pod IP addresses change?
  16. What problem does a Service solve?
  17. What is ClusterIP?
  18. What is LoadBalancer?
  19. How does a Service find Pods?
  20. What are labels?
  21. What is a selector?
  22. What are endpoints?
  23. What happens when Service selector does not match Pod labels?
  24. What is a ConfigMap?
  25. What is a Secret?
  26. ConfigMap vs Secret?
  27. What are resource requests?
  28. What are resource limits?
  29. Who uses requests during scheduling?
  30. What is 100m CPU?
  31. What is 1000m CPU?
  32. What can happen when CPU reaches its limit?
  33. What can happen when Memory exceeds its limit?
  34. What does OOMKilled mean?
  35. What is a Readiness Probe?
  36. What is a Liveness Probe?
  37. Can a Pod be Running but 0/1 Ready?
  38. What is Metrics Server?
  39. What does kubectl top pods show?
  40. What is HPA?
  41. What does Horizontal scaling mean?
  42. What is minReplicas?
  43. What is maxReplicas?
  44. What does averageUtilization: 50 mean?
  45. Why are CPU requests important for CPU-utilization HPA?
  46. Does HPA directly create Pods?
  47. Does HPA create Worker Nodes?
  48. What happens if HPA wants more Pods but the Node is full?
  49. What does Pending mean?
  50. How do you find why a Pod is Pending?
  51. What does FailedScheduling mean?
  52. What does Too many pods mean?
  53. What's the difference between CPU capacity and Pod capacity?
  54. What do you check when Pods are Running but Service has no endpoints?
  55. What commands would you use if an application is down?

Required Submission

Each student should submit screenshots showing:

1. kubectl get nodes
2. kubectl get pods
3. kubectl get deployment
4. kubectl get rs
5. kubectl get svc
6. kubectl get endpoints nginx-service
7. kubectl get configmap
8. kubectl get secret
9. kubectl describe pod showing Requests/Limits
10. kubectl top nodes
11. kubectl top pods
12. kubectl get hpa
13. HPA before load
14. Load generator Running
15. HPA during CPU load
16. Additional Nginx Pods created by scaling
17. HPA after load is removed
18. One troubleshooting example and its Events
Enter fullscreen mode Exit fullscreen mode

Top comments (0)