Almacenamiento en Kubernetes. PersistentVolumen. PersistentVolumenClaims
Como hemos comentado anteriormente los pods son efímero, la información guardada en ellos no es persistente, pero es evidentemente que necesitamos que nuestras aplicaciones tengan la posibilidad de que su información no se pierda. La solución es añadir volúmenes (almacenamiento persistente) a los pods para que lo puedan utilizar los contenedores. Los volúmenes son considerados otro recurso de Kubernetes.
Definiendo volúmenes en un pod
En la definición de un pod, además de especificar los contenedores que lo van a formar, también podemos indicar los volúmenes que tendrá. Además la definición de cada contenedor tendremos que indicar los puntos de montajes de los diferentes volúmenes. Por ejemplo, el el fichero pod-nginx.yaml
podemos ver la definición de tres tipos de volúmenes en un pod:
apiVersion: v1
kind: Pod
metadata:
name: www
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /home
name: home
- mountPath: /git
name: git
readOnly: true
- mountPath: /temp
name: temp
volumes:
- name: home
hostPath:
path: /home/debian
- name: git
gitRepo:
repository: https://github.com/josedom24/kubernetes.git
- name: temp
emptyDir: {}
En la sección volumes
definimos los volúmenes disponibles y en la definición del contenedor, con la etiqueta volumeMounts
, indicamos los puntos de montajes.
Hemos definido tres volúmenes de diferente tipo:
hostPath
: Este volumen corresponde a un directorio o fichero del nodo donde se crea el pod. Como vemos se monta en el directorio/home
del contenedor. En cluster multinodos este tipo de volúmenes no son efectivos, ya que no tenemos duplicada la información en los distintos nodos y su contenido puede depender del nodo donde se cree el pod.gitRepo
: El contenido del volumen corresponde a un repositorio de github, lo vamos a montar en el el directorio/git
y como vemos lo hemos configurado de sólo lectura.emptyDir
: El contenido de este volumen, que hemos montado en el directorio/temp
se borrará al eliminar el pod. Lo utilizamos para compartir información entre los contenedores de un mismo pod.
En la documentación de Kubernetes puedes encontrar todos los tipos de volúmenes que podemos utilizar.
Veamos los volúmenes que hemos creado:
kubectl create -f pod-nginx.yaml
pod "www" created
kubectl describe pod www
...
Volumes:
home:
Type: HostPath (bare host directory volume)
Path: /home/debian
HostPathType:
git:
Type: GitRepo (a volume that is pulled from git when the pod is created)
Repository: https://github.com/josedom24/kubernetes.git
Revision:
temp:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
Accedemos al pod y vemos los contenidos de cada directorio:
kubectl exec -it www -- bash
root@www:/# cd /home
root@www:/home# ls
fichero.txt
root@www:/home# cd /git
root@www:/git# ls
kubernetes
root@www:/git# cd /temp
root@www:/temp# ls
root@www:/temp# ^C
root@www:/temp#
- El directorio
/home
tiene un fichero que esta en el directorio/home/debian
del nodo donde se ha ejecutado el pod. - El directorio
/git
tiene el contenido del repositorio github que hemos indicado. - El directorio
/temp
corresponde a un directorio vacío que podemos utilizar para compartir información entre los contenedores del pod,la información de este directorio se perderá al eliminarlo.
Compartiendo información en un pod
Veamos con un ejemplo la posibilidad de compartir información entre contenedores de un pod. En el fichero pod2-nginx.yaml
creamos un pod con dos contenedores y un volumen:
apiVersion: v1
kind: Pod
metadata:
name: two-containers
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: busybox-container
image: busybox
command:
- sleep
- "3600"
volumeMounts:
- name: shared-data
mountPath: /pod-data
Vamos a crear el pod, vamos acceder al contenedor busybox-cotainer
y vamos a escribir un index.html
, que al estar compartido por el contenedor nginx-container
podremos ver ala acceder a él.
kubectl create -f pod2-nginx.yaml
pod "two-containers" created
kubectl get pods
NAME READY STATUS RESTARTS AGE
two-containers 2/2 Running 0 10s
www 1/1 Running 0 1h
kubectl exec -it two-containers -c busybox-container -- sh
/ # cd /pod-data/
/pod-data # echo "Prueba de compartir información entre contenedores">index.html
/pod-data # exit
kubectl port-forward two-containers 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
Almacenamiento disponible en Kubernetes: PersistentVolumen
Ya hemos visto que podemos añadir almacenamiento a un pod, sin embargo habría que distinguir dos conceptos:
- El desarrollador de aplicaciones no debería conocer con profundidad las características de almacenamiento que le ofrece el cluster. Desde este punto de vista, al desarrollador le puede dar igual que tipo de volumen puede utilizar (aunque en algún caso puede ser interesante indicarlo), lo que le interesa es, por ejemplo, el tamaño y las operaciones (lectura, lectura y escritura) del almacenamiento que necesita, y obtener del cluster un almacenamiento que se ajuste a esas características. La solicitud de almacenamiento se realiza con un elemento del cluster llamado PersistentVolumenCliams.
- El administrador será el responsable de dar de alta en el cluster los distintos tipos de almacenamientos que hay disponibles, y que se representa con un recurso llamado PersistentVolumen.
Definiendo un PersistentVolumen
Un PersistentVolumen es un objeto que representa los volúmenes disponibles en el cluster. En él se van a definir los detalles del backend de almacenamiento que vamos a utilizar, el tamaño disponible, los modos de acceso, las políticas de reciclaje, etc.
Tenemos tres modos de acceso, que depende del backend que vamos a utilizar:
- ReadWriteOnce: read-write solo para un nodo (RWO)
- ReadOnlyMany: read-only para muchos nodos (ROX)
- ReadWriteMany: read-write para muchos nodos (RWX)
Las políticas de reciclaje de volúmenes también depende del backend y son:
- Retain: Reclamación manual
- Recycle: Reutilizar contenido
- Delete: Borrar contenido
Creando un PersistentVolumen con NFS
Vamos a instalar en el master del cluster (lo podríamos tener en cualquier otro servidor) un servidor NFS para compartir directorios en los nodos del cluster.
Configuración en el master
En el master como root, ejecutamos:
apt install nfs-kernel-server
mkdir -p /var/shared
Y en el fichero /etc/exports
declaramos el directorio que vamos a exportar:
/var/shared 10.0.0.0/24(rw,sync,no_root_squash,no_all_squash)
Nota: La red 10.0.0.0/24 es la red interna donde se encuentra el master y los nodos del cluster.
Por último reiniciamos el servicio:
systemctl restart nfs-kernel-server.service
Y comprobamos los directorios exportados:
showmount -e 127.0.0.1
Export list for 127.0.0.1:
/var/shared 10.0.0.0/24
Configuración en los nodos
En cada uno de los nodos del cluster vamos a montar el directorio compartido, para ello:
apt install nfs-common
Y comprobamos los directorios exportados en el master:
showmount -e 10.0.0.4
Export list for 10.0.0.4:
/var/shared 10.0.0.0/24
Y ya podemos montarlo:
mount -t nfs4 10.0.0.4:/var/shared /var/data
Creación del volumen en Kubernetes
Ya podemos crear el volumen utilizando el objeto PersistentVolumen. Lo definimos en el fichero nfs-pv.yaml
:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /var/shared
server: 10.0.0.4
Y lo creamos y vemos el recurso:
kubectl create -f nfs-pv.yaml
persistentvolume "nfs-pv" created
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv 5Gi RWX Recycle Available 10s
El tipo de volumen disponible lo vamos a referenciar con su nombre (nfs-pv
), tiene 5Gb de capacidad, estamos utilizando NFS, el modo de acceso es RWX y su política de reciclaje es de reutilización del contenido.
Solicitud de almacenamiento en Kubernetes: PersistentVolumenClaims
A continuación si nuestro pod necesita un volumen, necesitamos hacer una solicitud de almacenamiento usando un obketo del tipo PersistentVolumenCliams.
Cuando creamos un PersistentVolumenCliams, se asignará un PersistentVolumen que se ajuste a la petición. Está asignación se puede configurar de dos maneras distintas:
- Estática: Primero se crea todos los PersistentVolumenCliams por parte del administrador, que se irán asignando conforme se vayan creando los PersistentVolumen.
- Dinámica: En este caso necesitamos un “provisionador” de almacenamiento (para cada uno de los backend), de tal manera que cada vez que se cree un PersistentVolumenClaim, se creará bajo demanda un PersistentVolumen que se ajuste a las características seleccionadas.
Creación de PersistentVolumenClaim
Siguiendo con el ejercicio anterior vamos a crear una solicitud de almacenamiento del volumen creado anteriormente con NFS. Definimos el objeto en el fichero nfs-pvc.yaml
:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
Creamos el recurso y obtenemos los recursos que tenemos a nuestra disposición:
kubectl create -f nfs-pvc.yaml
persistentvolumeclaim "nfs-pvc" created
kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nfs-pv 5Gi RWX Recycle Bound default/nfs-pvc 14m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/nfs-pvc Bound nfs-pv 5Gi RWX 15s
Como podemos observar al crear el pvc se busca del conjunto de pv uno que cumpla sus requerimientos, y se asocian (status bound) por lo tanto el tamaño indicado en el pvc es el valor mínimo de tamaño que se necesita, pero el tamaño real será el mismo que el del pv asociado.
Si queremos añadir un volumen a un pod a partir de esta solicitud, podemos usar la definición del fichero pod-nginx-pvc
:
apiVersion: v1
kind: Pod
metadata:
name: www-vol
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-vol
volumes:
- name: nfs-vol
persistentVolumeClaim:
claimName: nfs-pvc
Lo creamos y accedemos a él:
kubectl create -f pod-nginx-pvc.yaml
pod "www-vol" created
kubectl port-forward www-vol 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Evidentemente al montar el directorio DocumentRoot del servidor (/usr/share/nginx/html
) en el volumen NFS, no tiene index.html
, podemos crear uno en el directorio compartido del master y estará disponible en todos los nodos:
echo "It works..." | ssh debian@172.22.201.15 'cat >> /var/shared/index.html'
kubectl port-forward www-vol 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Si escalamos el pod no tendríamos ningún problema ya que todos los nodos del cluster comparten el mismo directorio referenciado por el volumen. Además el contenido del volumen es persistente, y aunque eliminemos el pod, la información no se pierde:
kubectl delete pod www-vol
pod "www-vol" deleted
kubectl create -f pod-nginx-pvc.yaml
pod "www-vol" created
kubectl port-forward www-vol 8080:80
Frwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Y desde otro terminal:
curl http://localhost:8080
It works...
Además podemos comprobar cómo se ha montado el volumen en el contenedor:
kubectl exec -it www-vol -- bash
root@www-vol:/# df -h
...
10.0.0.4:/var/shared 20G 4.8G 15G 26% /usr/share/nginx/html
...
Índice de entradas sobre Kubernetes
Este artículo corresponde a una serie de entradas donde he hablado sobre Kubernetes:
- Instalación de kubernetes con kubeadm
- Desplegando una aplicación en Kubernetes
- Recursos de Kubernetes: Pods
- Recursos de Kubernetes: ReplicaSet
- Recursos de Kubernetes: Deployment
- Kubernetes: Desplegando la aplicación Mediawiki
- Kubernetes: Desplegando la aplicación GuestBook (Parte 1)
- Recursos de Kubernetes: Services
- El servicio DNS en Kubernetes
- Kubernetes: Desplegando la aplicación GuestBook (Parte 2)
- Kubernetes: Desplegando la aplicación LetsChat
- Recursos de Kubernetes: Ingress
- Accediendo a nuestras aplicaciones Kubernetes con Ingress
- Recursos de Kubernetes: Namespaces
- Kubernetes. Configurando nuestras aplicaciones: variables de entornos, ConfigMap, Secrets
- Kubernetes. Desplegando WordPress con MariaDB
- Almacenamiento en Kubernetes. PersistentVolumen. PersistentVolumenClaims
- Kubernetes. Desplegando WordPress con MariaDB con almacenamiento persistente
- Integración de Kubernetes con OpenStack
Comments
Ivan Ivox
Hola!, muchas gracias desde ya por el tutorial!!. Una consulta.. segui al pie de la letra el tuturial pero tengo problemas en storageClass.. como tenes configurado?. ya que a la hora de montar el claim queda en pendiente porque no tengo definido el storageClass. Saludos!
Andy
Hola Profe[he sido alumno virtual tuyo]. Muchisimas gracias por compartir el contenido de K8s, me ha salvado la parte de atras de mi cuerpo!!!. Un saludo
Leave a Comment
Your email address will not be published. Required fields are marked *