DEV Community

Cover image for Kubernetes Cookbook Part 3: Organize Clusters with Namespaces
FlatlinerAtheist | CLI-NERDS
FlatlinerAtheist | CLI-NERDS

Posted on Originally published at clinerds.com

Kubernetes Cookbook Part 3: Organize Clusters with Namespaces

So far in this series you have a local cluster and you know how to run Pods on it. Everything you created landed in the same big shared room. This part teaches you how to put up walls: Kubernetes namespaces let you split one physical cluster into many virtual ones, aim kubectl at the room you care about, and stop any single team from eating the whole buffet.

What you need

  • A working local cluster from Part 1 (kind or minikube) and kubectl pointed at it.
  • Comfort with creating Pods from Part 2.
  • About 20 minutes and nothing to install.

Recipe 1: Meet the built-in namespaces

Problem: You keep hearing about "namespaces" and want to know what already exists in a fresh cluster before you create your own.

Ask the cluster to list them:

kubectl get namespaces
Enter fullscreen mode Exit fullscreen mode
NAME              STATUS   AGE
default           Active   4d
kube-node-lease   Active   4d
kube-public       Active   4d
kube-system       Active   4d
Enter fullscreen mode Exit fullscreen mode

(kubectl get ns is the short form and does the same thing.)

How it works: A namespace is a scope for names and for API objects. Two Pods can both be called clinerds-demo as long as they live in different namespaces. The four you see above come with every cluster:

  • default is where your objects go when you don't say otherwise. Everything you made in Parts 1 and 2 lives here.
  • kube-system holds the cluster's own machinery: the DNS server, the network plugin, and so on. Look, but don't deploy your apps here.
  • kube-public is readable by everyone, even unauthenticated clients. It's mostly used for cluster discovery info and you can ignore it.
  • kube-node-lease stores small "lease" heartbeat objects that each node renews so the control plane can tell quickly when a node has died.

One caveat worth burning into memory: namespaces are not a hard security boundary by themselves. By default, a Pod in one namespace can still open network connections to Pods in every other namespace, and anyone with cluster-wide kubectl access sees everything. Namespaces give you organization and a place to hang policies. The actual locks — network policies and role-based access control (RBAC) — come later in this series, especially in Part 10.

Recipe 2: Create dev and prod lanes and run the same app in both

Problem: You want to test changes without touching the "production" copy of your app, but you only have one cluster.

Create two namespaces, one for each lane:

kubectl create namespace cookbook-dev
kubectl create namespace cookbook-prod
Enter fullscreen mode Exit fullscreen mode

Now save the series demo app as clinerds-demo-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: clinerds-demo
spec:
  containers:
    - name: clinerds-demo
      image: hashicorp/http-echo
      args:
        - "-text=hello from the cookbook"
      ports:
        - containerPort: 5678
Enter fullscreen mode Exit fullscreen mode

Apply the exact same file into both namespaces:

kubectl apply -f clinerds-demo-pod.yaml -n cookbook-dev
kubectl apply -f clinerds-demo-pod.yaml -n cookbook-prod
Enter fullscreen mode Exit fullscreen mode

The -n flag tells kubectl which namespace to work in. Check one lane:

kubectl get all -n cookbook-dev
Enter fullscreen mode Exit fullscreen mode
NAME                READY   STATUS    RESTARTS   AGE
pod/clinerds-demo   1/1     Running   0          25s
Enter fullscreen mode Exit fullscreen mode

Now sweep the whole cluster at once:

kubectl get pods --all-namespaces
Enter fullscreen mode Exit fullscreen mode
NAMESPACE       NAME                                READY   STATUS    AGE
cookbook-dev    clinerds-demo                       1/1     Running   40s
cookbook-prod   clinerds-demo                       1/1     Running   40s
kube-system     coredns-76f75df574-abcde            1/1     Running   4d
Enter fullscreen mode Exit fullscreen mode

How it works: Both Pods are named clinerds-demo and nothing collides, because object names only have to be unique within a namespace. Think of namespaces as folders: two folders can each contain a file with the same name. --all-namespaces (short form -A) lifts the folder filter and shows you everything, which is great for "where did that thing go?" moments.

Peeking ahead: Namespaces also show up in DNS. Once an app has a Service, other Pods can reach it at service-name.namespace-name.svc.cluster.local, so dev and prod copies are addressable side by side. Part 5 serves the full story on Services and DNS.

Recipe 3: Stop typing -n on every command

Problem: You're doing all your work in cookbook-dev today and adding -n cookbook-dev to every command is getting old.

Your kubectl configuration (usually ~/.kube/config) holds a context: the bundle of "which cluster, which user, which namespace" that every command uses. Set the namespace on your current context:

kubectl config set-context --current --namespace=cookbook-dev
Enter fullscreen mode Exit fullscreen mode

Verify it stuck:

kubectl config view --minify | grep namespace
Enter fullscreen mode Exit fullscreen mode
    namespace: cookbook-dev
Enter fullscreen mode Exit fullscreen mode

Now plain commands default to that lane:

kubectl get pods
Enter fullscreen mode Exit fullscreen mode
NAME            READY   STATUS    RESTARTS   AGE
clinerds-demo   1/1     Running   0          5m
Enter fullscreen mode Exit fullscreen mode

No -n needed. When you're done, switch back to the cluster-wide default:

kubectl config set-context --current --namespace=default
Enter fullscreen mode Exit fullscreen mode

How it works: --current edits the context you're actively using instead of creating a new one. Every later command reads that namespace field unless you override it with an explicit -n. The setting lives in your local kubeconfig file, so it only affects your machine — it changes nothing on the cluster itself.

Variations:

  • The community tool kubens (installed alongside kubectx) makes this a one-word command: kubens cookbook-dev. Handy if you switch lanes fifty times a day.
  • You can also see the active namespace quickly with kubectl config view --minify -o jsonpath='{..namespace}'.

Recipe 4: Put a ResourceQuota on the dev buffet

Problem: The dev lane is for experiments, but one forgotten experiment shouldn't be allowed to eat the whole cluster's CPU and memory.

A ResourceQuota is a per-namespace budget. Save this as cookbook-dev-quota.yaml:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: cookbook-dev-quota
  namespace: cookbook-dev
spec:
  hard:
    pods: "3"
    requests.cpu: "500m"
    requests.memory: "512Mi"
Enter fullscreen mode Exit fullscreen mode
kubectl apply -f cookbook-dev-quota.yaml
Enter fullscreen mode Exit fullscreen mode

Make sure you're still targeting cookbook-dev from Recipe 3, then create a couple of Pods that each request 100 millicores (a millicore is one-thousandth of a CPU) and 64 mebibytes:

kubectl run demo-2 --image=hashicorp/http-echo --requests="cpu=100m,memory=64Mi" -- -text=two
kubectl run demo-3 --image=hashicorp/http-echo --requests="cpu=100m,memory=64Mi" -- -text=three
Enter fullscreen mode Exit fullscreen mode

That's three Pods — the budget is full. Try a fourth:

kubectl run demo-4 --image=hashicorp/http-echo --requests="cpu=100m,memory=64Mi" -- -text=four
Enter fullscreen mode Exit fullscreen mode
Error from server (Forbidden): pods "demo-4" is forbidden:
exceeded quota: cookbook-dev-quota, requested: pods=1, used: pods=3, limited: pods=3
Enter fullscreen mode Exit fullscreen mode

Check the running tab any time:

kubectl describe quota cookbook-dev-quota -n cookbook-dev
Enter fullscreen mode Exit fullscreen mode
Name:            cookbook-dev-quota
Namespace:       cookbook-dev
Resource         Used   Hard
--------         ----   ----
pods             3      3
requests.cpu     200m   500m
requests.memory  128Mi  512Mi
Enter fullscreen mode Exit fullscreen mode

How it works: A quota is enforced by an admission controller — a gatekeeper inside the API server that inspects every create request before it's stored. When you ask for a new Pod, the controller adds up what the namespace already uses plus what your Pod requests, and rejects the request if any hard limit would be exceeded. Nothing was "scheduled and then killed"; demo-4 never existed.

When it goes wrong:

  • The moment a quota mentions requests.cpu or requests.memory, every new Pod in that namespace must declare those requests, or it's rejected outright. Pods already running are grandfathered in. Recipe 5 shows how to supply those values automatically.
  • Quotas count requests (what a Pod reserves), not actual usage. A Pod sleeping at 1% CPU still spends its full request against the budget.

Recipe 5: Give every container defaults with a LimitRange

Problem: Recipe 4's quota now rejects any Pod that forgets to declare CPU and memory requests. You don't want every developer hand-writing the same numbers.

A LimitRange can inject sensible defaults. First free up some room in the quota, then save cookbook-dev-limits.yaml:

kubectl delete pod demo-2 demo-3
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: LimitRange
metadata:
  name: cookbook-dev-defaults
  namespace: cookbook-dev
spec:
  limits:
    - type: Container
      default:
        cpu: 250m
        memory: 128Mi
      defaultRequest:
        cpu: 100m
        memory: 64Mi
Enter fullscreen mode Exit fullscreen mode
kubectl apply -f cookbook-dev-limits.yaml
Enter fullscreen mode Exit fullscreen mode

Now create a Pod with no resource settings at all:

kubectl run demo-defaults --image=hashicorp/http-echo -- -text=defaults
Enter fullscreen mode Exit fullscreen mode

Inspect what the cluster actually stored:

kubectl describe pod demo-defaults
Enter fullscreen mode Exit fullscreen mode
    Limits:
      cpu:     250m
      memory:  128Mi
    Requests:
      cpu:     100m
      memory:  64Mi
Enter fullscreen mode Exit fullscreen mode

How it works: LimitRange is another admission controller, but instead of rejecting Pods it edits them on the way in. When a container arrives with no resources block, the controller copies in defaultRequest as the requests and default as the limits before the Pod is saved. Your YAML stayed clean, and the quota is satisfied because the requests now exist. Note this only applies to Pods created after the LimitRange — it doesn't retroactively fix old ones. A container that sets its own values keeps them; defaults only fill in blanks.

Cleaning up

When you're done experimenting, one command removes the whole lane and everything in it:

kubectl delete ns cookbook-dev
Enter fullscreen mode Exit fullscreen mode

Treat this with respect: deleting a namespace deletes every object inside it — Pods, quotas, LimitRanges, ConfigMaps, Secrets, all of it. There is no trash can and no undo. Keep cookbook-prod around if you want a running demo for the next part.

Where to go next

You can now slice one cluster into tidy lanes, point kubectl where you want, and keep resource appetites in check. The loose end is that our demo app is still a single hand-made Pod — one kubectl delete and it's gone forever. Part 4: Deployments fixes that with self-healing, replica-managed applications. For the official reference, see the Kubernetes namespaces documentation, and browse the whole series at Kubernetes Cookbook.

Top comments (0)