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 denamespaces
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).