Exploring Your Kubernetes Cluster with kubectl

Lesson 2 of 39 11 min read Updated

In the previous lesson you set up kubeconfig, so kubectl knows which cluster to talk to and who you are. Now it is time to use that connection and look around.

Think of kubectl as the remote control for your cluster. Every button you press becomes a request to the Kubernetes API server, and the API server sends back the answer.

In this lesson you will check that your cluster is reachable and healthy, find out which machines it is made of, and read the details of a single node. By the end you will feel at home in a live cluster from the command line.

What you will learn

  1. What kubectl is and how it talks to your cluster
  2. Which control plane components keep a cluster running, and how control-plane nodes differ from worker nodes
  3. How to check versions, and why kubectl must stay close to the cluster version
  4. How to list and inspect nodes, including the requests and limits of the Pods running on them

What is kubectl?

kubectl (say "kube-control" or "kube-c-t-l") is the official command-line tool for the Kubernetes API. With it you can:

  1. Create, update and delete Kubernetes objects such as Pod, Deployment and Service
  2. Inspect the current state of the cluster
  3. View logs and debug running workloads

kubectl never changes the machines directly. It sends HTTPS requests to the API server, and the cluster does the rest.

kubectl --- HTTPS request ---> API server ---> etcd, scheduler, controllers
kubectl <-- JSON response ---- API server

Almost everything you do with Kubernetes day to day goes through kubectl.

What a cluster is made of

The control plane

The control plane is the brain of the cluster. Every request enters through the API server. Behind it, three components do the work you will check in this lesson.

ComponentResponsibility
kube-apiserverThe front door. kubectl and every other component talk to it
schedulerDecides which node each new Pod should run on
controller-managerRuns controllers that keep the cluster in the desired state, for example making sure a Deployment has the right number of healthy replicas
etcdThe cluster's database. It stores every API object and the cluster state

Control-plane nodes and worker nodes

A cluster has two kinds of nodes:

  1. Control-plane nodes run the API server, scheduler, controller-manager and etcd. They manage the cluster itself. Kubernetes keeps your workloads off them to protect cluster stability.
  2. Worker nodes are the machines where your application containers actually run.

Think of control-plane nodes as the management office and worker nodes as the factory floor.

Managed clusters hide the office. On EKS, GKE or AKS the cloud provider runs the control plane for you, so kubectl get nodes shows only worker nodes. On local clusters such as kind or minikube, a single node often plays both roles.

Requests and limits

Each container in a Pod can declare two resource values for CPU and memory. You will see them for every Pod when you describe a node later in this lesson.

SettingMeaning
RequestThe minimum the container is guaranteed. The scheduler only places a Pod on a node that still has this much unreserved capacity.
LimitThe maximum the container is allowed to use. Above the CPU limit it is slowed down (throttled); above the memory limit it is killed (OOMKilled).

If a limit is higher than its request, the extra resources are best effort. They are available when the node has spare capacity, but never guaranteed.

This lets Kubernetes pack workloads tightly onto nodes without reserving the worst case for everyone.

Versions and the version skew policy

Kubernetes uses semantic versioning: MAJOR.MINOR.PATCH, for example 1.31.4. A new minor version comes out about three times a year.

Your local kubectl and the cluster's API server do not have to match exactly. kubectl is supported within one minor version of the API server, older or newer. With a v1.31 API server, kubectl v1.30, v1.31 and v1.32 are all supported.

Stay inside the skew window. Outside it many commands still work, but you can hit missing fields, confusing errors or broken features. Upgrade kubectl whenever you upgrade the cluster.

Commands for exploring a cluster

Check the kubectl and cluster versions

kubectl version prints the version of your local client and of the remote API server:

kubectl version

Expected output (your numbers will differ):

Client Version: v1.32.1
Kustomize Version: v5.5.0
Server Version: v1.31.4

The Kustomize Version line is the Kustomize engine built into kubectl. If the Server Version line is missing and an error appears instead, kubectl could not reach the cluster.

To check only your local tool, without contacting any cluster, add --client:

kubectl version --client

Check the control plane components

kubectl get componentstatuses (short name cs) gives a quick health report of the scheduler, the controller-manager and etcd:

kubectl get componentstatuses

Expected output:

Warning: v1 ComponentStatus is deprecated in v1.19+
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-0 Healthy ok

If every component shows Healthy, the control plane is in good shape.

This command is deprecated. ComponentStatus has been deprecated since Kubernetes 1.19, which is why it prints a warning. It still works on most self-managed clusters, but managed clusters may report components as Unhealthy even when all is well, because the API server cannot reach them directly.

The modern check asks the API server for its own readiness report:

kubectl get --raw='/readyz?verbose'

Expected output (shortened):

[+]ping ok
[+]log ok
[+]etcd ok
[+]etcd-readiness ok
[+]informer-sync ok
...
readyz check passed

Every line should end in ok, and the last line should say readyz check passed. The sibling endpoint /livez?verbose reports whether the API server is alive.

List the nodes

kubectl get nodes lists every node with its status, role, age and kubelet version:

kubectl get nodes

Expected output:

NAME STATUS ROLES AGE VERSION
kube0 Ready control-plane 45d v1.31.4
kube1 Ready <none> 45d v1.31.4
kube2 Ready <none> 45d v1.31.4
kube3 Ready <none> 45d v1.31.4

A node whose ROLES column says control-plane is a control-plane node. <none> means a worker node. Every node should be Ready.

Clusters older than Kubernetes 1.24 also showed a master role here. That label has been removed, so modern clusters show only control-plane.

Add more columns with -o wide

-o wide adds the node IP addresses, the operating system, the kernel and the container runtime:

kubectl get nodes -o wide

Expected output:

NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kube0 Ready control-plane 45d v1.31.4 192.168.1.10 <none> Ubuntu 24.04.1 LTS 6.8.0-45-generic containerd://1.7.22
kube1 Ready <none> 45d v1.31.4 192.168.1.11 <none> Ubuntu 24.04.1 LTS 6.8.0-45-generic containerd://1.7.22

Describe a single node

kubectl describe gives a deep, human-readable view of one object. For a node it shows labels, health conditions, hardware capacity, the Pods running on it and how much of the node they have reserved.

kubectl describe nodes <node-name>

kubectl accepts node, nodes or the short name no. For example:

kubectl describe nodes kube1

Expected output (shortened):

Name: kube1
Roles: <none>
Labels: kubernetes.io/arch=amd64
kubernetes.io/hostname=kube1
kubernetes.io/os=linux
Conditions:
Type Status Reason Message
---- ------ ------ -------
MemoryPressure False KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False KubeletHasSufficientPID kubelet has sufficient PID available
Ready True KubeletReady kubelet is posting ready status
Capacity:
cpu: 4
memory: 8137168Ki
pods: 110
Allocatable:
cpu: 4
memory: 8034768Ki
pods: 110
System Info:
Kernel Version: 6.8.0-45-generic
OS Image: Ubuntu 24.04.1 LTS
Container Runtime Version: containerd://1.7.22
Kubelet Version: v1.31.4
Non-terminated Pods: (3 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
kube-flannel kube-flannel-ds-x7k2p 100m (2%) 0 (0%) 50Mi (0%) 0 (0%) 45d
kube-system coredns-7c65d6cfc9-4xkqz 100m (2%) 0 (0%) 70Mi (0%) 170Mi (2%) 45d
kube-system kube-proxy-9fz4q 0 (0%) 0 (0%) 0 (0%) 0 (0%) 45d
Allocated resources:
Resource Requests Limits
-------- -------- ------
cpu 200m (5%) 0 (0%)
memory 120Mi (1%) 170Mi (2%)

The output is divided into sections. These are the ones you will read most often:

SectionWhat it tells you
LabelsMetadata tags on the node, such as the OS, CPU architecture and hostname
ConditionsHealth checks: memory pressure, disk pressure, PID pressure and readiness
Capacity / AllocatableTotal CPU, memory and Pod slots, and how much of that is left for Pods after system reservations
System InfoKernel version, OS image, container runtime and kubelet version
Non-terminated PodsPods running on this node, with their CPU and memory requests and limits
Allocated resourcesThe sum of all requests and limits on the node, as a percentage of what is allocatable

Limits in Allocated resources can add up to more than 100%. That is normal: it means the node is overcommitted, relying on Pods not all hitting their limits at the same time.

Hands-on: run a full cluster health check

Now combine the commands into a routine you can run on any cluster. Work through the steps in order; each one builds on the last.

Step 1 — Confirm kubectl can reach the cluster

kubectl version

You should see both Client Version and Server Version. If only the client version appears, followed by an error such as The connection to the server ... was refused, kubectl cannot reach the cluster.

In that case run kubectl config current-context and make sure you are on the right context, as shown in the kubeconfig lesson.

Step 2 — Check the control plane

kubectl get componentstatuses
kubectl get --raw='/readyz?verbose'

All three components (scheduler, controller-manager and etcd-0) should report Healthy, and the readiness report should end with readyz check passed.

Step 3 — List the nodes and confirm they are Ready

kubectl get nodes

Every node should show Ready. A node that shows NotReady needs investigation, and the next step is where you start.

Step 4 — Inspect a worker node

kubectl describe nodes kube1

Look at the Conditions section. MemoryPressure, DiskPressure and PIDPressure should be False (no problems), and Ready should be True.

Step 5 — Check what is running on the node

In the same output, scroll to Non-terminated Pods. You will see system Pods such as coredns (cluster DNS), kube-proxy and a network plugin such as kube-flannel.

These are the cluster's own infrastructure components. They run in system namespaces such as kube-system. Then read Allocated resources to see how much of the node's CPU and memory is already reserved by requests.

Step 6 — List the system Pods

The -n flag picks a namespace; the next lesson covers namespaces properly. List everything in kube-system:

kubectl get pods -n kube-system

Expected output on a kubeadm cluster (shortened):

NAME READY STATUS RESTARTS AGE
coredns-7c65d6cfc9-4xkqz 1/1 Running 0 45d
coredns-7c65d6cfc9-r8m2d 1/1 Running 0 45d
etcd-kube0 1/1 Running 0 45d
kube-apiserver-kube0 1/1 Running 0 45d
kube-controller-manager-kube0 1/1 Running 0 45d
kube-proxy-9fz4q 1/1 Running 0 45d
kube-proxy-tw7lp 1/1 Running 0 45d
kube-scheduler-kube0 1/1 Running 0 45d

On clusters you build yourself, the control plane runs as Pods too: kube-apiserver, kube-scheduler, kube-controller-manager and etcd. All of them should be Running. On a managed cluster you will only see the add-ons, such as coredns and kube-proxy.

That is your health-check routine. Version, control plane, nodes, one node in detail. A few read-only commands tell you whether a cluster is ready for work before you deploy anything to it, and none of them can break anything.

Troubleshooting

SymptomLikely causeWhat to do
Only Client Version is printed, then a connection errorWrong context, the cluster is stopped, or a network or VPN problemRun kubectl config current-context, start your local cluster (for example minikube start) or reconnect
componentstatuses shows Unhealthy on a managed clusterThe API server cannot reach those components directly; this is common and often harmlessUse kubectl get --raw='/readyz?verbose' instead
A node shows NotReadyThe kubelet stopped, the network plugin failed, or the node ran out of resourcesRun kubectl describe nodes <name> and read Conditions and Events
kubectl version prints a warning about the supported minor version skewkubectl is more than one minor version away from the API serverInstall a kubectl version that matches the cluster

Summary

kubectl is your remote control for the cluster: every command is a request to the API server. A handful of read-only commands tells you whether a cluster is reachable, healthy and ready for work.

  1. kubectl version confirms connectivity. Keep kubectl within one minor version of the API server.
  2. kubectl get componentstatuses is a quick (deprecated) control plane check; kubectl get --raw='/readyz?verbose' is the modern replacement.
  3. The control plane (API server, scheduler, controller-manager and etcd) manages the cluster; worker nodes run your workloads.
  4. kubectl get nodes lists nodes and their status; add -o wide for IPs, OS and container runtime.
  5. kubectl describe nodes <name> shows conditions, capacity, the Pods on the node and their resource usage.
  6. A request is guaranteed and a limit is the maximum. Requests drive scheduling and capacity planning.

Next, you will move from looking at nodes to working with the objects inside the cluster, using the most common kubectl commands.