Los namespaces (espacios de nombres) en Kubernetes permiten establecer un nivel adicional de separación entre los contenedores que comparten los recursos de un clúster.

Esto es especialmente útil cuando diferentes grupos de DevOps usan el mismo clúster y existe el riesgo potencial de colisión de nombres de los pods, etc usados por los diferentes equipos.

Los espacios de nombres también facilitan la creación de cuotas para limitar los recursos disponibles para cada namespace. Puedes considerar los espacios de nombres como clústers virtuales sobre el clúster físico de Kubernetes. De esta forma, proporcionan separación lógica entre los entornos de diferentes equipos.

Kubernetes proporciona dos namespaces por defecto: kube-system y default. A grosso modo, los objetos “de usuario” se crean en el espacio de nombres default, mientras que los de “sistema” se encuentran en kube-system.

Para ver los espacios de nombres en el clúster, ejecuta:

$ kubectl get namespaces
NAME          STATUS    AGE
default       Active    21d
kube-system   Active    21d

Puedes obtener el mismo resultado usando ns en vez de namespaces

Para comprobar la separación lógica entre los objetos de diferentes namespaces, lista los pods mediante kubectl get pods:

$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-3225377387-xdth3   1/1       Running   0          7d

Analizando al detalle el pod mediante kubectl describe pod nginx-3225377387-xdth3, observa como se encuentra en el espacio de nombres default:

$ kubectl describe pod nginx-3225377387-xdth3
Name:    nginx-3225377387-xdth3
Namespace:  default
Node:    k8s-snc/192.168.1.10
...

Compara los resultados obtenidos con los comandos anteriores con el de kubectl get pods --all-namespaces:

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                              READY     STATUS    RESTARTS   AGE
default       nginx-3225377387-xdth3            1/1       Running   0          7d
kube-system   etcd-k8s-snc                      1/1       Running   3          21d
kube-system   kube-apiserver-k8s-snc            1/1       Running   3          21d
kube-system   kube-controller-manager-k8s-snc   1/1       Running   3          21d
kube-system   kube-dns-2425271678-xbzt8         3/3       Running   12         21d
kube-system   kube-proxy-tbstt                  1/1       Running   3          21d
kube-system   kube-scheduler-k8s-snc            1/1       Running   3          21d
kube-system   weave-net-snspp                   2/2       Running   9          20d

La primera columna de la salida del comando anterior indica el espacio de nombres en el que se encuentra cada pod, en este caso.

Crea un nuevo espacio de nombres

Para crear un namespace, crea un fichero YAML como el siguiente:

apiVersion: v1
kind: Namespace
metadata:
   name: developers

El nombre del namespace debe ser compatible con una entrada válida de DNS.

Para crear el namespace, ejecuta:

$ kubectl create -f ns-developers.yaml
namespace "developers" created

Al obtener la lista de espacios de nombres disponibles, observa que ahora el nuevo namespace aparece:

$ kubectl get ns
NAME          STATUS    AGE
default       Active    21d
developers    Active    55s
kube-system   Active    21d

Observa con detalle el namespace creado:

$ kubectl describe ns developers
Name:    developers
Labels:     <none>
Annotations:   <none>
Status:     Active

No resource quota.

No resource limits.

Idealmente, el particionamiento del clúster en espacios de nombres permite repartir los recursos del clúster imponiendo cuotas, de manera que los objetos de un determinado namespace no acaparen todos los recursos disponibles.

A continuación indico cómo establecer algunos límites para el namespace (basado en Apply Resource Quotas and Limits).

Aplicando quotas al número de objetos en el namespace

Para aplicar una cuota, creamos un fichero YAML del tipo ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    persistentvolumeclaims: "2"
    services.loadbalancers: "2"
    services.nodeports: "0"

Esta cuota limita el número de:

  • volúmenes persistentes (2)
  • balanceadores de carga (2)
  • node ports (0)

Para crear la cuota, aplica el fichero YAML.

Debes especificar el namespace donde aplicar la cuota.

$ kubectl create -f quota-object-counts.yaml --namespace developers
resourcequota "object-counts" created

Comprobamos que se ha aplicado la cuota al namespace developers:

$ kubectl describe ns developers
Name:    developers
Labels:     <none>
Annotations:   <none>
Status:     Active

Resource Quotas
 Name:         object-counts
 Resource      Used  Hard
 --------      ---   ---
 persistentvolumeclaims 0  2
 services.loadbalancers 0  2
 services.nodeports  0  0

No resource limits.

Esta cuota impide la creación de más objetos de cada tipo de los especificados en la cuota (es decir, como máximo, puede haber dos load balancers en el espacio de nombres developers).

Aplicando cuotas a los recursos del namespace

Habitualmente los límites que se suelen establecer para cada espacio de nombres están enfocados a limitar los recursos de CPU y memoria del namespace.

El siguiente fichero YAML especifica un límite de 2 CPUs y 2GB de memoria. Además, especifica una limitación en cuanto a las peticiones que debe realizar un pod en este espacio de nombres. Finalmente, también se establece una limitación de como máximo, 4 pods.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
    pods: "4"

Aplicamos la nueva cuota mediante:

De nuevo, recuerda que debes especificar el namespace al que aplicar la cuota.

$ kubectl create -f quota-compute-resources.yaml --namespace developers
resourcequota "compute-resources" created

El espacio de nombres está limitado ahora de la siguiente manera:

$ kubectl describe ns developers
Name:          developers
Labels:        <none>
Annotations:   <none>
Status:     Active

Resource Quotas
 Name:            compute-resources
 Resource         Used  Hard
 --------         ---   ---
 limits.cpu       0     2
 limits.memory    0     2Gi
 pods             0     4
 requests.cpu     0     1
 requests.memory  0     1Gi

 Name:                  object-counts
 Resource               Used  Hard
 --------               ---   ---
 persistentvolumeclaims  0     2
 services.loadbalancers  0     2
 services.nodeports      0     0

No resource limits.

La limitación impuesta en las peticiones (requests) de memoria y CPU obligan a que se especifiquen límites en la definición de los recursos asignados a cada pod. En general, al crear la definición de un deployment no se especifican estos límites, lo que puede provocar algo de desconcierto.

Vamos a crear un Deployment en el namespace Developers. Aunque asignamos el deployment al namespace desde la línea de comando, en un fichero YAML usaríamos:

apiVersion: v1
kind: Service
metadata:
   name: ejemplo
   namespace: developers
spec:
   ...

Creamos un deployment:

$ kubectl run nginx --image=nginx --replicas=1 --namespace=developers
deployment "nginx" created

Todo parece ok hasta que buscamos el pod que debería crearse:

$ kubectl get pods --namespace developers
No resources found.

Analizamos el detalle del deployment

$ kubectl describe deployment nginx --namespace developers
Name:       nginx
Namespace:     developers
CreationTimestamp:   Sun, 23 Jul 2017 21:19:57 +0200
Labels:        run=nginx
Annotations:      deployment.kubernetes.io/revision=1
Selector:      run=nginx
Replicas:      1 desired | 0 updated | 0 total | 0 available | 1 unavailable
StrategyType:     RollingUpdate
MinReadySeconds:  0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:   run=nginx
  Containers:
   nginx:
    Image:     nginx
    Port:      <none>
    Environment:  <none>
    Mounts:    <none>
  Volumes:     <none>
Conditions:
  Type         Status   Reason
  ----         ------   ------
  Available       True  MinimumReplicasAvailable
  ReplicaFailure  True  FailedCreate
OldReplicaSets:      <none>
NewReplicaSet:    nginx-4217019353 (0/1 replicas created)
Events:
  FirstSeen LastSeen Count From        SubObjectPath  Type     Reason         Message
  --------- -------- ----- ----        -------------  -------- ------         -------
  2m     2m    1  deployment-controller         Normal      ScalingReplicaSet Scaled up replica set nginx-4217019353 to 1

No se ha creado el ReplicaSet. Vamos a ver porqué:

$ kubectl describe rs nginx-4217019353 --namespace developers
Name:    nginx-4217019353
Namespace:  developers
Selector:   pod-template-hash=4217019353,run=nginx
Labels:     pod-template-hash=4217019353
      run=nginx
Annotations:   deployment.kubernetes.io/desired-replicas=1
      deployment.kubernetes.io/max-replicas=2
      deployment.kubernetes.io/revision=1
Controlled By: Deployment/nginx
Replicas:   0 current / 1 desired
Pods Status:   0 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:   pod-template-hash=4217019353
      run=nginx
  Containers:
   nginx:
    Image:     nginx
    Port:      <none>
    Environment:  <none>
    Mounts:    <none>
  Volumes:     <none>
Conditions:
  Type         Status   Reason
  ----         ------   ------
  ReplicaFailure  True  FailedCreate
Events:
  FirstSeen LastSeen Count From        SubObjectPath  Type     Reason      Message
  --------- -------- ----- ----        -------------  -------- ------      -------
  4m     1m    16 replicaset-controller         Warning     FailedCreate   Error creating: pods "nginx-4217019353-" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory

El deployment crea un ReplicaSet, que a su vez intenta crear uno o más pods. Como en el Deployment no se ha especificado un límite para la CPU y memoria del pod y lo hemos exigido en las cuotas impuestas al namespace, la creación del pod falla. El mensaje de error es claro:

Error creating: pods "nginx-4217019353-" is forbidden: failed quota: compute-resources: must specify limits.cpu,limits.memory,requests.cpu,requests.memory

Si creamos el pod especificando los límites:

$ kubectl run nginx \
  --image=nginx \
  --replicas=1 \
  --requests=cpu=100m,memory=256Mi \
  --limits=cpu=200m,memory=512Mi \
  --namespace=developers

$ kubectl get pods --namespace developers
NAME                     READY     STATUS    RESTARTS   AGE
nginx-2432944439-1zqs7   1/1       Running   0          19s

Ahora, al revisar el namespace:

$ kubectl describe ns developers
Name:    developers
Labels:     <none>
Annotations:   <none>
Status:     Active

Resource Quotas
 Name:            compute-resources
 Resource         Used  Hard
 --------         ---   ---
 limits.cpu       200m  2
 limits.memory    512Mi 2Gi
 pods             1     4
 requests.cpu     100m  1
 requests.memory  256Mi 1Gi

 Name:                  object-counts
 Resource               Used  Hard
 --------               ---   ---
 persistentvolumeclaims 0     2
 services.loadbalancers 0     2
 services.nodeports     0     0

No resource limits.

Namespace y DNS

Cuando se crear un service, se crea la correspondiente entrada en el DNS. Esta entrada es de la forma <nombre-servicio>.<espacio-de-nombres>.svc.cluster.local, lo que significa que si un contenedor usa únicamente <nombre-de-servicio>, la resolución del nombre se realizará de forma local en el espacio de nombres en el que se encuentre.

Esta configuración permite usar la misma configuración entre diferentes espacios de nombres (por ejemplo Desarrollo, Integración y Producción).

Para que un contenedor pueda resolver el nombre de otro contenedor en otro namespace, debes usar el FQDN.

Borrando un namespace

AVISO: Al borrar un namespace se borran todos los objetos del espacio de nombres.

Para borrar un namespace, usa el comando delete:

$ kubectl delete ns developers
namespace "developers" deleted

El borrado del namespace es asíncrono, por lo que puedes verlo como Terminating hasta que se realiza el borrado definitivo del mismo:

$ kubectl get ns
NAME          STATUS        AGE
default       Active        21d
developers    Terminating   2h
kube-system   Active        21d

Resumen

En este artículo hemos visto qué es un Namespace y para qué sirve.

También hemos visto cómo crear un espacio de nombres, obtener información sobre él y crear objetos en el namespace.

Hemos aplicado cuotas para limitar los recursos disponibles y hemos visto cómo afecta a la creación de deployments en el espacio de nombres.

Finalmente, hemos eliminado el espacio de nombres (y todos los objetos contenidos en él).