Technical Theory

Monitoring Cluster and Application Resource Usage

Introduction

This tutorial guides you through monitoring resource usage in a Kubernetes cluster. You’ll learn how to use kubectl and the Metrics Server to monitor CPU and memory consumption at both the cluster and pod level. This is crucial for understanding application performance, identifying bottlenecks, and optimizing resource allocation.

Prerequisites:

  • A running Kubernetes cluster (e.g., Minikube, Kind, or a cloud-based cluster).
  • kubectl configured to interact with your cluster.
  • Basic understanding of Kubernetes concepts like Pods, Namespaces, and Deployments.

Task 1: Deploying the Metrics Server

The Metrics Server is a cluster-wide aggregator of resource usage data. It collects metrics from Kubelets and exposes them through the Kubernetes API.

  1. Download the Metrics Server manifest:

    NODE_TYPE // bash
    wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
  2. Apply the manifest:

    NODE_TYPE // bash
    kubectl apply -f components.yaml
    If you encounter RBAC errors, you might need to add --validate=false to the kubectl apply command to bypass validation.
  3. Verify the Metrics Server deployment:

    NODE_TYPE // bash
    kubectl get deployments -n kube-system metrics-server

    Expected Output:

    NODE_TYPE // output
    NAME            READY   UP-TO-DATE   AVAILABLE   AGE
    metrics-server   1/1     1            1           1m
  4. Check the Metrics Server logs (optional):

    If the deployment isn’t ready, check the logs for errors:

    NODE_TYPE // bash
    kubectl logs -n kube-system -l k8s-app=metrics-server

    Common issues include TLS certificate validation errors. A workaround is to add the --kubelet-insecure-tls flag to the Metrics Server deployment. This should ONLY be done in development/testing environments as it disables certificate validation.

    Edit the deployment:

    NODE_TYPE // bash
    kubectl edit deployment metrics-server -n kube-system

    Add --kubelet-insecure-tls to the args section of the metrics-server container spec:

    NODE_TYPE // yaml
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-insecure-tls # ADD THIS LINE
        image: k8s.gcr.io/metrics-server/metrics-server:v0.6.1
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /livez
            port: https
            scheme: HTTPS
          periodSeconds: 10
        name: metrics-server
        ports:
        - containerPort: 4443
          name: https
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /readyz
            port: https
            scheme: HTTPS
          periodSeconds: 10
        resources:
          limits:
            cpu: 100m
            memory: 300Mi
          requests:
            cpu: 100m
            memory: 200Mi
        securityContext:
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1001
        volumeMounts:
        - mountPath: /tmp
          name: tmp-dir

    Save the changes. Kubernetes will automatically redeploy the Metrics Server.

Task 2: Monitoring Node Resource Usage

Now that Metrics Server is running, you can use kubectl top node to view resource usage for each node in your cluster.

  1. Get node resource usage:

    NODE_TYPE // bash
    kubectl top node

    Expected Output (will vary based on your cluster):

    NODE_TYPE // output
    NAME      CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
    minikube   185m         9%     874Mi           44%

    This command displays the CPU and memory usage for each node. The CPU(cores) column shows the CPU usage in millicores, and CPU% shows the percentage of total CPU capacity being used. Similarly, MEMORY(bytes) shows the memory usage in bytes, and MEMORY% shows the percentage of total memory capacity.

Task 3: Monitoring Pod Resource Usage

You can also monitor resource usage for individual pods using kubectl top pod.

  1. Get pod resource usage:

    NODE_TYPE // bash
    kubectl top pod

    Expected Output (will vary based on your deployed pods):

    NODE_TYPE // output
    NAME                                        CPU(cores)   MEMORY(bytes)
    coredns-66c46b6d5c-mzlzj                    4m           22Mi
    etcd-minikube                               24m          114Mi
    kube-apiserver-minikube                     52m          349Mi
    kube-controller-manager-minikube            15m          70Mi
    kube-proxy-7z59z                            2m           20Mi
    kube-scheduler-minikube                     5m           24Mi
    metrics-server-7cd5fcb8b7-k2s2m             3m           14Mi
    storage-provisioner                         2m           13Mi

    This command displays the CPU and memory usage for each pod in the current namespace. If you want to monitor pods in a specific namespace, use the -n flag:

    NODE_TYPE // bash
    kubectl top pod -n <namespace>

    For example:

    NODE_TYPE // bash
    kubectl top pod -n kube-system

Task 4: Deploying a Sample Application

To further demonstrate pod monitoring, let’s deploy a simple application.

  1. Create a deployment: Create a file named nginx-deployment.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
            resources:
              requests:
                cpu: 100m
                memory: 128Mi
              limits:
                cpu: 200m
                memory: 256Mi
  2. Apply the deployment:

    NODE_TYPE // bash
    kubectl apply -f nginx-deployment.yaml
  3. Verify the deployment:

    NODE_TYPE // bash
    kubectl get deployments

    Expected Output:

    NODE_TYPE // output
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   2/2     2            2           1m
  4. Monitor the Nginx pods:

    NODE_TYPE // bash
    kubectl top pod

    You should now see the CPU and memory usage for the Nginx pods.

Task 5: Understanding Resource Requests and Limits

In the nginx-deployment.yaml file, you defined resources with requests and limits. Let’s clarify these concepts:

  • Requests: The minimum amount of resources (CPU and memory) that the pod is guaranteed to receive. The Kubernetes scheduler uses these values to decide which node has enough capacity to run the pod.

  • Limits: The maximum amount of resources that the pod is allowed to use. If a pod attempts to exceed its memory limit, it may be terminated by the OOM (Out Of Memory) killer. If a pod exceeds its CPU limit, it will be throttled.

It’s crucial to set appropriate resource requests and limits to ensure application stability and efficient resource utilization. Setting requests too low can lead to performance issues, while setting limits too high can waste resources.

Task 6: Using kubectl describe to view resource information.

The kubectl describe command will give you details on a pod and include its resource requests and limits.

  1. Describe the Nginx pod

    First you need to get the name of one of the Nginx pods.

    NODE_TYPE // bash
    kubectl get pods

    Example output:

    NODE_TYPE // output
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-6d6b4c6b98-gq69g   1/1     Running   0          2m
    nginx-deployment-6d6b4c6b98-v587x   1/1     Running   0          2m

    Now run the describe command on one of the Nginx pods.

    NODE_TYPE // bash
    kubectl describe pod nginx-deployment-6d6b4c6b98-gq69g

    The output contains a lot of information, but search for the “Resources:” section. It will show you the requests and limits for the containers in the pod.

    NODE_TYPE // output
    Containers:
      nginx:
        Container ID:   docker://b81549698b6593c9e29938094b2239ac80b799ee5153060842f128474118e460
        Image:          nginx:latest
        Image ID:       docker-pullable://nginx@sha256:8efd9af229fca91c495c8c009006c6263e834b57395606dca59b3b383ad41868
        Port:           80/TCP
        Host Port:      0/TCP
        State:          Running
          Started:      Thu, 09 Apr 2026 14:00:02 UTC
        Ready:          True
        Restart Count:  0
        Limits:
          cpu:     200m
          memory:  256Mi
        Requests:
          cpu:        100m
          memory:     128Mi
        Readiness:    http-get http://:80/ delay=0s timeout=1s period=10s #success=1 #failure=3
        Environment:  <none>
        Mounts:
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-rnlvz (ro)

Conclusion

You have successfully learned how to monitor cluster and application resource usage in Kubernetes using kubectl and the Metrics Server. You deployed the Metrics Server, monitored node and pod resource usage, and gained an understanding of resource requests and limits. This knowledge is crucial for managing and optimizing your Kubernetes deployments. Remember to adjust resource requests and limits based on your application’s needs to ensure stability and efficient resource utilization.

Next Topic