Article 17 – Namespaces: Logical Multi-Tenancy and Resource Quotas

The Security and Resource Risks of a Flat Cluster

If you run different environments (like Development, Testing, and Production) on a single cluster without boundaries, you face operational risks. For instance, developers might choose conflicting resource names, preventing workloads from launching.

Additionally, you risk accidental resource deletion. A team member attempting to clean up a test pod might run a delete command inside the wrong context and remove a production service.

There is also the threat of resource starvation. A memory leak or a runaway container in a development test environment could consume all host RAM, starving your production systems of resources.

Virtual Isolation and Quotas

Kubernetes addresses this risk using Namespaces, which act as virtual partitions inside your cluster.

By default, Kubernetes initializes with four namespaces:
* default: The home for resources created without an explicit namespace.
* kube-system: Reserved for internal control plane resources.
* kube-public: Accessible by all users, commonly used for public cluster bootstrap details.
* kube-node-lease: Holds heartbeat lease objects for active nodes.

To manage resource usage within these boundaries, you can configure a ResourceQuota to limit the total CPU, memory, or object counts allowed inside a namespace. You can also configure a LimitRange to apply default resource requests and limits to individual containers if a developer forgets to specify them:
* Default Request: The baseline memory/CPU allocated to a container when it launches.
* Default Limit: The maximum limit the container can consume before being throttled (CPU) or terminated with an Out of Memory (OOM) error (memory).

For service discovery, pods within the same namespace resolve services using short names (e.g., db-service). To access a service in a different namespace, append the namespace name to resolve the domain:
<service-name>.<namespace-name>.svc.cluster.local

Creating and Restricting a Namespace

Let’s create a namespace and apply resource restrictions. First, create the namespace:

$ kubectl create namespace engineering
namespace/engineering created

Now, let’s configure a quota to limit the namespace to a maximum of two pods, two CPUs, and 4GB of RAM. Save the following configuration as quota.yaml:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: engineering
spec:
  hard:
    pods: "2"
    requests.cpu: "1"
    requests.memory: 2Gi
    limits.cpu: "2"
    limits.memory: 4Gi

Apply the quota:

$ kubectl apply -f quota.yaml
resourcequota/team-quota created

Inspect the quota status inside the namespace:

$ kubectl get resourcequota -n engineering
NAME         AGE   REQUEST         LIMIT
team-quota   10s   pods: 0/2, ...  

If you attempt to launch a third pod in the engineering namespace, the admission controller will block the request and return a quota exceeded error.

To configure default resource limits for containers in this namespace, save the following as limitrange.yaml:

apiVersion: v1
kind: LimitRange
metadata:
  name: container-limits
  namespace: engineering
spec:
  limits:
  - default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 256Mi
      cpu: 250m
    type: Container

Apply the LimitRange:

$ kubectl apply -f limitrange.yaml
limitrange/container-limits created

To avoid typing the -n engineering flag with every command, you can change your active namespace context:

$ CONTEXT=$(kubectl config current-context)
$ kubectl config set-context $CONTEXT --namespace=engineering
Context "minikube" modified.

Now, running commands like kubectl get pods will automatically target the engineering namespace. To switch back to the default namespace context, run:

$ kubectl config set-context $CONTEXT --namespace=default