Skip to main content

Command Palette

Search for a command to run...

Day 50: Kubernetes Resource Management - CPU & Memory Limits 📊

Updated
11 min read
Day 50: Kubernetes Resource Management - CPU & Memory Limits 📊

Welcome back! 👋 Day 50 of the 100 Days Cloud DevOps Challenge, and today we're mastering Kubernetes Resource Management! This is critical for production stability - preventing resource starvation and ensuring fair resource allocation. Let's optimize! 🎯

🎯 The Mission - Pod Resource Limits

📋 TASK TICKET #DEV-8050 - Kubernetes Resource Constraints
Priority: HIGH | Type: Performance Optimization
Server: Jump Host | Cluster: Kubernetes

PROBLEM:
- Applications experiencing performance issues
- Resource constraints causing instability
- Need to enforce resource limits

REQUIREMENTS:
1. Pod Name: httpd-pod
2. Container Name: httpd-container
3. Image: httpd:latest
4. Resource Requests:
   - Memory: 15Mi
   - CPU: 100m
5. Resource Limits:
   - Memory: 20Mi
   - CPU: 100m

VERIFICATION:
- Pod running successfully
- Resources enforced
- No OOMKilled events

This is production resource optimization!

🤔 Why Resource Limits Matter?

Without Limits:

  • ❌ Pods can consume all node resources

  • ❌ Noisy neighbor problem

  • ❌ Node crashes from memory pressure

  • ❌ CPU starvation for other apps

  • ❌ Unpredictable performance

With Limits:

  • ✅ Fair resource allocation

  • ✅ Predictable performance

  • ✅ Node stability

  • ✅ Efficient scheduling

  • ✅ Cost optimization

Real scenario:

Without limits:
Pod A: Uses 8GB (hog!)
Pod B: Uses 100MB
Pod C: OOMKilled (not enough memory)

With limits:
Pod A: Limited to 2GB
Pod B: Guaranteed 100MB
Pod C: Runs successfully ✓

🛠️ Complete Implementation

Step 1: Access Jump Host

# SSH to jump host
ssh user@jump_host

# Verify kubectl
kubectl version --short
kubectl cluster-info

Environment ready!

Step 2: Create Pod with Resource Limits

Method 1: Declarative (YAML) - Recommended

# Create pod manifest
cat > httpd-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: httpd-pod
  labels:
    app: httpd
spec:
  containers:
  - name: httpd-container
    image: httpd:latest
    resources:
      requests:
        memory: "15Mi"
        cpu: "100m"
      limits:
        memory: "20Mi"
        cpu: "100m"
    ports:
    - containerPort: 80
EOF

# Apply the manifest
kubectl apply -f httpd-pod.yaml

Expected output:

pod/httpd-pod created

YAML breakdown:

apiVersion: v1              # Core API
kind: Pod                   # Resource type
metadata:
  name: httpd-pod           # Pod name (required)
  labels:
    app: httpd              # Label for selection
spec:
  containers:
  - name: httpd-container   # Container name (required)
    image: httpd:latest     # Image with tag
    resources:              # Resource constraints
      requests:             # Minimum guaranteed
        memory: "15Mi"      # 15 Mebibytes RAM
        cpu: "100m"         # 100 millicores (0.1 CPU)
      limits:               # Maximum allowed
        memory: "20Mi"      # 20 Mebibytes RAM
        cpu: "100m"         # 100 millicores (0.1 CPU)
    ports:
    - containerPort: 80     # Container listens on port 80

Method 2: Imperative (kubectl run) - Quick Testing

# Create pod with dry-run to generate YAML
kubectl run httpd-pod \
  --image=httpd:latest \
  --dry-run=client -o yaml > httpd-pod.yaml

# Edit to add resources
# Then apply
kubectl apply -f httpd-pod.yaml

Step 3: Verify Pod Creation

# Check pod status
kubectl get pods

Expected output:

NAME        READY   STATUS    RESTARTS   AGE
httpd-pod   1/1     Running   0          30s

Detailed view:

# View with resource info
kubectl get pod httpd-pod -o wide

Expected output:

NAME        READY   STATUS    RESTARTS   AGE   IP           NODE     
httpd-pod   1/1     Running   0          1m    10.244.1.5   node01

Step 4: Verify Resource Limits

# Describe pod to see resources
kubectl describe pod httpd-pod

Expected output (key sections):

Name:             httpd-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             node01/172.17.0.3
Start Time:       Thu, 26 Dec 2024 10:00:00 +0000
Labels:           app=httpd
Status:           Running
IP:               10.244.1.5
Containers:
  httpd-container:
    Container ID:   containerd://abc123...
    Image:          httpd:latest
    Image ID:       docker.io/library/httpd@sha256:def456...
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Thu, 26 Dec 2024 10:00:05 +0000
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     100m
      memory:  20Mi
    Requests:
      cpu:        100m
      memory:     15Mi
    Environment:  <none>

Key verification points:

  • ✅ Limits: cpu: 100m, memory: 20Mi

  • ✅ Requests: cpu: 100m, memory: 15Mi

  • ✅ Status: Running

  • ✅ Ready: True

Check resource allocation:

# View pod resources in JSON format
kubectl get pod httpd-pod -o jsonpath='{.spec.containers[0].resources}'

Expected output:

{"limits":{"cpu":"100m","memory":"20Mi"},"requests":{"cpu":"100m","memory":"15Mi"}}

Step 5: Test Pod Functionality

# Check pod logs
kubectl logs httpd-pod

Expected output:

AH00558: httpd: Could not reliably determine the server's fully qualified domain name
[Thu Dec 26 10:00:05.123456 2024] [mpm_event:notice] [pid 1:tid 140] AH00489: Apache/2.4.58 (Unix) configured
[Thu Dec 26 10:00:05.123456 2024] [core:notice] [pid 1:tid 140] AH00094: Command line: 'httpd -D FOREGROUND'

Test HTTP server:

# Get pod IP
POD_IP=$(kubectl get pod httpd-pod -o jsonpath='{.status.podIP}')

# Test connectivity (from another pod or node)
kubectl run curl-test --image=curlimages/curl:latest --rm -it --restart=Never -- curl http://$POD_IP

Expected output:

<html><body><h1>It works!</h1></body></html>

Step 6: Monitor Resource Usage

# Check actual resource consumption
kubectl top pod httpd-pod

Expected output:

NAME        CPU(cores)   MEMORY(bytes)   
httpd-pod   1m           12Mi

Analysis:

  • CPU: 1m (well under 100m limit)

  • Memory: 12Mi (within 15Mi request, under 20Mi limit)

  • Pod running efficiently ✅

View all pod resources:

# List all pods with resources
kubectl get pods -o custom-columns=NAME:.metadata.name,CPU_REQUEST:.spec.containers[0].resources.requests.cpu,CPU_LIMIT:.spec.containers[0].resources.limits.cpu,MEM_REQUEST:.spec.containers[0].resources.requests.memory,MEM_LIMIT:.spec.containers[0].resources.limits.memory

Expected output:

NAME        CPU_REQUEST   CPU_LIMIT   MEM_REQUEST   MEM_LIMIT
httpd-pod   100m          100m        15Mi          20Mi

Step 7: Complete Verification

# Comprehensive verification script
cat > verify.sh << 'EOF'
#!/bin/bash
echo "=== Kubernetes Resource Management Verification ==="
echo ""
echo "1. Pod Status:"
kubectl get pod httpd-pod
echo ""
echo "2. Resource Limits:"
kubectl get pod httpd-pod -o jsonpath='{.spec.containers[0].resources}' | jq
echo ""
echo "3. Current Usage:"
kubectl top pod httpd-pod
echo ""
echo "4. Pod Events:"
kubectl get events --field-selector involvedObject.name=httpd-pod --sort-by='.lastTimestamp' | tail -5
echo ""
echo "5. Container Status:"
kubectl get pod httpd-pod -o jsonpath='{.status.containerStatuses[0].state}'
echo ""
echo "✓ VERIFICATION COMPLETE"
EOF

chmod +x verify.sh
./verify.sh

Expected output:

=== Kubernetes Resource Management Verification ===

1. Pod Status:
NAME        READY   STATUS    RESTARTS   AGE
httpd-pod   1/1     Running   0          5m

2. Resource Limits:
{
  "limits": {
    "cpu": "100m",
    "memory": "20Mi"
  },
  "requests": {
    "cpu": "100m",
    "memory": "15Mi"
  }
}

3. Current Usage:
NAME        CPU(cores)   MEMORY(bytes)   
httpd-pod   1m           12Mi

4. Pod Events:
LAST SEEN   TYPE     REASON      OBJECT          MESSAGE
5m          Normal   Scheduled   pod/httpd-pod   Successfully assigned default/httpd-pod to node01
5m          Normal   Pulling     pod/httpd-pod   Pulling image "httpd:latest"
5m          Normal   Pulled      pod/httpd-pod   Successfully pulled image "httpd:latest"
5m          Normal   Created     pod/httpd-pod   Created container httpd-container
5m          Normal   Started     pod/httpd-pod   Started container httpd-container

5. Container Status:
{"running":{"startedAt":"2024-12-26T10:00:05Z"}}

✓ VERIFICATION COMPLETE

All checks passed! 🎉

🔍 Understanding Resource Management

Requests vs Limits

Requests (Minimum Guaranteed):

CPU: 100m (0.1 core)
- Scheduler ensures node has this available
- Pod won't be scheduled on nodes without it
- Used for scheduling decisions

Memory: 15Mi
- Guaranteed to pod
- Pod won't be evicted if using this amount
- Minimum required for application

Limits (Maximum Allowed):

CPU: 100m (0.1 core)
- Pod throttled if exceeding this
- Can't use more than limit
- Prevents resource hogging

Memory: 20Mi
- Pod killed (OOMKilled) if exceeding this
- Hard cap on memory usage
- Protects node from memory exhaustion

Resource behavior:

Scenario 1: Normal operation
Usage: 12Mi RAM, 50m CPU
✓ Within both request and limit
✓ Pod runs normally

Scenario 2: High CPU usage
Usage: 15Mi RAM, 150m CPU (exceeds limit)
✓ Memory OK
✗ CPU throttled to 100m (limit enforced)

Scenario 3: Memory spike
Usage: 25Mi RAM (exceeds limit), 80m CPU
✗ Pod OOMKilled and restarted
✓ CPU OK

Scenario 4: Low resources
Usage: 10Mi RAM, 30m CPU
✓ Using less than request (efficient!)
✓ Pod runs normally

CPU Units Explained

Millicore (m) notation:

1000m = 1 CPU core
100m = 0.1 CPU core (10%)
500m = 0.5 CPU core (50%)
1500m = 1.5 CPU cores

Examples:
100m = Small workload (web server)
500m = Medium workload (API service)
1000m = Large workload (database)
2000m = Heavy workload (ML training)

CPU is compressible:

  • Can be throttled without killing pod

  • Pod slows down but keeps running

  • Shares CPU time with other pods

Memory Units Explained

Memory notation:

Ki = Kibibyte (1024 bytes)
Mi = Mebibyte (1024 Ki)
Gi = Gibibyte (1024 Mi)

15Mi = 15 × 1024 × 1024 = 15,728,640 bytes
20Mi = 20 × 1024 × 1024 = 20,971,520 bytes

Common sizes:
10Mi = Very small (sidecar)
100Mi = Small (simple app)
500Mi = Medium (web app)
2Gi = Large (database)

Memory is NOT compressible:

  • Can't be throttled

  • Exceeding limit = OOMKilled

  • Pod restarted automatically

  • Critical to set correctly!

QoS Classes

Kubernetes assigns QoS based on resources:

1. Guaranteed (Highest priority):

# Requests = Limits for ALL resources
requests:
  memory: "20Mi"
  cpu: "100m"
limits:
  memory: "20Mi"
  cpu: "100m"

2. Burstable (Medium priority):

# Requests < Limits (Our case!)
requests:
  memory: "15Mi"
  cpu: "100m"
limits:
  memory: "20Mi"
  cpu: "100m"

3. BestEffort (Lowest priority):

# No requests or limits set
# First to be evicted under pressure

Check QoS class:

kubectl get pod httpd-pod -o jsonpath='{.status.qosClass}'

Expected output:

Burstable

Eviction order under pressure:

1. BestEffort pods (first to go)
2. Burstable pods exceeding requests
3. Burstable pods within requests
4. Guaranteed pods (last resort)

💡 Key Takeaways

Requests guarantee minimum resources
Limits cap maximum usage
CPU is compressible (throttled)
Memory is NOT compressible (OOMKilled)
100m = 0.1 CPU core
Mi = Mebibytes (1024-based)
QoS classes determine eviction priority
Resource management prevents noisy neighbors

🎓 Quick Interview Questions

Q: What happens if a pod exceeds CPU limits? A: CPU is throttled to the limit. Pod continues running but slows down. This is called CPU throttling or CPU pressure.

Q: What happens if a pod exceeds memory limits? A: Pod is immediately killed (OOMKilled) and restarted. Memory is incompressible, so exceeding limits causes termination.

Q: Why set requests lower than limits? A: Allows burst capacity. Pod guaranteed minimum (request) but can use more (up to limit) when available. Efficient resource utilization.

Q: What's the difference between 100m CPU and 0.1 CPU? A: They're identical. 100m = 100 millicores = 0.1 core. Just different notation, same value.

Q: How do you choose appropriate resource limits? A: Monitor actual usage with kubectl top, add 20-30% buffer for spikes, test under load, adjust based on performance metrics.

🚀 Common Resource Scenarios

Scenario 1: High Memory Application (Database)

apiVersion: v1
kind: Pod
metadata:
  name: postgres-pod
spec:
  containers:
  - name: postgres
    image: postgres:15
    resources:
      requests:
        memory: "1Gi"
        cpu: "500m"
      limits:
        memory: "2Gi"
        cpu: "1000m"

Scenario 2: CPU-Intensive Application (Video Encoding)

apiVersion: v1
kind: Pod
metadata:
  name: encoder-pod
spec:
  containers:
  - name: encoder
    image: ffmpeg:latest
    resources:
      requests:
        memory: "512Mi"
        cpu: "2000m"
      limits:
        memory: "1Gi"
        cpu: "4000m"

Scenario 3: Minimal Sidecar Container

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  containers:
  - name: main-app
    image: myapp:latest
    resources:
      requests:
        memory: "256Mi"
        cpu: "500m"
      limits:
        memory: "512Mi"
        cpu: "1000m"
  - name: logging-sidecar
    image: fluentd:latest
    resources:
      requests:
        memory: "50Mi"
        cpu: "50m"
      limits:
        memory: "100Mi"
        cpu: "100m"

Scenario 4: Guaranteed QoS (Critical Service)

apiVersion: v1
kind: Pod
metadata:
  name: critical-api
spec:
  containers:
  - name: api
    image: api:latest
    resources:
      requests:
        memory: "1Gi"
        cpu: "1000m"
      limits:
        memory: "1Gi"  # Same as requests
        cpu: "1000m"   # Same as requests

🌟 kubectl Resource Commands

View resources:

kubectl top nodes                    # Node resource usage
kubectl top pods                     # All pods usage
kubectl top pod httpd-pod            # Specific pod usage
kubectl describe node node01         # Node capacity/allocatable

Set resources:

# For deployments
kubectl set resources deployment nginx --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi

# Edit existing pod (must delete and recreate)
kubectl delete pod httpd-pod
kubectl apply -f httpd-pod.yaml

View resource allocation:

# See all pod resources
kubectl get pods -o custom-columns=NAME:.metadata.name,MEMORY_REQUEST:.spec.containers[*].resources.requests.memory,MEMORY_LIMIT:.spec.containers[*].resources.limits.memory

# Node resource pressure
kubectl describe nodes | grep -A 5 "Allocated resources"

🔧 Troubleshooting

Issue 1: Pod OOMKilled

# Check events
kubectl describe pod httpd-pod | grep -A 10 "Events"

# Solution: Increase memory limits
# Edit YAML: limits.memory: "40Mi"
kubectl delete pod httpd-pod
kubectl apply -f httpd-pod.yaml

Issue 2: Pod Pending (Insufficient Resources)

# Check why pending
kubectl describe pod httpd-pod

# Look for: "Insufficient cpu" or "Insufficient memory"

# Solution: Reduce requests or add nodes
# Reduce: requests.cpu: "50m"

Issue 3: CPU Throttling

# Check metrics
kubectl top pod httpd-pod

# If CPU at limit constantly
# Solution: Increase CPU limits
# limits.cpu: "200m"

Issue 4: Can't Update Pod Resources

# Error: "field is immutable"

# Solution: Delete and recreate
kubectl delete pod httpd-pod
kubectl apply -f httpd-pod.yaml

# Or use Deployments (support rolling updates)

📊 Resource Planning Best Practices

1. Start Conservative:

# Begin with minimal resources
requests:
  memory: "64Mi"
  cpu: "50m"
limits:
  memory: "128Mi"
  cpu: "100m"

2. Monitor and Adjust:

# Watch for 1 week
kubectl top pod httpd-pod --watch

# Adjust to peak + 30% buffer

3. Use Limits:

# Always set limits (prevent runaway)
limits:
  memory: "512Mi"  # Hard cap
  cpu: "500m"      # Throttle at this

4. Match Requests to Limits for Critical Apps:

# Guaranteed QoS for production
requests:
  memory: "1Gi"
  cpu: "1000m"
limits:
  memory: "1Gi"    # Same = Guaranteed
  cpu: "1000m"

5. Use ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: namespace-quota
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"

🎉 Final Thoughts

You've successfully mastered Kubernetes resource management! This is critical for production stability and cost optimization.

What you accomplished: ✅ Created pod with resource constraints
✅ Set CPU and memory limits
✅ Understood requests vs limits
✅ Learned QoS classes
✅ Mastered resource troubleshooting

Real-world impact:

  • Prevent outages (no resource starvation)

  • Cost optimization (right-sized resources)

  • Fair allocation (no noisy neighbors)

  • Predictable performance (guaranteed resources)

  • Cluster stability (protected nodes)

Skills acquired:

  • Resource limit configuration

  • CPU/Memory unit conversion

  • QoS class understanding

  • Resource monitoring

  • Troubleshooting techniques

This is production-grade Kubernetes! 💪


Day: 50/100
Challenge: KodeKloud Cloud DevOps
Date: December 25, 2025
Topic: Kubernetes Resource Management

How do you size resources for your applications? Share your resource management strategies! 📊

More from this blog

🚀 DevOps Challenge- KodeKloud Solutions

73 posts