Skip to content
Labels and annotations

Labels and annotations

Almost nothing in Kubernetes refers to anything else by name. Objects find each other through labels and selectors, and a great many “why is nothing connecting?” problems are a selector matching nothing.

Reference

Terminology

  • Label — A key/value pair attached to an object, intended to be selected on. Labels are indexed, so querying by them is cheap. Keys and values are constrained: 63 characters, alphanumeric with -, _, ..
  • Annotation — A key/value pair that is not selected on. Annotations are for tools and humans: build IDs, git commits, dashboard URLs, ingress configuration. Values can be large and unstructured.
  • Selector — A query over labels. matchLabels is equality-based; matchExpressions adds In, NotIn, Exists and DoesNotExist.
  • Set-based selector — A selector using matchExpressions, which can express things equality cannot: “has this label at all”, or “one of these three environments”.
  • Owner reference — How one object owns another. A Deployment owns ReplicaSets, which own Pods. Deletion cascades along this chain, and it is separate from labels — a common source of confusion.

The rule of thumb: if you would ever want to select on it, it is a label. If not, it is an annotation. Labels are for the cluster; annotations are for everything else.

Who Selects What

Deployment ──selects──▶ ReplicaSet ──selects──▶ Pods
                                                  ▲
Service ────────────────selects───────────────────┘
NetworkPolicy ──────────selects───────────────────┘
PodDisruptionBudget ────selects───────────────────┘

Every arrow is a label selector. Change a Pod’s labels so they no longer match its Deployment’s selector, and the Deployment will create a replacement while the orphan keeps running — two Pods where you expected one. Change a Service’s selector and its Endpoints empty out.

Selecting From The Command Line

kubectl get pods --show-labels
kubectl get pods -l app=web
kubectl get pods -l 'app in (web,api)'
kubectl get pods -l 'tier!=frontend'              # includes pods with no tier label at all
kubectl get pods -l app=web,tier=frontend         # AND

That third example is the one that surprises people: != matches objects that do not have the label, as well as those with a different value.

Exercises

  1. Add and change labels.

    kubectl run probe --image=docker.io/library/nginx:1.27 --labels=app=probe
    kubectl get pod probe --show-labels
    kubectl label pod probe tier=frontend
    kubectl get pods -l tier=frontend
  2. Watch a Service lose its endpoints. Create a Deployment and a Service that selects it, confirm the Endpoints are populated, then relabel the Pods so the selector no longer matches:

    kubectl create deployment web --image=docker.io/library/nginx:1.27 --replicas=2
    kubectl expose deployment web --port=80
    kubectl get endpoints web                     # two addresses
    kubectl label pod -l app=web app=renamed --overwrite
    kubectl get endpoints web                     # empty

    The Service is healthy, the Pods are healthy, and traffic goes nowhere. This is the single most useful diagnostic skill in this module.

  3. Use a set-based selector. Create a PodDisruptionBudget or NetworkPolicy with matchExpressions and confirm with kubectl get <kind> -o yaml that it was stored as you meant.

  4. Annotate something. Annotations do nothing on their own, which is the point:

    kubectl annotate pod probe owner=you reason=testing
    kubectl get pod probe -o jsonpath='{.metadata.annotations}{"\n"}'

What to Take Away

  • Objects refer to each other by selector, not by name.
  • Labels are for selecting; annotations are for everything else.
  • kubectl get endpoints <service> is the fastest way to find out whether a selector is doing what you think.
  • If you see more Pods than you asked for, look for one whose labels stopped matching its owner.

Where This Is Practised

Lab 01 in the workbook is built around this. Run labctl list to see whether it is ready yet.