Cómo trabajar con el objeto Deployment en Kubernetes
En uno de los primeros artículos que escribí sobre Kubernetes te dije, sin entrar en detalle, que la forma recomendada de desplegar los pods sobre tu clúster era a través del objeto Deployment, debido a que este gestiona los objetos ReplicaSet por nosotros y nos ayuda no solo en el despliegue sino también es posteriores actualizaciones. En este artículo quiero contarte todo lo que deberías saber sobre este objeto.
Cómo funciona un Deployment
Los deployments se definen como objetos de alto nivel que te ayudan no solo en el despliegue de tus aplicaciones en Kubernetes sino que también te permiten hacer actualizaciones de forma declarativa. En versiones anteriores, esta tarea se realizaba gestionando directamente el objeto ReplicationController, de forma más manual, pero ahora se ha delegado en estos y los ReplicaSets, de los que ya te hablé en un articulo anterior.
Como siempre, vamos a verlo con un ejemplo que siempre se entiende mejor . En este caso voy a desplegar un servidor web con tres replicas, inicialmente de Nginx:
Además del Deployment voy a crear un objeto de tipo Service para poder acceder al servidor web desde fuera del clúster, y ver de forma sencilla lo que va ocurriendo con él mismo durante el artículo.
Por lo tanto, cuando despliegues este manifiesto tendrás 6 objetos nuevos en tu entorno: un Deployment, un ReplicaSet, tres pods y un servicio de tipo LoadBalancer.
En el comando anterior he añadido el parámetro –record, el cual verás para qué sirve más adelante
En esta primera fase la salida esperada será la siguiente:
Puedes comprobar todos los objetos creados a través del siguiente comando:
y el resultado será parecido a este:
En mi caso, si accedo a través del navegador a la IP 52.155.177.3 veré el servidor de Nginx funcionando.
Ahora bien, cuando se dice que gracias a los deployments tenemos una actualización de manera declarativa ¿esto qué significa? Pues básicamente que podemos hacer modificaciones en el YAML que define el despliegue, ya sea volviendo a enviar todo él o a través de kubectl patch/edit, y el controlador de los deployments determinará si dicho cambio conlleva una actualización de los pods (esto es, cuando se ha modificado la sección template del pod dentro del objeto Deployment). Para comprobar esto, cambia la imagen nginx utilizada en la creación por mcr.microsoft.com/dotnet/core/samples:aspnetapp:
y aplica los nuevos cambios:
Al tratarse del mismo objeto Deployment creado anteriormente, el resultado ya no será created sino configured esta vez:
Como es la segunda vez que mandas información acerca del mismo Deployment, si vuelves a comprobar el listado de ReplicaSets comprobarás que ahora tienes dos para el mismo despliegue:
Pero sigues teniendo el mismo número de pods:
En el artículo sobre los ReplicaSet expliqué que estos crean los pods con el nombre del ReplicaSet (el cual está compuesto por el nombre del despliegue más un literal aleatorio) y un literal aleatorio por cada pod. Si comparas el nombre que tenían tus pods la primera vez y el nombre que tienen ahora podemos deducir que el ReplicaSet que está gestionando los pods no es el mismo que al principio, sino el nuevo que vemos con el literal 5b5f4f975. Esto es así porque el Deployment utiliza un nuevo ReplicaSet cada vez que haces una actualización, el cual tiene la versión actualizada de la plantilla que tiene que utilizar para los pods.
¿Pero por qué se queda el anterior con cero pods? Muy sencillo: en el caso de que necesitemos dar marcha atrás, porque la actualización no esté funcionando correctamente por ejemplo, el Deployment volverá a utilizar dicho ReplicaSet para recrear los pods con la configuración anterior. Puedes comprobarlo utilizando el comando rollout undo:
Kubernetes responderá con el siguiente mensaje:
y podrás ver que vuelves a tener un Nginx funcionando, en lugar de la aplicación de ejemplo de ASP.NET Core. De hecho, si vuelves a revisar los ReplicaSets comprobarás que ahora el que controla los pods es, de nuevo, el primero que se generó:
Llegados a este punto es posible que te preguntes ¿entonces voy a tener tantos ReplicaSet como todas las actualizaciones que esta aplicación haya tenido en su vida? La respuesta es no Por defecto sólo se guardan las 10 últimas revisiones, aunque es configurable a través de revisionHistoryLimit, dentro del apartado spec del deployment:
Por otro lado, si quisieras ir a una revisión en concreto podrías hacerlo añadiendo al comando el parámetro –to-revision=N, siempre dentro de las revisiones disponibles:
Otro dato importante es poder ver el estado en el que se encuentra una actualización. En este ejemplo los cambios son demasiado rápidos, por lo que voy a ralentizar el tiempo que tardan los pods en estar listos.
El resultado de esta acción será el siguiente:
Nota: Este cambio en el deployment no hará que se actualicen los pods, al estar fuera del apartado template.
Ahora voy a actualizar de nuevo el objeto Deployment, pero esta vez a través de set image:
El resultado será que la imagen ha sido actualizada:
En este caso, sí generará un redespliegue de pods con la nueva imagen. Para conocer el estado de esta actualización puedes utilizar rollout status:
Este último se quedará a la espera hasta que finalice la actualización, mostrando el avance de la misma:
Una vez que termine el proceso, comprobarás que ahora lo que tienes es un servidor Apache ejecutándose en tus pods
Para ver el histórico de las últimas actualizaciones que has llevado a cabo sobre tu despliegue inicial utiliza el comando rollout history:
El resultado debería de ser como el que sigue:
Gracias a el parámetro –record puedes ver qué comando se lanzó para este deployment cada vez que se actualizó, de tal manera que será más sencillo poder determinar a qué versión tienes que ir si fuera necesario. Sin embargo, lo habitual es que utilicemos el mismo nombre de archivo, si estamos trabajando con un despliegue automatizado, pero este tendrá diferentes versiones del contenido. Si es el caso, podemos ver el detalle de cada actualización indicando la revisión:
De hecho, en la misma podrás ver que solo se muestra el contenido de la plantilla a utilizar para los pods:
deployment.extensions/web-deployment with revision #4
Diferentes estrategias de despliegue
Por último, es importante saber que los Deployments nos permiten llevar a cabo la actualización a través de dos estrategias. Por defecto se utiliza la llamada RollingUpdate la cual va remplazando los pods, evitando la pérdida de servicio, basándose en los valores maxSurge (cuantos pods puede haber por encima del valor deseado) y maxUnavailable (cuántos como máximo pueden no estar disponibles). Por defecto, ambas propiedades tienen un valor del 25%, aunque se puede especificar un número exacto en lugar de un porcentaje. Debes tener en cuenta que para poder utilizar esta estrategia tu aplicación debe de ser capaz de convivir con versiones diferentes.
Por otro lado, también es posible utilizar el modo Recreate que lo que haces es eliminar primero todos los pods antiguos y después crea los nuevos. En este caso sí que hay pérdida de servicio.
Esta configuración se hace a través del apartado strategy:
Escalar un deployment
Por último, si solo quieres aumentar o disminuir el número de réplicas puedes hacerlo a través de scale:
En un próximo artículo te mostraré cómo hacer esto de manera automática.
¡Saludos!