By the end you'll know
- What a namespace is, and how to work in one
- Why the same name can exist twice in one cluster
- How Kubernetes decides who is allowed to do what
- How to give an app exactly the access it needs, and prove it
Namespaces: folders for your cluster
A cluster can run many apps, for many teams. If everything sat in one big list, names would clash, and anyone could change anything. A namespace is a folder inside the cluster that groups related things together.
You've been using one all along. Everything you created so far went into the namespace called default, and in Lesson 2 you saw the control plane's pods in kube-system. See them all:
kubectl get namespaces
NAME STATUS AGE
default Active 5m
kube-node-lease Active 5m
kube-public Active 5m
kube-system Active 5m
local-path-storage Active 5m
Two things to know about namespaces:
- Names only have to be unique inside a namespace. Two teams can each have a Deployment called
web, as long as they're in different namespaces. - They're a boundary for access, not a wall for traffic. Pods in different namespaces can still talk to each other by default. Namespaces are about organizing and permissions. (Locking down traffic between them is a topic for later.)
Try it: work in namespaces
Start a cluster and create two namespaces:
kind create cluster --name learn
kubectl create namespace shop
kubectl create namespace blog
Now create a Deployment with the same name in each one. -n (short for --namespace) says which namespace a command acts on:
kubectl create deployment web --image=nginx:1.28 -n shop
kubectl create deployment web --image=nginx:1.28 -n blog
Both work, because each web lives in its own namespace. Look at them:
kubectl get deployments -n shop
kubectl get deployments -A
-A (short for --all-namespaces) shows everything, with a NAMESPACE column so you can tell them apart.
Now try without -n:
kubectl get deployments
No resources found in default namespace. Without -n, kubectl uses default. This catches everyone at least once: the thing you're looking for exists, just not where you're looking. If you'll be working in one namespace for a while, you can change the default:
kubectl config set-context --current --namespace=shop
Switch back with --namespace=default when you're done.
Remember the DNS names from Lesson 8? This is where the namespace part comes in. From a pod in blog, the short name web finds the blog Service, and web.shop reaches the one in shop.
Be careful deleting a namespace
kubectl delete namespace shop deletes the namespace and everything inside it: every Deployment, pod, Service and Secret, with no "are you sure?". On a practice cluster that's a handy way to clean up. On a real one, double-check the name and your current context first.
Access control: who can do what
Namespaces organize things. Access control decides who may touch them. Kubernetes uses a system called RBAC (role-based access control), built from three pieces:
- Who: a user, or a ServiceAccount. A ServiceAccount is an identity for an app rather than a person, like a dashboard or a deploy tool that talks to the cluster.
- What they may do: a Role, a list of allowed actions, such as "get and list pods". Nothing is allowed unless a Role says so.
- The link: a RoleBinding, which gives a Role to a who, inside one namespace.
There's also a cluster-wide version of each: a ClusterRole and a ClusterRoleBinding work across every namespace, for things like nodes that don't belong to any namespace. They follow the same idea.
Try it: give an app read-only access
Create a ServiceAccount called viewer in shop:
kubectl create serviceaccount viewer -n shop
Before giving it anything, ask Kubernetes what it's allowed to do. kubectl auth can-i answers yes or no, and --as asks on behalf of someone else:
kubectl auth can-i list pods -n shop --as=system:serviceaccount:shop:viewer
no. A new ServiceAccount starts with no access at all. Now save this as access.yaml. It's two objects in one file, split by --- (Lesson 4):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: shop
rules:
- apiGroups: [""] # "" means the core group, where pods live
resources: ["pods"]
verbs: ["get", "list", "watch"] # read only: no create, update or delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: viewer-reads-pods
namespace: shop
subjects: # who gets the access
- kind: ServiceAccount
name: viewer
namespace: shop
roleRef: # which Role they get
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Apply it, and ask again, three ways:
kubectl apply -f access.yaml
kubectl auth can-i list pods -n shop --as=system:serviceaccount:shop:viewer
kubectl auth can-i delete pods -n shop --as=system:serviceaccount:shop:viewer
kubectl auth can-i list pods -n blog --as=system:serviceaccount:shop:viewer
yes, no, no. It can read pods in shop, it can't delete them, and it has no access in blog. That's exactly what we asked for, and you proved it without running the app at all.
In my real cluster
My dashboard, PodYard, runs on my home server and shows every pod in my production cluster. It logs in with its own ServiceAccount, podyard, whose ClusterRole can only get, list and watch the things the dashboard displays (pods, nodes, events, Deployments and Argo CD's app status) and read pod logs. Nothing can be created, changed or deleted, and Secrets aren't readable at all. Before PodYard ever ran, I checked it with kubectl auth can-i: yes to listing pods, no to deleting them, no to reading Secrets. If the dashboard, or my home server, were ever compromised, the most anyone could do with it is look.
My production cluster also uses namespaces the way this lesson describes: civicgrid for the app, monitoring for Prometheus and Alertmanager, argocd for deployments, cloudflared for the tunnel, and so on. Each tool keeps to its own floor.
Break it on purpose
Change the RoleBinding's namespace to blog, in both metadata.namespace and the subjects entry, and apply it again. Then ask:
kubectl auth can-i list pods -n blog --as=system:serviceaccount:shop:viewer
Still no. The binding is now in blog, but it points at a Role called pod-reader in blog, which doesn't exist. A RoleBinding can only use a Role from its own namespace. When access doesn't work, check these three things in order: is the binding in the right namespace, does it name the right Role, and does it name the right who. Change it back to shop when you're done.
Clean up
kind delete cluster --name learn
Check yourself
Two teams each want a Deployment called web. What makes that possible?
You run kubectl get pods and see nothing, but you know the pods exist. What's the most likely reason?
What does kubectl delete namespace shop do?
A brand-new ServiceAccount tries to list pods. What happens?
Which command checks what a ServiceAccount is allowed to do, without running anything as it?
A dashboard only needs to show pods. What's the safest access to give it?
Key terms
- Namespace: a folder inside a cluster that groups related things. Names only have to be unique within one.
- RBAC: role-based access control, the system Kubernetes uses to decide who can do what.
- ServiceAccount: an identity for an app or tool, rather than a person.
- Role: a list of allowed actions, inside one namespace. A ClusterRole is the cluster-wide version.
- RoleBinding: gives a Role to a user or ServiceAccount, inside one namespace. A ClusterRoleBinding does it cluster-wide.
- kubectl auth can-i: asks Kubernetes whether an action is allowed, for you or, with
--as, for someone else.
Go deeper
- Namespaces (opens in a new tab) in the official docs.
- Using RBAC authorization (opens in a new tab) covers Roles, ClusterRoles and every kind of binding.