Inspect Kubernetes Resources with kubectl

KubernetesBeginner
Practice Now

Introduction

In Kubernetes for Beginners, you used kubectl get, describe, and labels to check whether an application was working. Real clusters contain many objects, so the next skill is asking narrower questions and extracting only the evidence you need.

In this lab, you will deploy a small storefront workload and inspect it from several angles: namespace, labels, structured fields, controller ownership, and events. You will finish with a compact command that connects a Deployment to the Pods it manages.

You are not expected to memorize every output format. Instead, practice translating a question into a narrow query: “Which objects exist?”, “Which Pods match this Service?”, “Who owns this Pod?”, or “What happened most recently?” Each step adds one tool to that inspection workflow.

Create a Focused Workspace

In this step, you will create a namespace and deploy the prepared resources into it. A namespace narrows the scope of later queries and prevents similarly named objects from being confused.

Move into the lab workspace, then inspect the current context before changing cluster state. cd changes the shell's working directory, so later relative filenames such as storefront.yaml refer to the prepared workspace.

cd /home/labex/project/resource-inspection
kubectl config current-context

The output should be labex-v135. Create the namespace and apply the prepared multi-document manifest:

kubectl create namespace practice-observe
kubectl apply -f storefront.yaml
kubectl rollout status deployment/storefront -n practice-observe --timeout=60s

The final lines should look like this. Intermediate rollout messages may appear while the two Pods start:

namespace/practice-observe created
deployment.apps/storefront created
service/storefront created
deployment "storefront" successfully rolled out

-n practice-observe limits the command to that namespace. The rollout command waits until both Deployment replicas are available instead of relying on an arbitrary sleep.

You will use -n practice-observe repeatedly. Kubernetes commands otherwise use the namespace configured in your current context—usually default—and could make a healthy object appear “missing” simply because you queried the wrong scope.

Discover Resources and Their Scope

In this step, you will use API discovery before querying objects. kubectl api-resources lists the resource names the current API server understands, along with short names and whether each resource is namespaced. The pipe to head limits the first broad listing to its first ten lines so the terminal is not flooded.

kubectl api-resources | head
kubectl api-resources --namespaced=true | grep -E 'pods|deployments|services'

The pipe passes the resource list to grep. -E enables the | alternation, so one command can match several resource names. Notice that Pods, Deployments, and Services are namespaced.

List the application objects using their full names, then repeat the query with common short names:

kubectl get pods,deployments,services -n practice-observe
kubectl get po,deploy,svc -n practice-observe

The two commands address the same API resources. Short names save typing, but the full names are clearer in scripts and documentation.

Look for one Deployment, two generated Pods, and one Service. The comma-separated resource list is only a query convenience; these objects still have different roles and API schemas.

The type prefixes make the combined output easier to interpret. Generated suffixes, addresses, and ages will differ:

NAME                              READY   STATUS    RESTARTS   AGE
pod/storefront-...                1/1     Running   0          ...
pod/storefront-...                1/1     Running   0          ...

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/storefront   2/2     2            2           ...

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/storefront   ClusterIP   ...          <none>        80/TCP    ...

Filter with Labels and Selectors

In this step, you will use labels as queryable metadata. --show-labels displays every label, while -l selects only objects whose labels match an expression.

kubectl get pods -n practice-observe --show-labels
kubectl get pods -n practice-observe -l app=storefront
kubectl get pods -n practice-observe -l 'app=storefront,tier=web'

A comma between selector requirements means logical AND. The last query therefore returns Pods carrying both labels.

Compare the Service selector with the selected Pod labels:

kubectl get service storefront -n practice-observe -o jsonpath='Service selector: {.spec.selector.app}{"\n"}'
kubectl get pods -n practice-observe -l app=storefront -o custom-columns='NAME:.metadata.name,TIER:.metadata.labels.tier,IP:.status.podIP,READY:.status.containerStatuses[0].ready'
kubectl get endpointslices -n practice-observe -l kubernetes.io/service-name=storefront

JSONPath extracts one field from the Service. -o custom-columns produces a compact table from several Pod fields. This is more dependable than visually searching a large YAML document.

The final command asks for EndpointSlices whose automatically managed kubernetes.io/service-name label belongs to storefront. An EndpointSlice records the network endpoints selected for a Service. A non-empty ENDPOINTS column proves the matching labels produced routable backends.

The Service selector should print storefront, and both rows in the Pod table should carry tier=web. That matching app label is the connection: a Service discovers Pods through labels, not through Deployment ownership.

Service selector: storefront
NAME             TIER   IP          READY
storefront-...   web    10.244...   true
storefront-...   web    10.244...   true

NAME             ADDRESSTYPE   PORTS   ENDPOINTS                  AGE
storefront-...   IPv4          80      10.244...,10.244...          ...

Trace Controller Ownership

In this step, you will follow Kubernetes ownership from the Deployment to its ReplicaSet and Pods. Controllers attach ownerReferences so Kubernetes knows which higher-level object manages each dependent object.

kubectl get replicasets -n practice-observe -l app=storefront
kubectl get replicasets -n practice-observe -l app=storefront -o custom-columns='REPLICASET:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name'
kubectl get pods -n practice-observe -l app=storefront -o custom-columns='POD:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name'

The ReplicaSet owner should be the storefront Deployment. Each Pod owner is the generated ReplicaSet, not the Deployment directly:

Deployment/storefront -> ReplicaSet/storefront-... -> Pod/storefront-...

Use kubectl describe when you need this relationship together with conditions and recent events:

kubectl describe deployment storefront -n practice-observe

Do not read describe from top to bottom every time. For this question, focus on Selector, replica counts, conditions, and events. The best inspection command is the one that answers the current question with the least noise.

Read Events as a Timeline

In this step, you will inspect events as time-ordered evidence. Events describe scheduling, image, container, and controller actions, but they are temporary diagnostics rather than permanent audit records.

kubectl get events -n practice-observe --sort-by=.metadata.creationTimestamp

--sort-by orders objects using a field path. Focus the timeline on the Deployment and then on one selected Pod:

kubectl events -n practice-observe --for deployment/storefront
POD_NAME=$(kubectl get pods -n practice-observe -l app=storefront -o jsonpath='{.items[0].metadata.name}')
echo "$POD_NAME"
kubectl events -n practice-observe --for "pod/${POD_NAME}"

$(...) is command substitution: the shell runs the inner command and stores its output in POD_NAME. You now have a repeatable inspection path: narrow the namespace, select by labels, extract fields, follow owners, and finish with the relevant event timeline.

Pod names generated by controllers change across replacements, so selecting the current name by label is safer than copying an old value. This pattern will reappear throughout the troubleshooting labs.

The observed successful lifecycle normally includes Scheduled, Pulled, Created, and Started. A recently started local cluster can also retain an earlier warning such as FailedCreatePodSandBox while Calico becomes ready. Do not conclude that the application is still broken from an old warning alone: compare its timestamp with the later normal events, then confirm the Pod's current READY state. Timestamps and generated Pod names are intentionally omitted because they change on every run.

Summary

You inspected a Kubernetes application without scanning unrelated objects. You combined namespaces, labels, JSONPath, custom columns, owner references, and events to move from a broad inventory to precise operational evidence. The next lab applies this inspection workflow to failing Pods and containers.