13

Static Pods, Manual Scheduling, Labels & Selectors

Video: Day 13/40 — Static Pods, Manual Scheduling, Labels, and Selectors • https://www.youtube.com/watch?v=6eGf7_VSbrQ • Duration: ~30 min

Key terms

TermMeaning
Static podPod managed directly by the kubelet, not the API server
Manifest pathDirectory the kubelet watches for static pods
kubeletThe node agent
nodeNameField pinning a pod to a node (skips the scheduler)
LabelA key=value tag on objects
SelectorA query that matches labels
nodeSelectorSimple node targeting by label

Problem & solution

Sometimes you must run a pod without the scheduler or even the API server (like control-plane bootstrap), pin a pod to a specific node, and reliably group and find objects. Static pods, nodeName, and labels/selectors solve these.

Solution: Place pod manifests in the kubelet's manifest directory for node-managed static pods, and use labels/nodeName for manual scheduling.

The analogy

At the port the harbor master's office normally assigns every ship to a berth, but the dock foreman keeps one private ship he runs himself straight from his own logbook, without waiting on the office at all. He also sticks colored tags on containers so any crew can find and group them at a glance. In Kubernetes the office is the control plane, the foreman is the kubelet, his private ship is a static pod the kubelet runs from a file on disk, and the colored tags are labels you match with selectors.

Where this fits in the cluster

Today touches three layers: static pods are created by the kubelet on a node, manual scheduling bypasses the scheduler, and labels/selectors are the glue used everywhere.

What the kubelet is (the node agent)

The kubelet is the agent that runs on every node. It's the bridge between the control plane's desired state and the real containers running on that machine. The api-server decides "this pod should run on node-2"; the kubelet on node-2 actually makes it happen and keeps reporting back.

Graph legend — the kubelet as the per-node execution agent:

Graph nodeMaps toWhat it does
api-serverkube-apiserverHolds desired PodSpecs the kubelet watches
kubelet node agentthe node's kubeletTurns PodSpecs (or static files) into running containers
CRI containerd or CRI-Othe container runtimePulls images and creates/starts containers
containera running containerThe actual workload process on the node

What the kubelet does not do: it does not pick the node (that's the scheduler) and it does not run cluster-wide logic (that's the controllers). It only manages the pods assigned to its own node.

One-liner: the kubelet is the per-node worker that turns PodSpecs into running containers and reports their health back to the api-server. Static pods are special because the kubelet reads them straight from disk — no api-server or scheduler needed.

   scheduler  -> DECIDES which node           (placement)
   kubelet    -> RUNS + watches pods on ITS node  (execution)   <== Day 13
   runtime    -> actually creates the container    (containerd/CRI-O)

Static Pods — managed by kubelet, not the API server

A static pod is created directly by the kubelet from a local file, bypassing the scheduler and API server entirely.

Graph legend — how the control plane's etcd runs as a static pod:

Graph nodeMaps toWhat it does
/etc/kubernetes/manifests/etcd.yamlthe kubelet's staticPodPath directoryA pod manifest on disk the kubelet watches
kubelet reads filethe node's kubeletRuns and supervises the pod with no API server/scheduler
etcd static poda real control-plane static podThe cluster datastore, bootstrapped by kubelet alone
nginx pod (normal)a normal Pod via the API serverGoes through scheduler then kubelet for comparison

The kubelet watches a directory (default /etc/kubernetes/manifests/). Drop a pod YAML there -> kubelet runs it and keeps it alive. This is how control-plane components (apiserver, etcd, controller-manager, scheduler) themselves run.

   /etc/kubernetes/manifests/
        kube-apiserver.yaml      -> static pod
        etcd.yaml                -> static pod
        my-static.yaml           -> your static pod
  • A read-only mirror pod appears in the API for visibility, but you can't delete it via kubectl — you must remove the file.
# find the manifest path
grep staticPodPath /var/lib/kubelet/config.yaml
# static pods show with the node name as a suffix
kubectl get pods -A -o wide | grep <node-name>

Manual Scheduling — bypass the scheduler with nodeName

Setting spec.nodeName hardcodes which node a pod runs on, skipping the scheduler's normal placement decision.

   Normal:  scheduler picks node based on resources/affinity
   Manual:  you hardcode spec.nodeName -> pod lands there directly

The pod below sets spec.nodeName directly, so the scheduler is skipped and the pod runs on exactly that node.

apiVersion: v1
kind: Pod
metadata:
  name: pinned
spec:
  nodeName: cka-worker2      # skip scheduler, go to this node
  containers:
    - name: nginx
      image: nginx:1.27

Used when the scheduler is down or for special placement. If nodeName is set, the scheduler is skipped entirely.

Labels & Selectors — the glue of Kubernetes

Labels are key/value tags you attach to objects, and selectors are queries that find objects by those labels — this is how Kubernetes wires resources together.

Graph legend — a label selector matches by labels, not names:

Graph nodeMaps toWhat it does
Selector app=nginxa -l app=nginx / spec.selector queryFinds objects carrying that label
nginx pod app=nginxthe pods' metadata.labelsMatched regardless of env value
redis pod app=redisa pod with a different labelNot matched by the selector

Labels are key/value tags ON objects (e.g. app=web, env=prod); a selector is a query to FIND objects by those labels.

kubectl label pod nginx tier=frontend       # add a label
kubectl get pods --show-labels
kubectl get pods -l app=web                  # equality selector
kubectl get pods -l 'env in (dev,prod)'      # set-based selector
kubectl get pods -l app=web,env=prod         # AND of two labels
kubectl label pod nginx tier-                # remove label (trailing -)

Where selectors are used

Selectors show up across many resource types, each using labels to decide which pods or nodes they target.

   Service        -> selects which pods to route to
   Deployment/RS  -> selects which pods it owns
   NetworkPolicy  -> selects which pods a rule applies to
   nodeSelector   -> pod picks nodes by node labels

nodeSelector (simple node targeting)

nodeSelector is the simplest way to constrain a pod to nodes carrying a specific label.

spec:
  nodeSelector:
    disktype: ssd      # only schedule on nodes labeled disktype=ssd

First label the node so it carries the key the pod is looking for; otherwise the pod has nowhere to land.

kubectl label node cka-worker disktype=ssd

End-to-end example: label, pin, and select

Run two labeled pods (one pinned to a specific node), then use a Service that selects them by label — showing how labels wire pods to a Service.

Graph legend — labels wire two nginx pods (one pinned) to a Service:

Graph nodeMaps toWhat it does
Service nginx selector app=nginxService.spec.selectorRoutes to every pod labeled app=nginx
nginx pod web-aa pod with labels: {app: nginx, env: prod}Scheduled normally; matched by the selector
nginx pod web-b (pinned)a pod with spec.nodeName: cka-worker2Manually scheduled to node-2, still selector-matched
apiVersion: v1
kind: Pod
metadata:
  name: web-b
  labels: { app: nginx, env: prod }
spec:
  nodeName: cka-worker2         # manual scheduling: skip the scheduler
  containers:
    - name: nginx
      image: nginx:1.27
---
apiVersion: v1
kind: Service
metadata: { name: nginx }
spec:
  selector: { app: nginx }      # selector matches the pod labels above
  ports:
    - port: 80
      targetPort: 80
kubectl run web-a --image=nginx:1.27 --labels="app=nginx,env=prod"
kubectl apply -f web-b-and-svc.yaml
kubectl get pods -o wide -l app=nginx       # see both, and which nodes
kubectl get endpoints nginx                 # both pod IPs listed via the selector
kubectl get pods -l 'env in (prod)'         # set-based selector query

End-to-end flow

Static pods come straight from disk via the kubelet, and nodeName pins a pod to a node without the scheduler.

Graph legend — static pods from disk vs nodeName pinning:

Graph nodeMaps toWhat it does
kubelet watches /etc/kubernetes/manifestsstaticPodPathDirectory the kubelet reads static pod files from
Starts kube-apiserver static poda control-plane static podRuns without scheduler or API server
Mirror pod shown in API serverthe read-only mirror podVisibility only; delete the file to remove it
nginx pod with nodeName cka-worker2Pod.spec.nodeNamePins the pod to a node, bypassing the scheduler

Key takeaways

  • Static pods are file-managed by kubelet; that's how the control plane runs.
  • nodeName pins a pod to a node, skipping the scheduler.
  • Labels + selectors connect Services, controllers, policies to pods.

Checklist

  • [ ] Created a static pod via the manifests directory
  • [ ] Pinned a pod to a node with nodeName
  • [ ] Added/removed labels and queried with -l (equality + set-based)
  • [ ] Used nodeSelector with a node label