Learning Kubernetes - Episode 29 - Introduction and Explanation of Computational Resources

Learning Kubernetes - Episode 29 - Introduction and Explanation of Computational Resources

In this episode, we'll discuss Kubernetes computational resources management. We'll learn why we should always specify CPU and memory requests and limits, how resource allocation works, and best practices for resource management.

Arman Dwi Pangestu
Arman Dwi PangestuApril 4, 2026
0 views
9 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 28 thumbnail below

Episode 28Episode 28

In the previous episode, we learned about Kubernetes Dashboard for managing clusters through a web interface. In episode 29, we'll discuss Computational Resources, specifically CPU and memory management - one of the most critical aspects of running production workloads in Kubernetes.

Note: Here I'll be using a Kubernetes Cluster installed through K3s.

Understanding resource requests and limits is essential for cluster stability, efficient resource utilization, and preventing one application from starving others. Without proper resource management, your cluster can become unstable, unpredictable, and expensive.

What Are Computational Resources?

Computational Resources in Kubernetes refer to CPU and memory that containers can consume. Kubernetes allows you to specify how much of these resources a container needs (requests) and the maximum it can use (limits).

Think of resources like a restaurant reservation - requests are your guaranteed table (minimum resources), while limits are the maximum number of people you can bring (maximum resources). The restaurant (node) needs to know both to manage seating effectively.

Key resource types:

  • CPU - Measured in cores (or millicores)
  • Memory - Measured in bytes (Ki, Mi, Gi)
  • Ephemeral Storage - Temporary disk space
  • Extended Resources - Custom resources (GPUs, etc.)

Requests vs Limits

Understanding the difference is crucial.

Requests

Requests define the minimum amount of resources guaranteed to a container.

  • Used by scheduler to decide which node can run the Pod
  • Container is guaranteed this amount
  • Node must have available resources >= requests
  • Used for resource reservation
  • Affects Pod scheduling decisions

Limits

Limits define the maximum amount of resources a container can use.

  • Container cannot exceed this amount
  • If exceeded, container is throttled (CPU) or killed (memory)
  • Used for resource protection
  • Prevents resource exhaustion
  • Affects runtime behavior

Relationship Between Requests and Limits

Kubernetesyml
resources:
    requests:
        memory: "256Mi"  # Guaranteed minimum
        cpu: "250m"      # Guaranteed minimum
    limits:
        memory: "512Mi"  # Maximum allowed
        cpu: "500m"      # Maximum allowed

Rules:

  • Requests ≤ Limits (always)
  • Requests = what you need
  • Limits = what you might need at peak

Why Always Specify Resources?

Let's understand why resource specification is critical.

Problem 1: Unpredictable Scheduling

Without requests:

Kubernetesyml
# Bad: No resource requests
spec:
    containers:
        - name: app
          image: myapp:latest
          # No resources specified

Issues:

  • Scheduler doesn't know Pod requirements
  • May schedule on overloaded nodes
  • Can cause node resource exhaustion
  • Unpredictable performance

With requests:

Kubernetesyml
# Good: Clear resource requests
spec:
    containers:
        - name: app
          image: myapp:latest
          resources:
              requests:
                  memory: "256Mi"
                  cpu: "250m"

Benefits:

  • Scheduler makes informed decisions
  • Pods scheduled on appropriate nodes
  • Predictable resource availability
  • Better cluster utilization

Problem 2: Resource Starvation

Without limits:

Kubernetesyml
# Bad: No resource limits
spec:
    containers:
        - name: memory-leak-app
          image: leaky:latest
          # No limits - can consume all node memory

Issues:

  • One Pod can consume all node resources
  • Other Pods starved of resources
  • Node becomes unstable
  • Cascading failures

With limits:

Kubernetesyml
# Good: Resource limits protect node
spec:
    containers:
        - name: memory-leak-app
          image: leaky:latest
          resources:
              limits:
                  memory: "512Mi"
                  cpu: "500m"

Benefits:

  • Container cannot exceed limits
  • Other Pods protected
  • Node remains stable
  • Predictable behavior

Problem 3: Cost Inefficiency

Without proper resources:

  • Over-provisioning wastes money
  • Under-provisioning causes failures
  • No visibility into actual usage
  • Difficult to optimize costs

With proper resources:

  • Right-sized allocations
  • Efficient resource utilization
  • Clear cost attribution
  • Easy to optimize

Problem 4: Quality of Service (QoS)

Kubernetes assigns QoS classes based on resources:

Guaranteed (highest priority):

  • Requests = Limits for all containers
  • Best performance guarantee
  • Last to be evicted

Burstable (medium priority):

  • Requests < Limits
  • Can use extra resources when available
  • Evicted before Guaranteed

BestEffort (lowest priority):

  • No requests or limits
  • Uses whatever is available
  • First to be evicted

Without resource specifications, Pods get BestEffort QoS - the worst class.

CPU Resources

CPU is measured in cores or millicores.

CPU Units

Kubernetesyml
# 1 CPU core
cpu: "1"
cpu: "1000m"  # Same as 1 core
 
# Half CPU core
cpu: "0.5"
cpu: "500m"   # Same as 0.5 core
 
# Quarter CPU core
cpu: "0.25"
cpu: "250m"   # Same as 0.25 core
 
# 100 millicores (0.1 core)
cpu: "100m"

CPU Behavior

CPU is compressible:

  • Container can be throttled if exceeding limit
  • Container is not killed for exceeding CPU limit
  • Performance degrades but container continues running

Example:

Kubernetescpu-example.yml
apiVersion: v1
kind: Pod
metadata:
    name: cpu-demo
spec:
    containers:
        - name: app
          image: nginx:1.25
          resources:
              requests:
                  cpu: "250m"    # Guaranteed 0.25 core
              limits:
                  cpu: "500m"    # Max 0.5 core

Behavior:

  • Guaranteed 250 millicores
  • Can burst up to 500 millicores
  • If trying to use more than 500m, gets throttled
  • Never killed for CPU usage

CPU Throttling

When container exceeds CPU limit:

Kubernetesbash
# Check CPU throttling
kubectl top pod cpu-demo
 
# View detailed metrics
kubectl describe pod cpu-demo

Throttled containers show:

  • High CPU usage (near limit)
  • Increased response times
  • Degraded performance

Memory Resources

Memory is measured in bytes with standard units.

Memory Units

Kubernetesyml
# Bytes
memory: "134217728"   # 128 MiB in bytes
 
# Kibibytes (1024 bytes)
memory: "131072Ki"    # 128 MiB
 
# Mebibytes (1024 KiB)
memory: "128Mi"       # 128 MiB
 
# Gibibytes (1024 MiB)
memory: "1Gi"         # 1 GiB
 
# Decimal units (less common)
memory: "128M"        # 128 MB (1000-based)
memory: "1G"          # 1 GB (1000-based)

Note

Use binary units (Ki, Mi, Gi) for consistency with how operating systems report memory.

Memory Behavior

Memory is incompressible:

  • Container cannot be throttled for memory
  • If exceeding limit, container is killed (OOMKilled)
  • Pod may be restarted depending on restart policy

Example:

Kubernetesmemory-example.yml
apiVersion: v1
kind: Pod
metadata:
    name: memory-demo
spec:
    containers:
        - name: app
          image: nginx:1.25
          resources:
              requests:
                  memory: "256Mi"   # Guaranteed 256 MiB
              limits:
                  memory: "512Mi"   # Max 512 MiB

Behavior:

  • Guaranteed 256 MiB
  • Can use up to 512 MiB
  • If trying to use more than 512 MiB, gets OOMKilled
  • Pod restarted if restartPolicy allows

OOMKilled (Out of Memory Killed)

When container exceeds memory limit:

Kubernetesbash
# Check Pod status
kubectl get pod memory-demo
 
# Output shows OOMKilled
NAME          READY   STATUS      RESTARTS   AGE
memory-demo   0/1     OOMKilled   3          2m
 
# View events
kubectl describe pod memory-demo
 
# Events show:
# Reason: OOMKilled
# Message: Container exceeded memory limit

Resource Specification Examples

Basic Web Application

Kubernetesweb-app.yml
apiVersion: apps/v1
kind: Deployment
metadata:
    name: web-app
spec:
    replicas: 3
    selector:
        matchLabels:
            app: web
    template:
        metadata:
            labels:
                app: web
        spec:
            containers:
                - name: nginx
                  image: nginx:1.25
                  ports:
                      - containerPort: 80
                  resources:
                      requests:
                          memory: "128Mi"
                          cpu: "100m"
                      limits:
                          memory: "256Mi"
                          cpu: "200m"

Database Application

Kubernetesdatabase.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: postgres
spec:
    serviceName: postgres
    replicas: 1
    selector:
        matchLabels:
            app: postgres
    template:
        metadata:
            labels:
                app: postgres
        spec:
            containers:
                - name: postgres
                  image: postgres:15
                  resources:
                      requests:
                          memory: "512Mi"
                          cpu: "500m"
                      limits:
                          memory: "1Gi"
                          cpu: "1000m"
                  env:
                      - name: POSTGRES_PASSWORD
                        value: "secretpassword"

Background Worker

Kubernetesworker.yml
apiVersion: apps/v1
kind: Deployment
metadata:
    name: worker
spec:
    replicas: 5
    selector:
        matchLabels:
            app: worker
    template:
        metadata:
            labels:
                app: worker
        spec:
            containers:
                - name: worker
                  image: myworker:latest
                  resources:
                      requests:
                          memory: "256Mi"
                          cpu: "250m"
                      limits:
                          memory: "512Mi"
                          cpu: "500m"

Microservice with Sidecar

Kubernetesmicroservice.yml
apiVersion: apps/v1
kind: Deployment
metadata:
    name: api-service
spec:
    replicas: 3
    selector:
        matchLabels:
            app: api
    template:
        metadata:
            labels:
                app: api
        spec:
            containers:
                # Main application
                - name: api
                  image: myapi:latest
                  resources:
                      requests:
                          memory: "256Mi"
                          cpu: "250m"
                      limits:
                          memory: "512Mi"
                          cpu: "500m"
                # Sidecar (logging agent)
                - name: log-agent
                  image: fluent/fluentd:v1.16
                  resources:
                      requests:
                          memory: "64Mi"
                          cpu: "50m"
                      limits:
                          memory: "128Mi"
                          cpu: "100m"

Quality of Service (QoS) Classes

Kubernetes assigns QoS classes automatically based on resource specifications.

Guaranteed QoS

Requirements:

  • Every container has requests and limits
  • Requests = Limits for CPU and memory
Kubernetesguaranteed-qos.yml
apiVersion: v1
kind: Pod
metadata:
    name: guaranteed-pod
spec:
    containers:
        - name: app
          image: nginx:1.25
          resources:
              requests:
                  memory: "256Mi"
                  cpu: "250m"
              limits:
                  memory: "256Mi"  # Same as requests
                  cpu: "250m"      # Same as requests

Characteristics:

  • Highest priority
  • Best performance guarantee
  • Last to be evicted under pressure
  • Predictable resource allocation

Burstable QoS

Requirements:

  • At least one container has requests or limits
  • Requests < Limits (or only one specified)
Kubernetesburstable-qos.yml
apiVersion: v1
kind: Pod
metadata:
    name: burstable-pod
spec:
    containers:
        - name: app
          image: nginx:1.25
          resources:
              requests:
                  memory: "128Mi"
                  cpu: "100m"
              limits:
                  memory: "256Mi"  # Higher than requests
                  cpu: "200m"      # Higher than requests

Characteristics:

  • Medium priority
  • Can burst above requests when resources available
  • Evicted before Guaranteed, after BestEffort
  • Flexible resource usage

BestEffort QoS

Requirements:

  • No requests or limits specified
Kubernetesbesteffort-qos.yml
apiVersion: v1
kind: Pod
metadata:
    name: besteffort-pod
spec:
    containers:
        - name: app
          image: nginx:1.25
          # No resources specified

Characteristics:

  • Lowest priority
  • No resource guarantees
  • First to be evicted under pressure
  • Unpredictable performance

Warning

Warning: Avoid BestEffort QoS in production. Always specify at least requests for predictable behavior.

Checking QoS Class

Kubernetesbash
kubectl get pod guaranteed-pod -o jsonpath="{.status.qosClass}"
# Output: Guaranteed
 
kubectl get pod burstable-pod -o jsonpath="{.status.qosClass}"
# Output: Burstable
 
kubectl get pod besteffort-pod -o jsonpath="{.status.qosClass}"
# Output: BestEffort

Resource Quotas

Limit total resources in a namespace.

Creating Resource Quota

Kubernetesresource-quota.yml
apiVersion: v1
kind: ResourceQuota
metadata:
    name: compute-quota
    namespace: development
spec:
    hard:
        requests.cpu: "10"        # Total CPU requests
        requests.memory: "20Gi"   # Total memory requests
        limits.cpu: "20"          # Total CPU limits
        limits.memory: "40Gi"     # Total memory limits
        pods: "50"                # Max number of Pods

Apply quota:

Kubernetesbash
kubectl apply -f resource-quota.yml

Check quota usage:

Kubernetesbash
kubectl describe resourcequota compute-quota -n development

Output:

Kubernetesbash
Name:            compute-quota
Namespace:       development
Resource         Used   Hard
--------         ----   ----
limits.cpu       5      20
limits.memory    10Gi   40Gi
pods             15     50
requests.cpu     2.5    10
requests.memory  5Gi    20Gi

Quota Enforcement

When quota is exceeded:

Kubernetesbash
kubectl apply -f deployment.yml -n development
 
# Error: exceeded quota
Error from server (Forbidden): error when creating "deployment.yml": 
pods "app-xyz" is forbidden: exceeded quota: compute-quota, 
requested: requests.memory=1Gi, used: requests.memory=19.5Gi, 
limited: requests.memory=20Gi

Limit Ranges

Set default and min/max resources for containers.

Creating Limit Range

Kuberneteslimit-range.yml
apiVersion: v1
kind: LimitRange
metadata:
    name: resource-limits
    namespace: development
spec:
    limits:
        # Container limits
        - type: Container
          default:
              memory: "512Mi"   # Default limit
              cpu: "500m"
          defaultRequest:
              memory: "256Mi"   # Default request
              cpu: "250m"
          max:
              memory: "2Gi"     # Maximum limit
              cpu: "2000m"
          min:
              memory: "64Mi"    # Minimum request
              cpu: "50m"
        # Pod limits
        - type: Pod
          max:
              memory: "4Gi"
              cpu: "4000m"

Apply limit range:

Kubernetesbash
kubectl apply -f limit-range.yml

Limit Range Behavior

Without resources specified:

Kubernetesyml
# Pod definition without resources
spec:
    containers:
        - name: app
          image: nginx:1.25
          # No resources specified

Kubernetes applies defaults:

Kubernetesyml
# Automatically applied by LimitRange
spec:
    containers:
        - name: app
          image: nginx:1.25
          resources:
              requests:
                  memory: "256Mi"  # From defaultRequest
                  cpu: "250m"
              limits:
                  memory: "512Mi"  # From default
                  cpu: "500m"

Monitoring Resource Usage

Using kubectl top

View node resources:

Kubernetesbash
kubectl top nodes

Output:

Kubernetesbash
NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
node-1     850m         42%    3.2Gi           40%
node-2     1200m        60%    4.5Gi           56%

View Pod resources:

Kubernetesbash
kubectl top pods

Output:

Kubernetesbash
NAME                    CPU(cores)   MEMORY(bytes)
web-app-abc123-xyz      150m         180Mi
database-def456-uvw     450m         850Mi
worker-ghi789-rst       200m         320Mi

View Pod resources in namespace:

Kubernetesbash
kubectl top pods -n production

Using Metrics Server

Metrics Server must be installed:

Kubernetesbash
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Verify Metrics Server:

Kubernetesbash
kubectl get deployment metrics-server -n kube-system

Viewing Resource Allocation

Check node allocatable resources:

Kubernetesbash
kubectl describe node node-1

Output shows:

Kubernetesbash
Capacity:
  cpu:                4
  memory:             8Gi
Allocatable:
  cpu:                3800m
  memory:             7.5Gi
Allocated resources:
  Resource           Requests      Limits
  --------           --------      ------
  cpu                2500m (65%)   5000m (131%)
  memory             4Gi (53%)     8Gi (106%)

Common Mistakes and Pitfalls

Mistake 1: No Resource Specifications

Problem: Pods without resources get BestEffort QoS.

Solution: Always specify at least requests:

Kubernetesyml
resources:
    requests:
        memory: "256Mi"
        cpu: "250m"

Mistake 2: Limits Too Low

Problem: Containers frequently OOMKilled or throttled.

Solution: Monitor actual usage and adjust:

Kubernetesbash
# Monitor usage
kubectl top pod myapp
 
# Adjust limits based on actual usage
resources:
    limits:
        memory: "512Mi"  # Increased from 256Mi

Mistake 3: Requests Too High

Problem: Pods can't be scheduled due to over-requesting.

Solution: Set requests based on actual minimum needs:

Kubernetesyml
resources:
    requests:
        memory: "128Mi"  # Reduced from 512Mi
        cpu: "100m"      # Reduced from 500m

Mistake 4: Requests = Limits Always

Problem: Wastes resources, prevents bursting.

Solution: Allow bursting for variable workloads:

Kubernetesyml
resources:
    requests:
        memory: "256Mi"  # Baseline
        cpu: "250m"
    limits:
        memory: "512Mi"  # Can burst 2x
        cpu: "500m"

Mistake 5: Ignoring Multi-Container Pods

Problem: Forgetting to set resources for all containers.

Solution: Specify resources for every container:

Kubernetesyml
containers:
    - name: app
      resources:
          requests:
              memory: "256Mi"
              cpu: "250m"
    - name: sidecar
      resources:
          requests:
              memory: "64Mi"   # Don't forget sidecar
              cpu: "50m"

Best Practices

Start with Monitoring

Before setting resources:

  1. Deploy without limits initially (with requests)
  2. Monitor actual usage for 1-2 weeks
  3. Set limits based on observed peak usage + buffer
  4. Continue monitoring and adjust

Use Appropriate Ratios

CPU:

  • Requests: baseline usage
  • Limits: 2-3x requests for burstable workloads
  • Limits: 1x requests for consistent workloads

Memory:

  • Requests: minimum needed
  • Limits: 1.5-2x requests (smaller buffer than CPU)
  • Memory leaks can cause OOMKills

Set Namespace Defaults

Use LimitRange for consistent defaults:

Kubernetesyml
apiVersion: v1
kind: LimitRange
metadata:
    name: defaults
spec:
    limits:
        - type: Container
          defaultRequest:
              memory: "128Mi"
              cpu: "100m"
          default:
              memory: "256Mi"
              cpu: "200m"

Use Resource Quotas

Prevent namespace resource exhaustion:

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

Different Resources for Different Workloads

Web servers:

Kubernetesyml
resources:
    requests:
        memory: "128Mi"
        cpu: "100m"
    limits:
        memory: "256Mi"
        cpu: "200m"

Databases:

Kubernetesyml
resources:
    requests:
        memory: "1Gi"
        cpu: "500m"
    limits:
        memory: "2Gi"
        cpu: "1000m"

Batch jobs:

Kubernetesyml
resources:
    requests:
        memory: "512Mi"
        cpu: "500m"
    limits:
        memory: "1Gi"
        cpu: "2000m"

Document Resource Decisions

Add annotations explaining resource choices:

Kubernetesyml
metadata:
    annotations:
        resources.note: "Based on 2-week monitoring, peak usage 180Mi/150m"
spec:
    containers:
        - name: app
          resources:
              requests:
                  memory: "256Mi"
                  cpu: "200m"
              limits:
                  memory: "512Mi"
                  cpu: "400m"

Troubleshooting Resource Issues

Pod Pending Due to Insufficient Resources

Kubernetesbash
kubectl describe pod myapp
 
# Events show:
# Warning  FailedScheduling  pod has unbound immediate PersistentVolumeClaims
# 0/3 nodes are available: 3 Insufficient cpu.

Solutions:

  1. Reduce resource requests
  2. Add more nodes
  3. Delete unused Pods
  4. Scale down other workloads

Pod OOMKilled

Kubernetesbash
kubectl get pod myapp
# STATUS: OOMKilled
 
kubectl describe pod myapp
# Reason: OOMKilled
# Exit Code: 137

Solutions:

  1. Increase memory limits
  2. Fix memory leaks in application
  3. Optimize application memory usage
  4. Use memory profiling tools

CPU Throttling

Kubernetesbash
kubectl top pod myapp
# CPU usage at or near limit
 
# Check throttling metrics
kubectl describe pod myapp

Solutions:

  1. Increase CPU limits
  2. Optimize application CPU usage
  3. Scale horizontally (more replicas)
  4. Profile application for CPU hotspots

Node Resource Pressure

Kubernetesbash
kubectl describe node node-1
# Conditions:
#   MemoryPressure   True
#   DiskPressure     False

Solutions:

  1. Evict BestEffort Pods
  2. Add more nodes
  3. Reduce resource requests
  4. Implement resource quotas

Conclusion

In episode 29, we've explored Computational Resources in Kubernetes in depth. We've learned why resource requests and limits are critical, how they affect scheduling and runtime behavior, and best practices for resource management.

Key takeaways:

  • Always specify resources for predictable behavior
  • Requests guarantee minimum resources for scheduling
  • Limits prevent resource exhaustion and protect nodes
  • CPU is compressible - throttled when exceeded
  • Memory is incompressible - OOMKilled when exceeded
  • QoS classes determine eviction priority
  • Guaranteed QoS requires requests = limits
  • Burstable QoS allows flexible resource usage
  • BestEffort QoS should be avoided in production
  • Resource Quotas limit namespace resource usage
  • Limit Ranges set defaults and boundaries
  • Monitor actual usage before setting limits
  • Different workloads need different resource profiles
  • Requests affect scheduling, limits affect runtime
  • Use kubectl top to monitor resource usage

Proper resource management is fundamental to running stable, efficient Kubernetes clusters. By understanding and implementing resource requests and limits, you ensure predictable application behavior, efficient resource utilization, and cluster stability.

Are you getting a clearer understanding of Computational Resources in Kubernetes? Keep your learning momentum going and look forward to the next episode!

Note

If you want to continue to the next episode, you can click the Episode 30 thumbnail below

Episode 30Episode 30

Related Posts