Flats, not houses: Namespaces
- Published
- Read
- 6 minutes
- Tags
Give development and production their own namespaces, with separate nginx instances and Ingress routes in one cluster.
Namespaces let development and production have their own copies of an application in the same cluster.
In chapter two, you gave nginx a front door. Add a development copy and naming things gets awkward. You could call everything nginx-development, nginx-production, and so on. Kubernetes gives you another place to put that distinction: the namespace.
Think of a cluster as a building with several flats. Each flat can have its own kitchen without the residents agreeing on a unique name for it. A namespace gives a group of Kubernetes resources that kind of address.
Give each environment its own room
Development, staging, and production are common reasons to use namespaces. You can also organise them around applications or teams. Here, I’ll use development and production, with the same small nginx application in each.
A Pod named guide-nginx can exist in both. So can a Service and an Ingress with that name. Names must be unique for a resource type within its namespace; they do not need to be unique across the whole cluster.
Resources such as Pods and Services belong to namespaces, while Nodes belong to the cluster. Creating two namespaces does not create two sets of machines.
You’ve been using default
Continue with the Docker Desktop kind cluster from the previous chapters, on Windows or macOS. Keep the nginx YAML files and the Traefik installation from chapter two. Stop any temporary port-forward with Ctrl+C.
kubectl config use-context docker-desktop
kubectl get namespaces
kubectl get pod,service,ingress -n defaultYou should find default and some Kubernetes system namespaces. Your existing guide-nginx resources and Traefik are in default.
That namespace let you get started without creating one yourself. Now the application gets two named environments. Traefik can stay where it is.
Create development and production
Save this as namespaces.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: development
---
apiVersion: v1
kind: Namespace
metadata:
name: productionThe --- separates two objects in one YAML file.
kubectl apply -f namespaces.yaml
kubectl get namespacesBoth new namespaces should show Active.
Open nginx-pod.yaml and nginx-service.yaml from chapter two. Remove the namespace: default line from each, leaving the names, labels, and other fields unchanged. Their metadata should now look like this:
# nginx-pod.yaml
metadata:
name: guide-nginx
labels:
app: guide-nginx# nginx-service.yaml
metadata:
name: guide-nginxThese are excerpts; keep the rest of each file. With no namespace written into them, you can choose the destination when applying them:
kubectl apply -n development -f nginx-pod.yaml -f nginx-service.yaml
kubectl apply -n production -f nginx-pod.yaml -f nginx-service.yaml
kubectl wait -n development --for=condition=Ready pod/guide-nginx --timeout=120s
kubectl wait -n production --for=condition=Ready pod/guide-nginx --timeout=120s-n is short for --namespace. The same files now describe two copies of the application. This creates new resources; it does not move the originals out of default.
Inspect each environment:
kubectl get pod,service -n development
kubectl get pod,service -n production
kubectl get pods -AEach namespace should contain a running guide-nginx Pod and its Service. -A lists Pods across all namespaces, with a NAMESPACE column to tell them apart.
If a Pod does not become ready, use kubectl describe pod guide-nginx -n development, substituting production when needed. Check the Events section for the cause.
Give each flat an address
Each environment also gets its own Ingress. Save this as nginx-ingress-development.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: guide-nginx
namespace: development
spec:
ingressClassName: traefik
rules:
- host: development.localhost
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: guide-nginx
port:
number: 80Make a copy named nginx-ingress-production.yaml and change these two values:
| Field | Development file | Production file |
|---|---|---|
metadata.namespace | development | production |
spec.rules[0].host | development.localhost | production.localhost |
Keep the rest of the copied file, including metadata.name.
kubectl apply -f nginx-ingress-development.yaml
kubectl apply -f nginx-ingress-production.yaml
kubectl get ingress -n development
kubectl get ingress -n productionBoth Ingresses are named guide-nginx, with different hosts. The backend Service must be in the same namespace as its Ingress. The development rule therefore uses the development Service; production uses its own copy. Each Service selects matching Pods within that same namespace.
The existing Traefik controller watches all namespaces by default. The chapter-two configuration leaves that default in place, so you do not need another controller.
Open both addresses:
You should see the nginx welcome page at each. Names ending in .localhost are reserved for the local machine; they are useful here because both routes enter through the same Traefik installation on your laptop.
Namespaces distinguish the Kubernetes objects. The different hostnames tell Traefik which route to use. Giving both Ingresses the same hostname and path would not give the browser a way to choose an environment.
If an address does not work
Use plain http:// and the exact hostname from the rule. A Traefik 404 usually means no matching route; check kubectl describe ingress guide-nginx -n development. If nginx is unavailable, check its Pod and Service in that namespace.
If the browser cannot resolve a .localhost name, test routing without hostname resolution. On macOS:
curl --noproxy "*" -H "Host: development.localhost" http://127.0.0.1
curl --noproxy "*" -H "Host: production.localhost" http://127.0.0.1On Windows, run the same commands with curl.exe in place of curl. Expect nginx’s welcome-page HTML. If these work, the Ingress routes work and the remaining problem is browser name resolution. If neither address connects, revisit the Traefik LoadBalancer and port-80 checks from chapter two.
Take development down
With both pages working, remove only the development Pod:
kubectl delete pod guide-nginx -n development
kubectl get pods -n productionRefresh both pages, bypassing the browser cache. Development should fail while its only Pod is gone. Production should still serve nginx, and its Pod should remain running.
Recreate development using the shared Pod file:
kubectl apply -n development -f nginx-pod.yaml
kubectl wait -n development --for=condition=Ready pod/guide-nginx --timeout=120sRefresh development again. Its Service and Ingress stayed in place, so the same address should work once nginx is ready.
I’m keeping -n explicit in these commands. It makes the destination visible before you press Enter, which is a useful habit when one of the names is production.
Leave the cluster ready
Once both new routes work again, remove the original nginx resources from default:
kubectl delete ingress guide-nginx -n default
kubectl delete service guide-nginx -n default
kubectl delete pod guide-nginx -n defaultKeep Traefik running in default. Avoid deleting everything in that namespace: it contains the controller serving both new environments.
Keep namespaces.yaml, the shared Pod and Service files, and both new Ingress files. The old nginx-ingress.yaml is superseded and can be archived. The original http://localhost route is gone; use the two environment addresses from now on.
Keep both environments running for chapter four, which introduces scaling.
