Polyaxon v3 is coming →

How to use kubectl describe

Use kubectl describe to inspect Kubernetes resources, events, labels, pod state, and failure details without guessing from status alone.

April 2, 2024by Polyaxon
Apr 2, 2024

How to use kubectl describe

Use kubectl describe to inspect Kubernetes resources, events, labels, pod state, and failure details without guessing from status alone.

Picture

kubectl get tells you what Kubernetes thinks the current state is. kubectl describe tells you why the state may be stuck. Events, scheduling failures, image pull errors, mounts, labels, and resource details are all in one place.

When an ML job is pending or a service fails to start, describe is often faster than staring at a dashboard. It gives you the cluster's own explanation before you start inventing one.

What is kubectl describe?

As is evident by its name, the kubectl describe command is used to view details of a Kubernetes resource or resource group. Here's how it's used:

kubectl describe <resource_type> <resource_name>

Common resource types include pods, services, nodes, events, and more. The command takes the prefix of the resource name as input. You can also provide the complete name of the resource and it would work just fine.

Here's how you can use this command:

kubectl describe deployment test-webapp

Here's what the output would look like:


Name:                   test-webapp
Namespace:              default
CreationTimestamp:      Tue, 31 May 2022 10:38:00 +0530
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=test-webapp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  run=test-webapp
  Containers:
   test-webapp:
    Image:      k8s.gcr.io/hpa-example
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     25m
      memory:  10Mi
    Requests:
      cpu:        10m
      memory:     5Mi
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   test-webapp-d5f9b9d8d (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  6d8h  deployment-controller  Scaled up replica set test-webapp-d5f9b9d8d to 1

Exploring kubectl describe in detail

The kubectl describe command provides a few options to tailor it to your liking. Here are some of those:

Viewing by label

You can also use metadata labels to filter and identify resources when describing them. Here's what the syntax looks like for that:

Viewing resources using a file

The kubectl describe command allows you to query for resources using data stored in a file. Here's what the syntax looks like:

kubectl describe -f test-pod.json

Shorthand syntax

You can use the following syntax too:

kubectl describe <resource-type>/<resource-name>

It works the same as the syntax shared at the beginning of the article.

Kubectl describe vs. kubectl get

People often debate on whether or not describe was needed when there already was a get command. To understand this better, let's try to view them side by side:

Here's what a get command on a pod returns:

kubectl get pods

NAME                           READY   STATUS    RESTARTS   AGE
test-webapp-5f7b7b7b7b-5q7q7   1/1     Running   0          1d

Here's what a describe call on the same pod returns:


$ kubectl describe pod test-webapp-d5f9b9d8d-flqjk
Name:         test-webapp-d5f9b9d8d-flqjk
Namespace:    default
Priority:     0
Node:         minikube/192.168.49.2
Start Time:   Mon, 06 Jun 2022 18:00:04 +0530
Labels:       pod-template-hash=d5f9b9d8d
              run=test-webapp
Annotations:  <none>
Status:       Running
IP:           10.244.0.16
IPs:
  IP:           10.244.0.16
Controlled By:  ReplicaSet/test-webapp-d5f9b9d8d
Containers:
  test-webapp:
    Container ID:   docker://d581e3e779ae164630de23594b0c4df8c1eecacdbd6b0b7e68655656d37c7491
    Image:          k8s.gcr.io/hpa-example
    Image ID:       docker-pullable://k8s.gcr.io/hpa-example@sha256:581697a37f0e136db86d6b30392f0db40ce99c8248a7044c770012f4e8491544
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    137
      Started:      Mon, 06 Jun 2022 18:38:02 +0530
      Finished:     Mon, 06 Jun 2022 18:39:07 +0530
    Ready:          False
    Restart Count:  8
    Limits:
      cpu:     25m
      memory:  10Mi
    Requests:
      cpu:        10m
      memory:     5Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-nnjsx (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  kube-api-access-nnjsx:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                   From               Message
  ----     ------     ----                  ----               -------
  Normal   Scheduled  42m                   default-scheduler  Successfully assigned default/test-webapp-d5f9b9d8d-flqjk to minikube
  Normal   Pulled     41m                   kubelet            Successfully pulled image "k8s.gcr.io/hpa-example" in 1m0.625635569s
  Normal   Pulled     30m                   kubelet            Successfully pulled image "k8s.gcr.io/hpa-example" in 1.177994667s
  Normal   Pulled     29m                   kubelet            Successfully pulled image "k8s.gcr.io/hpa-example" in 1.104291834s
  Normal   Pulled     28m                   kubelet            Successfully pulled image "k8s.gcr.io/hpa-example" in 1.010987459s
  Normal   Pulling    26m (x5 over 42m)     kubelet            Pulling image "k8s.gcr.io/hpa-example"
  Normal   Created    26m (x5 over 41m)     kubelet            Created container test-webapp
  Normal   Pulled     26m                   kubelet            Successfully pulled image "k8s.gcr.io/hpa-example" in 1.132110626s
  Normal   Started    26m (x5 over 41m)     kubelet            Started container test-webapp
  Warning  BackOff    2m21s (x80 over 29m)  kubelet            Back-off restarting failed container

As you can see, the get command (by default) returns a very quick summary of the status of the resource in question. On the other hand, the describe command prepares a detailed summary of the resource with additional details like container ID, limits, node details, etc.

Also, the get command returns the information in a tabular fashion by default. You also have the option to view the information in any other format such as JSON or YAML by passing it as an option. But, the output returned in such a case is quite verbose and is usually difficult to read.

At the end of the day, describe provides you with just the right amount of information about your Kubernetes resource. If you are looking for anything less or more, you should look towards get.

Final thoughts

kubectl describe is one of the best first commands when Kubernetes state looks wrong. It exposes scheduler events, image errors, mounts, labels, and resource details without requiring a full observability stack.

Polyaxon surfaces much of this context in the run and service views, but knowing describe keeps you dangerous in the right way when you need to debug below the platform layer.