Este artículo es parte de una serie de artículos donde te explicaremos de qué va Kubernetes, desde lo más sencillo hasta lo más geek: desplegar una aplicación en un cluster de producción. Antes de entrar en materia densa, es necesario entender por qué la necesidad de Kubernetes, y es que años atrás la mayoría de las aplicaciones eran enormes monolitos de código que tenían un ciclo de actualización muy lento debido a esta misma arquitectura.
Este artículo es parte de una serie de artículos donde te explicaremos de qué va Kubernetes, desde lo más sencillo hasta lo más geek: desplegar una aplicación en un cluster de producción. Antes de entrar en materia densa, es necesario entender por qué la necesidad de Kubernetes, y es que años atrás la mayoría de las aplicaciones eran enormes monolitos de código que tenían un ciclo de actualización muy lento debido a esta misma arquitectura. Digo años atrás queriendo sonar optimista, pero hoy en día existen muchísimas aplicaciones así aún en producción que son dolores de cabeza tanto para los desarrolladores como para los equipos de Ops, porque a la hora de una actualización muchas cosas pueden salir mal, el redespliegue puede tomar horas y si llega a fallar el hardware donde estas aplicaciones están desplegadas, les deseo mucha suerte a todos.
Hoy en día estos bloques anticuados de código han sido lentamente fraccionados en componentes independientes mucho más pequeños llamados microservicios; y lo interesante de esta nueva manera de desarrollar aplicaciones es que estos al estar desacoplados del resto, pueden ser desarrollados, actualizados y escalados individualmente. Este enfoque abre la puerta a un proceso de desarrollo mucho más fácil, rápido y seguro - pudiendo así cambiar componentes rápidamente y en ciclos mucho más cortos dependiendo de los requerimientos del negocio. Migrar al enfoque de microservicios es tomar una aplicación monolítica que corre generalmente en un servidor con mucha capacidad computacional, y fraccionarla en procesos más pequeños que corren en infraestructura usualmente separada y se comunican usando protocolos síncronos como HTTP, a través del cual exponen RESTful APIs:
De igual manera seguimos teniendo un problema, si estamos hablando de un par de microservicios, mantener este proceso de forma manual es algo más sencillo que con la anticuada arquitectura monolítica, pero, ¿qué pasa si estamos hablando de cientos de microservicios? Hacer este proceso manualmente es bastante complejo, y muchas cosas pueden fallar. Necesitamos automatizar - lo que incluye autoescalamiento de esos componentes en los servidores dependiendo de la demanda, configuración automática, supervisión y manejo de fallas y si es posible, despliegue automático. Aquí es donde Kubernetes viene a cambiarnos el juego a todos, y a hacernos la vida más sencilla.
Entendiendo qué es Kubernetes y por qué todos lo aman
Siempre me gusta comparar el hecho de desarrollar y desplegar aplicaciones monolíticas con andar a pie, y usar Kubernetes como andar en un Tesla. La diferencia es así de abismal. Este sistema permite a los desarrolladores desplegar sus aplicaciones ellos mismos o de forma automática y tan frecuentemente como ellos lo deseen, sin necesidad de requerir de la ayuda del equipo de Infraestructura. Eso por un lado, ya es una ventaja enorme. Por otro lado, ayuda al equipo de Infra a monitorear las fallas de hardware y en el caso de que este falle, se hace muchísimo más fácil (porque es automático) el proceso de migrar esas aplicaciones a hardware sano; por ende, el trabajo de ellos se resume a supervisar Kubernetes en vez de ocuparse de la infraestructura y las aplicaciones que corren en esta.
Quizás te estés preguntando cómo es que Kubernetes es capaz de dejar a la gente sin nada que hacer. Yo también me hice la misma pregunta, y no es magia. Kubernetes es una plataforma open source para orquestamiento de containers desarrollada por Google originalmente, pero que hoy en día es mantenida por la Cloud Native Computing Foundation, y lo que esencialmente hace es que abstrae la infraestructura de hardware y expone un recurso computacional enorme donde se pueden desplegar y ejecutar aplicaciones sin necesidad de conocer los servidores que estén debajo, donde todos los procesos son estandarizados y uniformes. Esto es que si una aplicación corre en un ambiente de Kubernetes, debe correr en cualquier otro - por ejemplo, si un desarrollador tiene instalado en su laptop un ambiente controlado (y muchísimo más pequeño, obviamente) de Kubernetes y sus microservicios funcionan en local, ese mismo grupo de microservicios debe funcionar exactamente igual en un cluster más grande, así que yo como desarrollador ya no tengo la excusa de “ah, pero es que en mi laptop funciona”, porque ya no me van a creer.
Con cada vez más compañías aceptando el modelo de Kubernetes como la mejor manera de ejecutar aplicaciones, de a poco se ha ido convirtiendo en el estándar de la industria para correr aplicaciones distribuidas en la nube, pero también en infraestructura on-premise.
Beneficios de un ecosistema basado en Kubernetes
Independientemente de cuántos componentes se estén desarrollando y desplegando, uno de los problemas más grandes que los desarrolladores y los equipos de Ops tienen que enfrentar siempre es tener que lidiar con diferentes ambientes en los que corren las aplicaciones, y no solo diferencias entre ambientes de desarrollo y producción, sino la inconsistencia entre las mismas máquinas de producción, sus diferentes sistemas operativos, las librerías instaladas en éstos, arquitecturas de procesador, arquitecturas de redes, entre otras. En el mejor de los escenarios, los ingenieros de infraestructura tienen que preocuparse porque una sola aplicación tenga todo lo que necesite para funcionar en un servidor, pero, ¿qué pasa si en ese mismo servidor tienen que coexistir múltiples aplicaciones? De nuevo, más problemas. Para reducir el número de problemas que se enfrentan en producción, lo ideal es mantener un ambiente consistente donde cualquier aplicación pueda coexistir, y que este ambiente se mantenga sea en desarrollo o producción. Kubernetes permite solventar este problema, al involucrar más a los desarrolladores en el proceso de despliegue de las aplicaciones porque les permite entender qué problemas se enfrentan en producción y así atacarlos en una etapa temprana del desarrollo y no cuando la aplicación está ya en producción. Por otro lado, Kubernetes permite reducir la brecha de tiempo entre cada release de las aplicaciones, por lo que una aplicación de cualquier naturaleza puede incluirse en sprints ágiles más cortos y atacar fallas de una manera más eficaz.
Kubernetes permite que los desarrolladores y los sysadmins se dediquen a lo que mejor saben hacer. Aun y cuando ambos trabajan para alcanzar el mismo objetivo de hacer funcionar un software para clientes de manera exitosa, las tareas no son las mismas. Los desarrolladores prefieren enfocarse en desarrollar nuevas features para mejorar la experiencia del usuario y hacer aplicaciones cada vez más funcionales, en cambio los sysadmins se preocupan por la infraestructura de hardware que subyace bajo esas aplicaciones, la seguridad, uso de los recursos disponibles y otros aspectos que no son prioridad para el developer. Adoptar Kubernetes es dibujar una línea entre estos dos roles, y que el sysadmin no se preocupe por la compatibilidad de dependencias de todas las aplicaciones en un servidor, o por escalar manualmente una aplicación, y que los desarrolladores no tengan que acudir a otro equipo cada vez que quieren desplegar una feature nueva. Esta última razón hace incluso que muchas compañías renten clusters en proveedores Cloud como AWS, GCP o Azure y se olviden del rol del ingeniero de infraestructura porque ya deja de ser necesario y les permite reducir costos.
La base de Kubernetes: los containers y su magia
Hace unas líneas mencioné que Kubernetes orquestaba containers, y la verdad es que hoy en día todo es un container, tanto así que se acostumbra a entregar una aplicación con su archivo que define cómo encapsular ésta en uno de ellos. Kubernetes usa tecnologías de containers Linux para proveer aislamiento de las aplicaciones, así que antes de adentrarnos en Kubernetes como tal, prefiero que comencemos por destacar lo básico de los containers.
Entendiendo qué son los containers
Es normal que diferentes componentes de software corriendo en el mismo host requieran diferentes versiones de librerías y que entre ellas existan conflictos, es por ello que cuando una aplicación está compuesta de un pequeño número de grandes componentes, es normal desplegar cada uno de ellos en su propia VM y así se logre aislar cada bloque. Hasta ahí es entendible esta estrategia, pero, ¿cómo se hace si esos componentes se vuelven mucho más pequeños y la cantidad crece? No se le puede asignar una VM a cada componente si no se quiere desperdiciar recursos y se quiere mantener los costos de hardware bajos. El despliegue de VMs significa que cada una se debe configurar y mantener de manera separada, y que seguramente se tenga que contratar a un sysadmin que las opere.
En vez de usar máquinas virtuales para aislar los ambientes de cada microservicio, se pueden implementar containers Linux. Estos permiten desplegar múltiples servicios en la misma máquina host y no sólo exponerlos entre ellos, sino también aislar sus dependencias y requerimientos para lograr el funcionamiento ideal - de igual manera que como se lograría con VMs pero con uso de muchísimos menos recursos. Cabe destacar que los containers no son VMs, porque los procesos que corren dentro de estos también corren en el sistema operativo de la host machine, pero desde el punto de vista del container, este proceso corre aislado dentro de él. En el caso de las VMs, los procesos corren en sistemas operativos separados - esto es que los procesos de las VMs no corren en el sistema operativo de la host machine.
Comparemos ambos elementos: al contrario de las VMs, los containers son muy ligeros, lo que permite ejecutar mayor número de componentes de software en el mismo hardware, principalmente porque las VMs necesitan correr sus propios procesos, lo que significa mayores recursos computacionales además de los que ya necesita el componente de software que correría dentro de ellas. Un container, por otro lado, es un simple proceso aislado corriendo en el sistema operativo del host, consumiendo los recursos que consume la aplicación y sin recargar al OS con procesos extra. El resultado final es que se pueden ejecutar muchas más aplicaciones en los mismos “hierros”:
Detrás de los containers hay mucho tecnicismo, y necesitaría un artículo entero para explicarlos con detalle, así que por ahora mantengamos en mente que permiten aislar aplicaciones de una manera que consuma muy pocos recursos, tienen todo lo que una aplicación necesita para funcionar, permiten abstraer las aplicaciones de la infraestructura subyacente pero también que son unidades volátiles.
En analogía, los containers son esos contenedores que viajan en un buque de carga a través del atlántico, el buque es ese hardware complejo que subyace debajo de los containers y Kubernetes es el piloto de ese buque o, el helmsman - esa persona que lleva el timón del buque. De hecho, Kubernetes es una palabra griega que se traduce como “piloto”, y se pronuncia Koo-ber-netties.
Kubernetes visto desde muy arriba
Kubernetes permite ejecutar aplicaciones en miles de nodos computacionales como si todos ellos fuesen un enorme servidor, por lo que simplifica el desarrollo, despliegue y mantenimiento de aplicaciones. Este sistema al estandarizar el ecosistema de despliegue, hace que este proceso siempre se haga de la misma manera, no importa si el cluster tiene un par de nodos o miles de ellos, como puede verse en empresas muy grandes como Google. El tamaño del cluster no hace diferencia alguna. Los nodos adicionales significan mayores recursos disponibles.
Arquitectura de un cluster Kubernetes
A nivel de hardware, un cluster de Kubernetes está compuesto generalmente por muchos nodos, y estos se dividen en dos tipos:
- El nodo maestro (o master node): también llamado “Kubernetes Control Plane” que es quien controla el sistema entero de Kubernetes.
- Los nodos obreros (o worker nodes): quienes ejecutan las aplicaciones que los desarrolladores despliegan.
Existen varios componentes dentro de los nodos que son importantes resaltar:
El plano de control (Control plane)
Es quien controla el cluster y lo hace funcionar. Está compuesto por varios componentes que pueden correr en un solo nodo maestro, o estar distribuidos entre varios y replicados para garantizar alta disponibilidad (HA). Los componentes del plano de control son:
● El API server: este es el componente con el cual los usuarios de Kubernetes se comunican a través de comandos o alguna herramienta como Lens, así como también con lo que se comunican otros componentes del cluster.
● El Scheduler: es el componente que “agenda” a las aplicaciones - esencialmente esto significa que asigna un worker node disponible a una aplicación y así poderla desplegar.
● El Controller Manager: quien realiza funciones a nivel del cluster, como replicar componentes, monitoreo de nodos, manejo de fallas en el cluster, etc.
● El etcd: esencialmente es un sistema de almacenamiento de data distribuido que permite guardar toda la configuración del cluster de una manera persistente y segura, y por detrás de este componente existe una base de datos.
Es importante mencionar que los componentes del plano de control no ejecutan aplicaciones, solo controlan funciones relacionadas con el cluster en sí. La ejecución de aplicaciones es realizada por los worker nodes.
Los worker nodes (nodos obreros)
Son los nodos que ejecutan aplicaciones encapsuladas en containers. Los componentes de estos nodos se encargan de ejecutar, monitorear y proveer de los servicios necesarios para las aplicaciones. Estos componentes son:
● Docker, rkt, containerd o algún otro container runtime. Este componente ejecuta containers.
● El Kubelet, quién se comunica con el API server del plano de control y gestiona los containers de su nodo.
● El Kubernetes Service Proxy o kube-proxy: quien se encarga de balancear el tráfico en la red interna del nodo y la comunicación entre los componentes de las aplicaciones.
Detrás de Kubernetes hay años de desarrollo y mejora continua, y frecuentemente se le incorporan nuevas funcionalidades que me tomarían muchos artículos más para explicar en detalle. Muchas de esas funcionalidades son poco usadas, otras son el día a día en la vida de un desarrollador que implementa Kubernetes para el despliegue de sus aplicaciones. En los siguientes artículos de esta serie iré explicando mucho más este sistema hasta que pongamos todo esto en práctica y despleguemos una aplicación tanto en un entorno de desarrollo como de producción. En el siguiente artículo estaré abordando los componentes que acompañan a las aplicaciones para que puedan coexistir dentro del cluster y los mecanismos para exponerlas al mundo, ¡te espero allá!