Post original escrito por Eduard Tomás en campusMVP
Hace poco os explicaba en un artículo qué es Kubernetes, cuál es su arquitectura y su funcionamiento básico. Léelo antes de continuar si no tienes claros estos conceptos.
En esta ocasión voy a continuar con aquella explicación analizando cómo es el ciclo de vida de una aplicación que está desplegada con Kubernetes.
Lo primero que debemos tener claro es que en Kubernetes nunca se ejecutan contenedores de forma directa. Es decir, no se puede desplegar un contenedor o parar un contenedor. En su lugar se opera con lo que se llaman pods.
Un pod es la unidad mínima de despliegue y operación en Kubernetes. Es decir, desplegamos pods, escalamos pods y son los pods los que se ejecutan en los distintos nodos.
La relación entre pod y contenedor no tiene por qué ser de uno a uno: es decir, en un único pod se pueden ejecutar varios contenedores. Solo recuerda que en este caso se escalarán y desplegarán de forma conjunta (ya que se escala o despliega todo el pod).
Aunque es posible, teóricamente, desplegar en un pod una web y su API en dos contenedores, eso no es lo que suele hacer. En la gran mayoría de casos las relaciones entre pod y contenedor son, efectivamente, de uno a uno. Pero que un pod pueda ejecutar varios contenedores habilita ciertos escenarios avanzados como p. ej. tener un contenedor que ofrezca servicios a otro contenedor (como podría ser hacer de proxy, de logger o similar).
Es importante tener presente que todos los contenedores que se ejecutan en un mismo pod comparten espacio de red. Es decir, se comunican entre ellos usando localhost y no pueden abrir los mismos puertos. También comparten el espacio IPC por lo que pueden usar cualquier técnica de IPC (como semáforos) para comunicarse entre ellos.
Los distintos estados de un pod
Un pod pasa por varias fases a lo largo de su ciclo de vida:
- Pending : el pod ha sido creado, pero aún no se han creado sus contenedores ni se ha decidido qué nodo lo va a ejecutar.
- Running : los contenedores del pod han sido creados y el pod ya se está ejecutando en un nodo. Al menos un contenedor del pod se está ejecutando o está en proceso de creación.
Esto es importante : que un pod esté en estado running no significa que sus contenedores estén ejecutándose. P. ej. se pueden estar iniciando o reiniciando.
- Succeeded : los contenedores del pod han finalizado (todos) su ejecución de forma satisfactoria.
- Failed : los contenedores del pod han finalizado (todos) su ejecución y al menos uno de ellos lo ha hecho con error.
- Unknown : el estado del pod no se puede saber. Esto suele indicar un error de comunicación con el nodo minion que está ejecutando el pod.
- CrashLoopBackOff : el pod está corriendo, pero uno de sus contenedores se está reiniciando debido a que ha terminado (generalmente de forma errónea).
Es importante separar el ciclo de vida del pod del ciclo de vida de sus contenedores. Un pod puede estar en la fase de running pero uno de sus contenedores puede no estar ejecutándose. Cuando un pod _está en fase de _CrashLoopBackOff eso significa que uno de los contenedores está en proceso de reinicio, pero otros contenedores del pod pueden estar ejecutándose.
Muchas veces se dice que Kubernetes reinicia un pod pero esa expresión no es estrictamente cierta: Kubernetes reinicia contenedores de un pod, pero el pod en sí mismo sigue ejecutándose durante todo este tiempo. Que Kubernetes decida reiniciar o no los contenedores de un pod (y en qué casos) depende de la configuración del pod. Es decir, para ciertos pods no hay posibilidad de que entren nunca en fase de Succeeded ya que su misión es estar ejecutándose permanentemente (p. ej. una API), por lo que si un contenedor termina será reiniciado por Kubernetes.
Los pods se consideran "objetos transitorios", en el sentido de que hay determinados eventos del clúster a los cuales no sobreviven. P. ej. si el nodo minion se cae, todos los pods que se estén ejecutándose en él se caen también. Los pods no se mueven de nodo ni se reinician (sí se reinician sus contenedores como se ha comentado antes).
En general no se suele crear nunca pods de forma directa , en su lugar usamos lo que llamamos un controlador. Un controlador es el responsable de garantizar que en todo momento se cumple una cierta condición al respecto de los pods que controla, como que en todo momento haya N instancias de este pod.
Hago aquí un inciso de nomenclatura: cuando digo "N instancias de un pod" me refiero a N pods _idénticos (ejecutando los mismos contenedores y con la misma configuración) pero son N _pods.
Si estamos usando un controlador y deseamos tener en todo momento 2 instancias de un determinado pod y uno de los dos pods se muere (porque se muere su nodo p. ej.), el controlador creará otro pod para asegurar de que siga habiendo dos instancias.
Cuando tenemos N instancias, los N pods se consideran todos "idénticos". Si por algún motivo (p. ej. debe desescalarse) debe eliminarse algún pod, el controlador eliminará uno cualquiera, sin preferencias. Eso encaja con el modelo stateless de aplicación en el que los contenedores encajan perfectamente. Es decir, como desarrollador debes asegurar que tus contenedores sean stateless, ya que en cualquier momento Kubernetes puede decidir eliminar el pod (y por lo tanto el contenedor) y recrearlo en otra parte.
Este modelo stateless no siempre aplica: a veces, si tenemos N instancias de un pod hay alguno que juega un rol especial y por lo tanto se requiere que estos tengan identidad. Un ejemplo es una configuración de base de datos en modo maestro/esclavo. Si uno de los pods se cae, el nuevo que se cree debe ser del mismo tipo, y no solo eso, sino que debe recibir el estado que tenía el pod caído. Kubernetes es capaz de gestionar estos casos mediante un controlador específico (llamado StatefulSet
), pero lo ideal es que nuestras aplicaciones sean stateless.
Post original escrito por Eduard Tomás en campusMVP
Top comments (0)