Leía el otro día que desde el principio la tendencia hacia los microservicios estaba pensada para las aplicaciones stateless, es decir, sin “memoria” del estado, donde cada interacción con la aplicación se considera independiente del resto. El ejemplo clásico de aplicación stateless es un servidor web. Así que no es de extrañar que la aplicación que siempre aparece en todo tutorial que se precie de Docker/Kubernetes es Nginx.
En el mundo real, sin embargo, la mayoría de aplicaciones requieren algún tipo de persistencia, incluso las webs más sencillas (así surgieron las cookies). Pero por el momento, Kubernetes y el almacenamiento son dos conceptos que no combinan demasiado bien, aunque funcionan perfectamente por separado.
Los contenedores (y los pods) son objetos perecederos, de usar y tirar, casi literalmente. Pero claro, esto es un problema cuando quieres almacenar datos. En Docker la solución pasa por los volúmenes, bien montados directamente desde el host en el contenedor o usando contenedores de datos. No me consta que en Kubernetes exista la opción de crear contenedores de este tipo. En cuanto a la opción de montar una carpeta del host en un contenedor deja de ser viable en cuanto tienes más de un nodo, por lo que la solución pasa por sacar el almacenamiento fuera del clúster de Kubernetes.
“Fuera” quiere decir que la aplicación guarda la información en una base de datos en una máquina -física o virtual- a la que la aplicación se conecta remotamente. Otro caso habitual es el almacenamiento de ficheros; en este caso la aplicación en Kubernetes se conecta a algún tipo de recurso compartido externo, normalmente NFS o almacenamiento en la nube (AWS EBS, AWS S3 o Google Cloud Storage). En este sentido hay dos soluciones destacadas: GlusterFS y Ceph.
Almacenamiento en Kubernetes: un proceso de dos fases
Sea como sea, el proceso de conseguir almacenamiento para un pod en Kubernetes tiene dos fases: la provisión y el consumo del.
El primer aspecto -la provisión- queda fuera del ámbito de Kubernetes y corre a cargo de un “administrador”, que define los PersistentVolumes fuera del clúster: en Gluster, un servidor NFS o en un servicio cloud.
En la segunda fase, un usuario -en el fichero de definición del Pod o cualquier otro objeto en Kubernetes que requiera almacenamiento- reclama el espacio para ser usado mediante un PersistentVolumeClaim.
En función del sistema de almacenamiento, cuando se reclamar una determinada cantidad de almacenamiento de un StorageClassName concreto que lo soporte, Kubernetes puede provisionar dinámicamente el volumen.
El hecho de que la provisión de almacenamiento quede fuera del ámbito de los tutoriales supone que la curva de aprendizaje de Kubernetes se convierte en un rampa empinada en cuanto dejas los ejemplos basados en Nginx.
Mi experiencia personal
Estoy aprovechando para mejorar mis conocimientos sobre Kubernetes siguiendo los tutoriales de la página oficial Kubernetes.io. Al intentar seguir el tutorial StatefulSet Basics se da por supuesto conocimiento con el provisionamiento de PersistentVolume. En particular, se supone que el clúster está configurado para provisionar dinámicamente PersistentVolumes, o en su defecto, que se provisionen manualmente.
This tutorial assumes that your cluster is configured to dynamically provision PersistentVolumes. If your cluster is not configured to do so, you will have to manually provision five 1 GiB volumes prior to starting this tutorial.
Desafortunadamente, no hay ningún tutorial sobre cómo crear los PersistentVolumes y en la página de Conceptos sobre PersistentVolumes no me queda claro la relación entre el PersistentVolumeClaim que se especifica en el pod de ejemplo y el apartado VolumeClaimTemplates que aparece en la definición del StatefulSet. En particular, en Claims as Volumes se indica que los pods acceden al almacenamiento usando el claim como un volumen.
The cluster finds the claim in the pod’s namespace and uses it to get the PersistentVolume backing the claim. The volume is then mounted to the host and into the pod.
Sobre cómo encuentra el clúster el claim en el espacio de nombres del pod y lo relaciona con un PersistentVolume disponible, no se dan detalles.
En la próxima entrada explicaré cómo conseguí crear correctamente los pods del StatefulSet del tutorial.
Actualización: La entrada ya está publicada: Troubleshooting: Creación de pods del tutorial ‘StatefulSet Basics’
El problema de ráiz era que creaba un PersistentVolume y un PersistentVolumeClaim manualmente (como se indica en la página de conceptos). El PVClaim se enlazaba (bound) correctamente con el PV. Sin embargo, los pods del StatefulSet no se creaban. Examinando al detalle los pods observé que la creación del pod desde el StatefulSet creaba un nuevo PVClaim que no conseguía ligarse a ningún PV disponible.
Al desconocer el mecanismo por el cual un claim “encuentra” un PV adecuado, la única vía disponible fue la de ensayo y error.
El siguiente objetivo es crear un tutorial previo al de StatefulSet Basics donde describir el proceso de creación de los PersistentVolumes para el StatefulSet del tutorial.