agentleFS
k8s-servantk8s-servant/k8s-skills/skills/k8s-node-troubleshooting/SKILL.md

k8s-node-troubleshooting

Skill36 starsChanged 15 days agoApache-2.0View on GitHub

Use when a Kubernetes node is unhealthy or pods will not go onto it: a node NotReady or Unknown, MemoryPressure, DiskPressure or PIDPressure, pods Evicted, pods Pending with "untolerated taint(s)", "Insufficient cpu" or "Insufficient memory" while the node looks idle, a node cordoned (SchedulingDisabled), or the cluster autoscaler not adding or removing nodes. Investigates with kubectl get, describe and events, and proposes fixes. Reads first and asks before changing anything.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    2. Describe the node: Read Conditions (status, reason, LastHeartbeatTime), Taints, Unschedulable, Lease (RenewTime), Capacity and …

    line 65
  2. Very specific:

    Pods on a NotReady node: keep their status for a while.

    line 128
  3. Very specific:

    Remove a taint left from a maintenance that is over (the key, value and effect exactly as describe node shows them):

    line 167
  4. Distinctive:

    Check the kubelet on a node that stopped reporting.

    line 177

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Kubernetes node troubleshooting

Find out why a node is unhealthy, or why pods will not be scheduled onto it, and propose a fix. Read first, ask before changing. Run the investigation commands freely. Run a command under an "Ask first" line only after the user approves that exact command; if your credentials do not allow it, give it to the user to run. Never run a "Proposed fix"; hand it to the user.

You see the node only through the Kubernetes API. Checks on the node itself (the kubelet, the container runtime, the disk) need someone with access to the machine, so they are always proposed fixes for a human.

The commands use placeholders: my-node, my-namespace, my-app and my-pod. Replace them with the real names. Never read Secrets. If output contains passwords, tokens or keys, do not repeat them.

When to use

  • kubectl get nodes shows a node NotReady, Unknown or SchedulingDisabled.
  • A node reports MemoryPressure, DiskPressure or PIDPressure, or pods were Evicted.
  • Pods stay Pending and the FailedScheduling event names taints, Insufficient cpu or Insufficient memory, or unschedulable nodes.
  • The cluster autoscaler does not add a node for Pending pods, or does not remove an empty one.

Hand-offs, by skill name. When the evidence points elsewhere, continue with that skill; if it is not installed, keep going with the steps here and tell the user which skill would help:

  • The nodes are healthy and have room, and the pod itself fails (crash, image, probe, quota): k8s-workload-troubleshooting.
  • A volume cannot attach or mount on the node: k8s-storage-troubleshooting.
  • NetworkUnavailable is True, or the CNI pods on the node fail: k8s-network-troubleshooting.
  • On AKS, a node pool or the cluster is Failed, or an upgrade or scale fails in Azure (quota, VM capacity, a drain blocked by a PodDisruptionBudget): k8s-aks-troubleshooting.

Investigate

Work through the steps in order. Stop when you have the cause and its evidence.

1. List the nodes. STATUS shows Ready, NotReady or Unknown, plus SchedulingDisabled when a node is cordoned.

kubectl get nodes -o wide

2. Describe the node. Read Conditions (status, reason, LastHeartbeatTime), Taints, Unschedulable, Lease (RenewTime), Capacity and Allocatable, Non-terminated Pods with their requests, and Allocated resources.

kubectl describe node my-node

3. See the pods on the node, in every namespace you can read.

kubectl get pods -A -o wide --field-selector spec.nodeName=my-node
kubectl get pods -A --field-selector spec.nodeName=my-node,status.phase=Failed

The second command lists failed pods there, which include Evicted ones.

4. Read the node's events and its heartbeat. Node events are kept in the default namespace; the heartbeat is a Lease named after the node. The built-in view role cannot read Leases; if the second command is forbidden, use LastHeartbeatTime of the conditions in describe node instead.

kubectl get events -A --field-selector involvedObject.kind=Node,involvedObject.name=my-node
kubectl get lease my-node -n kube-node-lease

5. For a Pending pod, read why the scheduler refused it, and what it asks for.

kubectl describe pod my-pod -n my-namespace
kubectl get pod my-pod -n my-namespace -o jsonpath='{.spec.nodeSelector}{"\n"}{.spec.tolerations}{"\n"}{.spec.containers[*].resources.requests}'

6. Look at usage, if the cluster has metrics-server. top shows usage, not requests; the scheduler uses requests. Without metrics-server it fails with "Metrics API not available"; say so and go on.

kubectl top node my-node

7. Open the matching reference and follow its steps:

What you seeReference
NotReady, Unknown, "Kubelet stopped posting node status"references/not-ready.md
MemoryPressure, DiskPressure, PIDPressure, Evicted podsreferences/pressure.md
Insufficient cpu or Insufficient memory, requests near Allocatablereferences/capacity.md
untolerated taint(s), SchedulingDisabled, node(s) were unschedulablereferences/taints.md
Pending pods but no new node, or an empty node that staysreferences/autoscaler.md

Read the results

What the common findings mean (Kubernetes 1.37):

  • Ready Unknown: the node controller has not heard from the kubelet within node-monitor-grace-period (50 seconds by default). The kubelet is down, or the node cannot reach the API server. The Lease in kube-node-lease, normally renewed every 10 seconds, stops changing. The node gets the node.kubernetes.io/unreachable taints.
  • Ready False: the kubelet is running and reports the node is not healthy (for example the container runtime or the network plugin is not ready). The reason and message say which. The node gets the node.kubernetes.io/not-ready taint.
  • Pods on a NotReady node keep their status for a while. Pods tolerate the not-ready and unreachable NoExecute taints for 300 seconds by default (added by the DefaultTolerationSeconds admission controller), then they are evicted. DaemonSet pods tolerate them without a time limit and are not evicted.
  • MemoryPressure, DiskPressure or PIDPressure True: the kubelet crossed an eviction threshold. It adds a NoSchedule taint for that pressure, frees resources, then evicts pods. Node-pressure eviction does not respect PodDisruptionBudgets.
  • FailedScheduling with 0/N nodes are available: ...: each part counts the nodes refused for one reason, for example 1 node(s) had untolerated taint(s), 2 Insufficient cpu or 1 node(s) were unschedulable. The text after preemption: says whether evicting lower-priority pods could help.
  • Insufficient cpu on an idle node: the scheduler adds up the requests of the pods on the node and compares them with Allocatable, not with actual use. Allocated resources in describe node shows the requests as a percentage of Allocatable.
  • SchedulingDisabled, Unschedulable: true: the node is cordoned. New pods do not go there; pods already there keep running.

State the cause in one sentence, then the evidence you saw (the condition and its message, the taint, the event, the numbers), then the proposed fix.

Proposed fix

Show each fix to the user before running it: what it changes and how to undo it. Cordoning, uncordoning, adding or removing a NoSchedule taint and labelling a node are reversible, so they go under "Ask first" and run only after the user says yes. Draining or deleting a node, force-deleting pods and anything on the node host are never run by you: hand them to the user as proposed fixes and say why. Prefer fixing the source the workload or the node pool is managed from when there is one. Write each undo with the values you found.

Uncordon a node whose maintenance is over and that is Ready:

Ask first: the agent runs this only after the user approves it.

kubectl uncordon my-node
# Undo: kubectl cordon my-node

Remove a taint left from a maintenance that is over (the key, value and effect exactly as describe node shows them):

Ask first: the agent runs this only after the user approves it.

kubectl taint nodes my-node example.com/maintenance=true:NoSchedule-
# Undo: kubectl taint nodes my-node example.com/maintenance=true:NoSchedule

Check the kubelet on a node that stopped reporting. This runs on the node, which you cannot reach; a person with access to the machine runs it:

Proposed fix: review before running. The agent does not run this.

systemctl status kubelet
journalctl -u kubelet --since "30 min ago" --no-pager | tail -n 50
sudo systemctl restart kubelet
# Undo: nothing to undo for the first two lines, they only read; a restart cannot be undone, the kubelet just starts again

Move the pods off a node that will not come back soon. Draining evicts every pod that is not a DaemonSet pod and deletes emptyDir data, so never run it yourself:

Proposed fix: review before running. The agent does not run this.

kubectl drain my-node --ignore-daemonsets --delete-emptydir-data
# Undo: kubectl uncordon my-node (the evicted pods are recreated by their controllers, not moved back)

Sources

Checked against the Kubernetes 1.37 documentation:

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.