— 6 min de lectura
Ya no es la última moda, ahora tendríamos que hablar de Blockchain, Machine Learning y cosas así, pero los microservicios hace no tanto eran lo que estaba en boca de todo el mundo, todo el mundo pasaba aplicaciones monolíticas a microservicios, los microservicios habían llegado para salvarnos de todos los males del software.
Obviamente, no fue así, unas cosas por otras, sigue habiendo muchas cosas mejorables en el mundo del software, pero aunque ya no se usen para todo y no tengan sentido siempre, los microservicios llegaron para quedarse, y en este post vamos a intentar explicar para humanos qué son, y formas de implementarlos en Google Cloud.
Si sigues habitualmente nuestro blog verás que hemos hecho ya unos cuántos artículos con la terminología para humanos, con los que pretendemos acercar la tecnología a todo el mundo:
¿Qué es un microservicio?
Lo primero que hay que explicar es que los microservicios surgen como un enfoque o metodología de arquitectura del software. Lo anterior a los microservicios eran los monolitos, piezas de software en las que todas las partes estaban contenidas en la misma arquitectura, si una parte necesitaba crecer porque la demanda aumentaba, la infraestructura entera debía aumentar. Además, si había que añadir una funcionalidad nueva a una parte del sistema, a la hora de entregar el software el monolito entero tenía que entregarse, no solo la pieza correspondiente a la funcionalidad nueva, y así.
Un microservicio es un componente de software independiente, que engloba una funcionalidad normalmente completa, por pequeña que sea (de ahí lo de micro) y que ofrece una API al resto de piezas del software para comunicarse con ella. La independencia del microservicio también suele serlo a nivel de hardware, de tal modo que si la demanda de un servicio en concreto aumenta, solo la infraestructura para ese servicio tendrá que aumentar.
El desarrollo de la nube, el acceso a infraestructura o plataformas como servicio, ha ayudado mucho a facilitar las arquitecturas basadas en microservicios, porque esa separación a nivel de recursos por servicio es mucho más fácil de configurar y montar en la nube que si tenemos que preocuparnos nosotros de todo el hardware. El auge del IoT o Internet de las cosas, con redes complejas de sensores enviando información para ser procesada, también ha fomentado mucho su utilización.
Formas de ejecutar microservicios en Google Cloud
Como hemos dicho en otras ocasiones, en Google Cloud hay multiples formas de ejecutar el mismo proyecto, en gran medida depende de que queramos gestionar nosotros la infraestructura o delegar la gestión al propio Google. Es el caso de un cluster de Kubernetes con los microservicios, que podemos hacerlo tanto en máquinas de Compute Engine directamente, o con un cluster gestionado con Google Kubernetes Engine (GKE).
El primer caso es lo más parecido a tener nosotros las máquinas en cualquier sitio y gestionar el clúster de Kubernetes (más sobre esto en Kubernetes para humanos), solo que son máquinas virtuales, no tenemos que preocuparnos por si se muere la fuente de alimentación o el ventilador hace ruido, etc, pero tanto la instalación como mantenimiento de Kubernetes sería por nuestra cuenta, en el segundo caso es un cluster gestionado por Google.
Por esto, en estos casos no dejamos de ser conscientes de la existencia de las máquinas en las que se ejecutan esos microservicios, de la cantidad de nodos, de su ubicación. En el caso de crear el cluster nosotros mismos con Compute Engine, directamente somos responsables nosotros mismos de meter más máquinas en el cluster si hiciera falta. Si queremos abstraernos completamente de esto, podemos ejecutar los contenedores directamente en la infraestructura de Google con Google Cloud Run y olvidarnos de la infraestructura que haya por debajo. Pongo olvidarnos en itálica porque aunque es verdad que no sabremos qué máquinas están ejecutando nuestro código, en parte esa es la gracia de Google Cloud Run, sí que tenemos que definir el modelo de infraestructura, cantidad de recursos que queremos que tengan disponibles, etc. Esto será así en el resto de casos también, con App Engine o Cloud Functions, digamos que es responsabilidad nuestra definir qué tipo de máquina necesitamos y qué escalado, si queremos que escalen automáticamente.
La diferencia entre las tres últimas soluciones es que en el caso de Cloud Run desplegaremos directamente imágenes de contenedores, en AppEngine si lo que vamos a ejecutar es código en Node, Ruby, PHP y una serie de lenguajes estándar que Google soporta, tendremos contenedores nativos ya preparados y no tenemos que preocuparnos de crear el contenedor, solo del código y de escribir un fichero app.yml con la configuración.
Con Cloud Functions las diferencias aumentan considerablemente. En Cloud Run y AppEngine, podríamos tener múltiples servicios en la misma aplicación/despliegue, con Cloud Functions lo que estamos desplegando son funciones específicas, aisladas entre sí. De hecho cada ejecución de una función de Cloud Functions es independiente del resto y no comparten los datos, así que si necesitamos almacenar cualquier cosa tendremos que hacerlo externamente o en ficheros de Cloud Storage. Las Cloud Functions son perfectas para realizar tareas muy sencillas de extracción de información de una base de datos, por ejemplo, procesarla y guardarla en otro sitio, o transformaciones de ficheros. Al poder ser lanzadas a raíz de un evento, las Cloud Functions también son perfectas para enlazar con colas de eventos de Pub/Sub aquí una serie de ejemplos extraídos de la documentación de Google Cloud, pero hay infinidad de posibilidades:
En este caso la información de temperatura de un sensor se almacena en una cola de pub/sub (se accede al sensor a través de Cloud IoT Core, que es una plataforma para gestión de dispositivos IoT), y de ahí se dispara una función de Cloud Functions que cambia la configuración de otro dispositivo, en este caso un ventilador.
En este caso cuando se almacena una nueva imagen, en Cloud Storage por ejemplo, lanzamos con un evento una Cloud Function para que mande esa imagen a través de una API de procesamiento de imágenes, Cloud Vision API, y si detectamos que hay algo ofensvo en la imagen, difuminarlo con otra función de Cloud Functions y almacenar el resultado de nuevo en Storage.
Coste
Obviamente, todo esto que hemos comentado tiene un coste, pero en nuestra experiencia, si se escoge la herramienta adecuada para cada problema, se reducen los costes notablemente. Por ejemplo, en el último caso que mencionaba anteriormente, si el procesamiento de imágenes es algo fundamental en nuestra aplicación y que va a estar ocurriendo constantemente, puede tener sentido mantenerlo junto al resto de funcionalidades, pero en muchas ocasiones es algo costoso en procesamiento y que ocurre de forma puntual. Si lo mantenemos con el resto de cosas, tenemos que escalarlo todo para esos picos puntuales. En esos casos, sacarlo fuera a un microservicio, con cualquiera de las soluciones mencionadas, nos puede reducir mucho los costes.
También he mencionado también otra situación en la que nos va a venir muy bien, ejecuciones de tareas muy puntuales, cosas que ocurren una vez al día o al mes, procesamiento de datos para hacer un análisis estadístico por ejemplo y guardar ese análisis en otras tablas, etc, esas ejecuciones son perfectas en funciones separadas. Esto es algo que tradicionalmente se ha hecho siempre con un cron por las noches, pero en aplicaciones internacionales con múltiples zonas horarias, o cuando este procesamiento es muy intenso en cálculo, tiene más sentido sacarlo fuera.
Velocidad en el escalado
Ya para terminar y para que no quede un artículo muy largo, otro tema que hemos notado nosotros con algún cliente, es que al final si tienes todo en el mismo monolito y tiene que escalar, la imagen de los contenedores es grande, los contenedores tardan en levantarse, el cluster de máquinas tarda en reaccionar cuando hay picos de demanda... todo el sistema se vuelve perezoso, es como un elefante intentando ser ágil, es complicado. Se pueden suavizar los picos con máquinas de reserva, y escalando antes de tiempo (bajando los porcentajes a los que escalar), pero esto claro, tiene un coste.
Es más fácil, en estos casos, dividir en distintos servicios y escalarlos por separado, bien con contenedores más reducidos o bien sacando algunos servicios a funciones de Cloud Functions.