Namespaces, Labels and Selectors

K
Kai··4 min read

In the two previous articles, both Deployment and Service "found pods by label" without us explaining it carefully. This article pays that debt: labels and selectors are the tagging and selection mechanism running throughout Kubernetes — understanding it is understanding how objects bind to each other. Alongside comes namespaces, how to split the cluster into separate compartments.

Namespaces: splitting the cluster into compartments

A cluster usually serves many teams, many environments, many applications. Dumping everything in one place is chaos. A namespace is a "virtual folder" that groups resources, allowing name isolation and applying separate quotas/permissions.

kubectl get namespaces
NAME              STATUS   AGE
default           Active   11m
kube-node-lease   Active   11m
kube-public       Active   11m
kube-system       Active   11m

Every cluster has a few built-in namespaces: default (where your resources land if you don't specify), kube-system (system components — remember in Article 1 we inspected the control plane pods here), kube-public, kube-node-lease. Create a new namespace:

kubectl create namespace dev

Then deploy into it with the -n flag:

kubectl create deployment api --image=nginx:alpine -n dev
kubectl get pods -n dev
NAME                   READY   STATUS    RESTARTS   AGE
api-6676db897b-nlm78   1/1     Running   0          10s

The core point: resources in different namespaces are name-isolated from each other. You can have a Deployment web in default and another web in dev — they don't collide. kubectl get pods (without -n) only shows the current namespace; to see across all, use --all-namespaces (or -A):

kubectl get pods --all-namespaces
NAMESPACE   NAME                   READY   STATUS    AGE
default     web-5687994c96-682pg   1/1     Running   5m2s
dev         api-6676db897b-nlm78   1/1     Running   10s

Some resources live inside a namespace (Pod, Deployment, Service, ConfigMap...), others are cluster-scoped, belonging to no namespace (Node, Namespace, PersistentVolume). If you switch namespaces often, a tool like kubens saves your fingers; or set a default: kubectl config set-context --current --namespace=dev.

Note: a namespace is not a network security boundary by default — a pod in one namespace can still call a pod in another via DNS (web.default). To block that you need a NetworkPolicy (out of scope for this fundamentals series).

Labels: free-form tags for resources

A label is a key=value pair you attach to an object to classify it in ways meaningful to you: app=web, tier=frontend, env=production, version=v2... Labels do nothing on their own — the power comes from selecting by them.

View the labels on the web pods:

kubectl get pods -l app=web --show-labels
NAME                   READY   STATUS    LABELS
web-5687994c96-682pg   1/1     Running   app=web,pod-template-hash=5687994c96
web-5687994c96-qmkr5   1/1     Running   app=web,pod-template-hash=5687994c96
web-5687994c96-spvjm   1/1     Running   app=web,pod-template-hash=5687994c96

Two labels: app=web (we set it in the Article 4 manifest) and pod-template-hash=... (added automatically by the Deployment). The second one is worth noticing — it's exactly how the ReplicaSet distinguishes "pods of which version" during a rolling update (Article 4): each ReplicaSet has its own hash, stamped onto the pods it creates. The system uses labels to manage itself.

Attach/change a label manually:

kubectl label pod web-5687994c96-682pg tier=frontend
pod/web-5687994c96-682pg labeled

Selectors: filtering by label

A selector is a query that filters objects by label. This is what Deployment and Service have been quietly using. Filter with the -l flag:

kubectl get pods -l 'app=web,tier=frontend'    # matches: 1 (the pod just labeled)
kubectl get pods -l app=web                     # matches: 3 (all web pods)

A comma means ANDapp=web,tier=frontend selects pods with both labels. Besides equality, selectors also support other operations: env in (prod,staging), tier!=backend, or just the existence of a key (tier).

This is exactly the mechanism we've met over the past few articles:

   Service "web"  ──selector app=web──►  every pod with label app=web
   Deployment "web" ──selector app=web──►  manages pods with label app=web

Because it's dynamic selection, it automatically covers new pods: any pod stamped with the right label automatically falls within reach of the Service/Deployment, no re-declaration needed. That's why self-healing and scaling (Article 4) "fit snugly" with the Service's load balancing (Article 5) — everything speaks the same label language.

Kubernetes recommends a common label set (app.kubernetes.io/name, app.kubernetes.io/version, app.kubernetes.io/part-of...) so that tools and people understand resources uniformly. Not mandatory, but following it from the start makes large clusters far easier to manage — consistently labeling env, app, version makes filtering logs, debugging, and applying policies later much cleaner.

Wrap-up

A namespace splits the cluster into name-isolated compartments (every cluster ships with default, kube-system...); use -n/--all-namespaces to work across all — but remember a namespace doesn't block networking by itself. A label is a free-form key=value tag attached to objects (including system labels added automatically, like pod-template-hash), and a selector is a query that filters by label (-l app=web, supporting AND/in/existence). This is the foundational "glue": Deployment and Service find pods dynamically via selectors, which is why all the scaling/self-healing/load-balancing mechanisms fit together.

The application runs and is reachable, but the configuration (environment variables, passwords) is still baked into the image. Article 7: ConfigMap and Secret — separating configuration from the image so the same image runs in every environment.