In the last part, we set up Ingress as a smart front door for our cluster, routing traffic to different microservices. Our cluster is becoming more powerful, but it's also becoming more crowded.
Right now, our cluster is like one giant, open-plan office. Every team and every application works in the same shared space. The Deployment for the api-service
lives right next to the webapp
Deployment. This creates two significant problems:
- Organizational Chaos: What if two different teams both want to create a
ConfigMap
nameddatabase-config
? The names will collide, and one will overwrite the other. There's no isolation. - Resource Contention: What if one application has a memory leak and starts consuming all the available CPU and memory on a Node? It becomes a "noisy neighbor," starving every other application on that machine and potentially crashing it.
To solve this, Kubernetes provides tools to partition our cluster logically and enforce fair resource usage.
Namespaces: Creating Virtual Walls
A Namespace is a way to divide your cluster resources into logically isolated groups. It’s like creating virtual folders or "sub-clusters" within your main cluster.
- Analogy: If the cluster is a large office building, a Namespace is a specific floor or a set of rooms dedicated to a single team (e.g., "Engineering," "Marketing").
Using Namespaces, the "Engineering" team can create a service named database
and the "Marketing" team can also create a service named database
. Because they are in different Namespaces, their names won't collide.
By default, every Kubernetes cluster has a few starting Namespaces, including default
(where our resources have lived so far) and kube-system
(where cluster-critical components run). Let's create our own.
Create a file named namespace.yaml
:
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: team-alpha
Now apply it:
kubectl apply -f namespace.yaml
To create resources inside this Namespace, you add a namespace: team-alpha
field to their metadata
. When using kubectl
, you must specify the namespace using the -n
or --namespace
flag. If you don't, kubectl
assumes you mean the default
namespace.
For example: kubectl get pods -n team-alpha
.
Requests and Limits: Enforcing Fair Play
Namespaces provide organizational isolation, but not resource isolation. To solve the "noisy neighbor" problem, we define resource Requests and Limits for our containers.
-
Request: This is the amount of resources that Kubernetes guarantees for your container. The Kubernetes scheduler uses this value to find a Node with enough available capacity to place your Pod.
- Analogy: A
Request
is like booking a meeting room for 2 hours. That time is reserved for you, and no one else can claim it.
- Analogy: A
-
Limit: This is the maximum amount of resources a container is allowed to use. Kubernetes enforces this limit. If a container tries to use more memory than its limit, it will be terminated (
OOMKilled
). If it tries to use more CPU, it will be throttled.- Analogy: A
Limit
is the physical size of the meeting room. You can't expand past its walls, no matter how many people you try to fit inside.
- Analogy: A
CPU is measured in "milliCPUs" or "millicores." 100m
is 0.1 of a CPU core. 1000m
is 1 full core. Memory is measured in bytes, typically using suffixes like Mi
(Mebibytes) or Gi
(Gibibytes).
Hands-On: A Well-Behaved Deployment
Let's create a new, well-behaved deployment that lives in its own namespace and has clear resource constraints.
Create a file named resource-deployment.yaml
:
# resource-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: constrained-app
namespace: team-alpha # We are deploying this into our new namespace
spec:
replicas: 1
selector:
matchLabels:
app: constrained-app
template:
metadata:
labels:
app: constrained-app
spec:
containers:
- name: app
image: nginx
# This is the new resources section
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "250m"
Now apply it:
kubectl apply -f resource-deployment.yaml
Notice we didn't need the -n
flag here because we specified the namespace inside the manifest's metadata.
Let's verify it. Remember to use the -n
flag to look inside the team-alpha
namespace.
kubectl get pods -n team-alpha
You'll see the constrained-app
Pod running. Now, let's inspect it to see if our resource constraints were applied.
# Get your Pod name first from the command above
kubectl describe pod <your-pod-name> -n team-alpha
Scroll down in the output, and you'll see a section that clearly lists the container's resource Requests and Limits, confirming that Kubernetes is now managing this Pod's resources.
Requests:
cpu: 100m
memory: 64Mi
Limits:
cpu: 250m
memory: 128Mi
What's Next
We can now build a well-organized, multi-tenant cluster where applications are isolated and play nicely with their neighbors. By setting Requests and Limits, we've made our cluster more stable and predictable.
But as our deployments become more complex, the chances of something going wrong increase. What happens when a Pod won't start? What do you do when you see CrashLoopBackOff
or ImagePullBackOff
? Knowing how to diagnose and fix these problems is a critical skill.
In the next part, we will become cluster detectives. We'll explore the essential toolkit for debugging applications in Kubernetes, learning how to use logs, events, and other tools to find the root cause of any problem.
Top comments (0)