Y tú, ¿odias o amas Kubernetes?
Tags: kubernetes, aws, containers, discuss
Published: 2024-01-12 08:11:43

En los últimos meses, me he encontrado con bastante gente con cierta tendencia a odiar Kubernetes e intentar evitarlo a toda costa. Precisamente, no ha sido gente que apueste por modelos legacy, sino todo lo contrario: gente con un bagaje grande en modernización de aplicaciones y uso de AWS. Esto es curioso porque el estado actual de implantación de Kubernetes es cada día más alto y tiene muchísimos defensores.

**Kubernetes es un gran producto**, creo que nadie lo duda y tiene muchos casos de uso válidos. También existen muchas alternativas válidas a Kubernetes que pueden ser más adecuadas dependiendo del caso de uso.

Pero **¿cuáles son los problemas que veo a Kubernetes?**, ¿por qué otras alternativas me gustan más? Os invito a acompañarme en esta bonita historia de odio hacia un stack tecnológico.

¿Qué es Kubernetes?
**Kubernetes es un orquestador de contenedores**. Sobre esta frase, que sigue estando en la página oficial de [Kubernetes](https://kubernetes.io/), he tenido un montón de discusiones y ha generado también bastantes bromas (hay quien tiene un kimono muy bonito con esa frase serigrafiada en la espalda). Por desgracia, y dando la razón al propietario del Kimono, **no es un orquestador de contenedores**, es muchas más cosas. Y aquí es donde quizás viene el primer problema: Kubernetes no es sencillo, nunca lo ha sido y nunca lo será.

Para explicar esto hay que irse al origen de Kubernetes. En 1979… vale, igual no hace falta irse tan atrás, pero los ancestros de contenerización de aplicaciones se remontan a esa fecha ;). En 2006, varios ingenieros de Google empezaron a desarrollar algo curioso dentro del kernel de Linux llamado “process containers”, aunque luego lo renombraron como “control groups” o como se le conoce más comúnmente “**cgroups**”. Esta feature es la que permitió el nacimiento de los contenedores tal y como los conocemos ahora.

De forma interna, Google empezó a usar esta funcionalidad para sus aplicaciones y, como necesitaba gestionarlas, creó **Borg** (el ancestro de Kubernetes) para gestionar sus propios contenedores.

Años después se liberó Docker (ya existían implementaciones de contenedores anteriores, pero ninguna tan buena y simple). Aquí surgió el primer problema, los contenedores eran una idea increíble, pero no existía una forma de gestionarlos y gobernarlos (es verdad que existían ciertas soluciones, pero no eran muy completas).

A finales de 2014 todo cambió. Google reescribió Borg con todo el conocimiento que tenía y lo liberó como Kubernetes. Kubernetes vino a solucionar todos los problemas y rápidamente se convirtió en la tecnología preferente para gestionar y gobernar contenedores. Curiosamente, 2014 fue un año prolífico: a finales de ese año se lanzaron 2 servicios de AWS basados en contenedores de los que hablaremos más adelante, como son **Lambda** y **ECS**.

## Empiezan los problemas

Kubernetes era la solución más completa y más madura, realmente Google llevaba casi 10 años utilizando y manteniendo Borg cuando lanzó Kubernetes.

Como es una solución que está pensada para gestionar miles y miles de contenedores repartidos entre cientos de clusters físicos, se nota bastante a la hora de utilizarlo. No está pensado para pequeñas cargas, más bien está pensado para cargas muy grandes.

**Kubernetes solucionaba muchas cosas, pero también trajo nuevos problemas**, como gestionar el networking de Kubernetes, la insolación de cargas, escalado, securización, cifrado, modelo de despliegue, modelo de operación, parcheos, etc.

Un claro ejemplo de estos problemas, es el escalado de Kubernetes. Escalar y desescalar un [pod](https://kubernetes.io/es/docs/concepts/workloads/pods/pod/) (que es un grupo de uno o más contenedores) es sencillo. Pero escalar y desescalar la infraestructura donde se ejecutan los pod ([nodes](https://kubernetes.io/docs/concepts/architecture/nodes/)) es muy complejo, porque hay que generar infraestructura, los pods no tienen tamaños homogéneos, hay que distribuir las réplicas de los pods en diferentes servidores, reunificar pods en un mismo server para liberar otros y poderlos apagar, etc.

Aunque existen muchas soluciones que ayudan (como [Karpenter](https://karpenter.sh/)), el esfuerzo y coste de gestionar y mantener un cluster de Kubernetes es muy alto.

Kubernetes requiere bastante expertise en la tecnología y un equipo que mantenga el stack tecnológico.

En 2014 la implantación Cloud todavía era pequeña. Por ejemplo, OpenStack estaba en su punto más alto, en ese momento el mundo IT era mucho más complejo y dependiente de la infraestructura pura. Gestionar capas y capas de complejidad era nuestro día a día.

Pero no estamos en 2014 y el mundo Cloud nos ha facilitado la vida y ha cambiado el paradigma. Ahora estamos en un momento en el que se intenta simplificar al máximo estas tareas y se tiende a empoderar a los desarrolladores para agilizar los despliegues. Por este motivo, añadir estas capas de complejidad en infraestructura es ir a contracorriente.

Aquí entran a jugar una serie de servicios Cloud más sencillos que un cluster puro de Kubernetes, pero en los que podemos ejecutar cargas contenerizadas como pueden ser [Lambda](https://aws.amazon.com/es/lambda/), [Fargate](https://aws.amazon.com/es/fargate/), [ECS (Elastic Container Service)](https://aws.amazon.com/es/ecs/) y [EKS (Elastic Kubernetes Service)](https://aws.amazon.com/es/eks/).

Voy a ser sincero: ninguno, incluido EKS que es un servicio gestionado de Kubernetes, tienen la potencia de un Kubernetes puro, **pero no necesitamos esa potencia**.

## ¿Es necesario desplegar Kubernetes en todos los casos de uso?

La respuesta es no. Muchos casos de uso, por no decir la mayoría, **no requieren de algo tan complejo como Kubernetes**.

- **Lambda es un servicio serverless** que permite la ejecución de código directamente, sin necesidad de provisionar infraestructura.
No podemos gestionar el contenedor propiamente, pero realmente nos genera un contenedor o contexto preconstruido que nos permite ejecutar código directamente.

- **Fargate (ECS o EKS)** es un servicio que permite la ejecución de contenedores en modo serverless sin necesidad de preocuparte por el cluster que ejecuta la carga.

Tanto Lambda como Fargate te abstraen de toda esa complejidad, ellos se encargan de gestionarla. Simplemente, despliegas tu código o tu imagen y ya está. Algo muy sencillo, pero a la vez muy potente. Los 2 hacen uso de una tecnología open source desarrollada por Amazon muy interesante llamada [Firecracker](https://firecracker-microvm.github.io/).

- **ECS es el orquestador de contenedores gestionado de AWS**, es mucho más sencillo que Kubernetes. Es únicamente un orquestador de contenedores que está delegando el resto de tareas a otros servicios de AWS. 

- **EKS es servicio gestionado de Kubernetes** que nos abstrae del despliegue de infraestructura y se encarga de gestionar parte de nuestros clusters de Kubernetes (los master de Kubernetes), eliminando parte de la complejidad de Kubernetes pero dejándonos cierta flexibilidad

Todos estos servicios permiten desplegar aplicaciones basadas en un modelo de contenedores de forma más sencilla, abstrayéndose de la complejidad de Kubernetes. Son servicios que están más acotados y pensados para una gran variedad de casos de uso.

¿Por qué desplegar algo tan complejo como Kubernetes, si podemos utilizar herramientas más sencillas? Bueno es algo bastante complicado de explicar, pero vamos a intentar responderlo y analizarlo en el post.

**Kubernetes no tiene Lock-In
**Es bastante común pensar que Kubernetes no tiene Lock-In y es una de las justificaciones más utilizadas para priorizar el uso de Kubernetes frente a otras alternativas. Pero desgraciadamente **Kubernetes tiene Vendor Lock-In**.

Por un lado, un desarrollo hecho en Kubernetes, requiere ejecutarse en Kubernetes, no vas a poder ejecutarlo en otro tipo de contenerización y menos fuera del mundo de contenedores. Y eso es un Lock-In, no muy grande, porque el modelo de contenedores es bastante flexible y permite movernos de forma “sencilla”.

Pero seamos sinceros, nadie despliega Kubernetes Vainilla. Kubernetes Vainilla tiene poco Lock-In (aunque lo tiene), pero es difícil de desplegar y además requiere de más software para poder gestionar toda la complejidad asociada a Kubernetes.

Aquí entran los Vendors que nos proponen diferentes Stacks que añaden herramientas que solucionan o facilitan muchos de los problemas que hemos comentado. El problema aquí es que cada Vendor añade sus propias funcionalidades para dar valor a su Stack provocando que existan Lock-In entre diferentes Stacks. Es curioso hablar de evitar el Lock-In con Stacks que utilizan sus propias herramientas cerradas e incluso que modifican el modelo de Kubernetes.

Mucha gente piensa que migrar de un sabor a otro de Kubernetes es transparente, mientras que si vamos a un servicio Cloud va a ser muy costoso. Una vez que estamos en contenedores el esfuerzo va a ser muy similar.

Existe un [artículo reciente](https://medium.com/digital-mckinsey/does-kubernetes-really-give-you-multicloud-portability-476270a0acc7) que compara migrar un proyecto estándar a diferentes sabores de Kubernetes gestionados y adicionalmente a ECS, curiosamente el tiempo de migración y el esfuerzo de migración es exactamente el mismo.

[De Lock-In hemos hablado otras veces en el blog](https://www.paradigmadigital.com/dev/podcast-cloud-lock-in-caracteristicas/). Es un mal necesario, y debemos gestionarlo como tal. Existe mucha tendencia a evitarlo. En parte, es culpa de un abuso de Lock-In por ciertos Vendors y, por otro lado, que en el pasado no se gestionó adecuadamente. Hay que valorar si un Lock-In como el que pueda tener Lambda, Fargate, ECS o EKS nos conviene y nos facilita la vida y además tener en cuenta **cuánto nos costaría salir a otra tecnología**.

Lo importante no es no tener Lock-In (porque básicamente es imposible evitarlo), sino gestionarlo correctamente.

## Kubernetes es Multi-Cloud

Esta es la mayor mentira jamás contada en Cloud y la respuesta es que no. **Kubernetes no es Multi-Cloud**, puedes ejecutar Kubernetes en Múltiples Cloud, pero no por ello significa que en cada Cloud funcione igual.

Un ejemplo que me gusta utilizar es Terraform, ya que permite desplegar infraestructura en todas las Cloud, pero un código de Terraform que has generado para AWS, solo funcionará en AWS, no funcionará en otro Cloud.

Lo que nos da Terraform es la posibilidad de usar la misma estructura y lenguaje, pero no el mismo contenido. Lo mismo pasa con Kubernetes (aunque realmente esta **potencia nos la dan los contenedores** y no Kubernetes).

Un cluster de Kubernetes en AWS no funcionará igual en Azure o en Google Cloud y esto es debido a que las diferentes Cloud se parecen, pero su implementación es totalmente diferente. Solamente con ver las diferencias en el modelo de networking y el modelo de IAM (Identity and Access Management) nos podemos dar cuenta de las diferencias.

Desde hace un tiempo y gracias al genial [Corey Quinn](https://www.lastweekinaws.com/) siempre recomiendo lo mismo al hablar de Multi-Cloud, antes de nada **prueba a montarlo en Multi-Region dentro del mismo Cloud Provider**.

Gestionar algo tan sencillo como la persistencia se empieza a complicar mucho en el momento que pasamos de una región a varias regiones. Y cada capa que vayamos añadiendo se complica cada vez más y estamos hablando de un mismo Cloud donde el modelo es igual y las APIs son compatibles, si nos vamos a otro Cloud el problema se multiplica exponencialmente.

## Kubernetes = Cloud

Hay una concepción bastante grande de que si utilizamos Kubernetes, estamos utilizando Cloud. Si bien todos los Cloud Providers tienen servicios gestionados de Kubernetes, Kubernetes como tecnología no nació en el cloud y su evolución fue de forma paralela.

Es cierto que las **buenas prácticas de Kubernetes se alinean mucho con las buenas prácticas tanto de Cloud, como de modernización de aplicaciones**. El uso de contenedores tiene sentido en arquitecturas de microservicios.

**Los contenedores no son algo nuevo**, realmente el uso de contenedores o más bien de ancestros de los contenedores viene de lejos y muchos administradores de sistemas Unix hemos utilizado esos ancestros, por lo que evolucionar a Kubernetes no era algo difícil e incluso se puede ver como algo natural.

Esto en sí no es un problema, tener una estrategia en On-Prem de contenedores no es malo. **El problema es que a veces se utiliza Kubernetes como parte de una evolución cloud inexistente**.

Hablamos de una estrategia Cloud que se basa en el uso de Kubernetes en Cloud como si de una infraestructura OnPrem se tratase. Esto es una muy mala idea, porque realmente estamos utilizando el Cloud como un CPD anexo y el Cloud no funciona igual que un CPD.

**En Kubernetes no entra todo
**Entramos en la parte final y para mí el gran problema de Kubernetes. Al final se ha dado tanta flexibilidad a Kubernetes que se puede ejecutar cualquier carga.

Esto en principio parece bueno, pero el que se pueda ejecutar, no significa que sea lo más optimo, y menos si queremos evolucionar. Un claro ejemplo serían las BBDD en Kubernetes. Es posible ejecutar una BBDD en Kubernetes, pero no tiene sentido. Al final no estás contenerizado un microservicio, sino que estás contenerizado un servidor entero de BBDD.

**¿De qué sirve un pod, que consume un servidor entero?
**
Otro ejemplo horrible son los famosos “Lift and Shift to Kubernetes”, **¿qué sentido tiene pasar de un servidor virtualizado a un pod en Kubernetes?** Es posible hacerlo, pero solamente estamos generando problemas y utilizando la tecnología de contenedores para algo que no es su propósito.

El problema no es que Kubernetes pueda ejecutar estas cargas, **el problema es que es un mal caso de uso**, que se está generalizando demasiado. Un gran poder conlleva una gran responsabilidad y en el caso de Kubernetes este poder se está utilizando para contenerizar cargas que no debieran de ejecutarse en Kubernetes.

## Conclusiones

No os voy a mentir, **Kubernetes no es una mala solución**, hay casos de uso en que es la solución más óptima. En Paradigma hay compañeros que están trabajando en proyectos de Kubernetes en los cuales no hay otra opción al uso de Kubernetes y se está haciendo un gran trabajo. He visto bastantes clusters de Kubernetes que están muy bien montados, muy bien operados y que son necesarios.

**Realmente no odio Kubernetes, odio las malas implementaciones de Kubernetes**, que por desgracia últimamente son las más habituales. Una buena tecnología que tendría que usarse para un tipo de casos de uso, se está utilizando para casos de uso erróneos. Esto es un problema, porque muchas veces estamos generando una complejidad innecesaria. Al final estas malas implementaciones están abocadas al fracaso.

Es muy habitual que empecemos por montar un cluster de Kubernetes para ejecutar nuestras futuras cargas de trabajo, sin tener en cuenta las cargas de trabajo en sí. Primero montamos el cluster y luego ya definimos las cargas. También existe la variante de directamente desarrollar en Kubernetes sin tener en cuenta si va a ser lo más optimo.

Estamos en 2023, la división entre infraestructura y desarrollo es algo del pasado, debemos de pensar en la carga que vamos a desarrollar y elegir el lugar más optimo para ejecutarla.

**Mi recomendación es ir de menos a más complejidad, carga a carga y evaluando cada salto.
**
El orden que yo propongo sería el siguiente:

1. Lambda
2. Fargate
3. ECS
4. EKS
5. Kubernetes EC2
6. Kubernetes OnPrem

Es importante en cada salto evaluar el “**por qué**”.

Si no puedo utilizar Lambda debo de **preguntarme el motivo y si realmente está razonado**. En muchos casos no se utiliza Lambda porque se requiere que el contenedor siempre esté ejecutándose.

Pero realmente ese requerimiento está razonado o simplemente es porque me resulta más cómodo o habitual un desarrollo en el que el servicio no depende de eventos y esté siempre ejecutándose. Lo mismo pasa con Fargate, que en muchas ocasiones se descarta por no permitir discos persistentes.

Aunque ECS, EKS y Kubernetes permiten montar discos persistentes en los pods no es algo recomendado, es más se debería de evitar al máximo.

Este ejercicio lo debemos hacer con todas las cargas y en todos los pasos, en muchas ocasiones se abusa de Kubernetes, porque nos permite malos usos del pasado. Pero esto no es una ventaja, es un problema.

También es importante analizar cada carga sin tener en cuenta el global. Si por ejemplo el 80% de nuestras cargas pueden funcionar en Fargate y el resto requiere de un EKS, no pasa nada, montemos un cluster pequeño para ese 20% restante y ejecutemos el 80% en lambda.

Por último, **no debemos de olvidarnos de EC2**, existen cargas que no tiene sentido contenerizar ahora mismo. Un monolito contenerizado, no deja de ser un monolito. Para estos casos quedarnos en EC2 e ir evolucionando nuestra aplicación a otros modelos en el futuro no es malo.

**Hasta aquí este análisis de mi odio hacia Kubernetes o, más bien, de mi odio hacia su mal uso**. PD: Durante el artículo hay varios vínculos a tecnologías muy interesantes como Karpenter y Firecracker, os recomiendo que le echéis un vistazo.