Migración de una aplicación monolítica a microservicios en Google Kubernetes Engine

En este artículo, se explican los conceptos de alto nivel de la migración de un sitio web de una plataforma monolítica a una plataforma de microservicios refactorizada y basada en contenedores en Google Cloud. La migración se realiza función por función, lo que evita un evento de migración a gran escala y los riesgos que esto conlleva. Este artículo está dirigido a profesionales de TI que están a cargo de un sitio web complejo que se aloja en una plataforma monolítica y que desean modernizar. No necesitas tener un conocimiento profundo de Google Cloud ni de Kubernetes para leer este artículo.

El objetivo de este tipo de migración es proporcionar un entorno más ágil y escalable para las funciones individuales del sitio, en el que las funciones se pueden administrar y actualizar con mayor facilidad que si fuesen parte de la plataforma monolítica. Ejecutar en un entorno de este tipo genera mejoras más rápidas en cada función migrada, lo que proporciona valor a los usuarios durante el proceso.

En este artículo, se usan sitios web de comercio electrónico como carga de trabajo de ejemplo. Muchos sitios web de comercio electrónico se compilan con plataformas monolíticas y patentadas, por lo que son buenas alternativas para la migración que se describe aquí. Sin embargo, puedes aplicar los principios descritos en este artículo a una amplia gama de cargas de trabajo. Puedes beneficiarte de los principios que se mencionan en este artículo, siempre y cuando tus sistemas y restricciones sean lo suficientemente similares a los que se describen aquí. Por ejemplo, los sitios web de reserva de hoteles o alquiler de automóviles también serían buenos candidatos para este patrón de migración.

Como se describe en el documento Migración a GCP: Comienza ahora, existen tres patrones principales para migrar a la nube: lift-and-shift, mejorar y mover y extraer y reemplazar. En este artículo, se describe un caso específico del patrón extraer y reemplazar, en el que el patrón se aplica de forma incremental a cada función de la aplicación, en lugar de aplicarse a la aplicación en conjunto.

Además de este patrón de migración, el artículo explora dos patrones híbridos. Durante la migración en sí, la aplicación tiene una arquitectura híbrida en la que algunas funciones están en la nube y otras aún son locales. Una vez finalizada la migración, la aplicación completa se aloja en la nube, pero aún interactúa con los servicios de backend que permanecen locales.

Terminología

aplicación
En este artículo, una aplicación es un sistema de software completo, con la capacidad de tener muchas funciones, que los usuarios finales perciben como una sola unidad. Por ejemplo, un sitio web de comercio electrónico es una aplicación. Todo lo que se puede hacer con una aplicación se realiza por medio de una función específica de esa aplicación.
función
Una unidad de funcionalidad para una aplicación. Las funciones pueden estar orientadas al usuario y ser esenciales para la aplicación (como el carrito de compras en un sitio web de comercio electrónico), pueden estar orientadas al usuario y ser requeridas por la aplicación (como el proceso de acceso), o pueden ser internas a la aplicación (como la administración de stock para un sitio web de comercio electrónico).
servicio
Un componente independiente de una aplicación. En este artículo, una aplicación está compuesta por diferentes servicios que son invisibles para los usuarios finales. Por ejemplo, una base de datos usada por un sitio web es un servicio.
aplicación monolítica
Es una aplicación compilada como una sola unidad que se puede implementar. (También se la conoce solo como monolítica). Dentro de los ejemplos, se incluyen una sola aplicación Java EE o una sola aplicación web .NET. A menudo, una aplicación monolítica se asocia con una base de datos y una interfaz de usuario del cliente.
microservicio
Es un servicio único que se compila para incluir una función de la aplicación. En el patrón de microservicios, la aplicación es el conjunto de varios servicios, cada uno con un objetivo específico. Por ejemplo, puedes tener un servicio que controle el carrito de compras de tus clientes, otro que controle el servicio de pagos y otro que interactúe con una aplicación de backend para el stock. Esos microservicios deben estar vinculados de forma flexible y deben interactuar entre sí por medio de API bien definidas. Se pueden escribir en diferentes lenguajes y frameworks, y pueden tener diferentes ciclos de vida.
arquitectura híbrida
Se trata de una arquitectura en la que usas un proveedor de servicios en la nube pública (como Google Cloud) en combinación con recursos privados alojados en tu propio centro de datos. Hay muchas razones para implementar una arquitectura híbrida y distintas formas de hacerlo; todo eso se describe en el artículo Patrones y prácticas de nube híbrida y múltiples nubes.
servicios sin estado y con estado
Indica si un servicio administra directamente el almacenamiento de datos. En este artículo, se usa la misma definición de “sin estado” y “con estado” que la que se usa en la metodología Twelve-Factor App. Un servicio sin estado es aquel que no depende de ningún dato para funcionar. Un servicio con estado es lo opuesto. Por ejemplo, un servicio que controla los carritos de compras de tus clientes es un servicio con estado, ya que los carritos de compras deben almacenarse y recuperarse. Un servicio que verifica la disponibilidad de los elementos en el sistema de backend es un servicio sin estado: es el sistema de backend el que almacena los datos (estado), no el servicio.

¿Por qué migrar a microservicios?

Desglosar una aplicación en microservicios tiene las siguientes ventajas, cuya mayoría se debe a que los microservicios están vinculados de manera flexible.

  • Los microservicios se pueden probar e implementar de forma independiente. Cuanto más pequeña sea la unidad de implementación, más fácil será la implementación.
  • Se pueden implementar en diferentes lenguajes y frameworks. En cada microservicio, puedes elegir la mejor tecnología para su caso práctico en particular.
  • Los pueden administrar diferentes equipos. El límite entre microservicios facilita que un equipo se dedique a uno o varios microservicios.
  • Mediante la migración a los microservicios, puedes flexibilizar las dependencias entre los equipos. Cada equipo debe preocuparse solo por las API de los microservicios de los que dependen. El equipo no necesita pensar en cómo se implementan esos microservicios, en sus ciclos de lanzamiento, etcétera.
  • Puedes lograr diseños para evitar fallas con más facilidad. Si hay límites entre los servicios, es más fácil determinar qué hacer si un servicio está inactivo.

Los microservicios tienen algunas desventajas en comparación con las aplicaciones monolíticas:

  • Debido a que una app basada en microservicios es una red de servicios diferentes que a menudo interactúan de maneras que no son evidentes, la complejidad general del sistema suele ser mayor.
  • A diferencia de los componentes internos de una aplicación monolítica, los microservicios se comunican por medio de una red. En algunas circunstancias, esto puede considerarse un problema de seguridad. Istio resuelve este problema mediante la encriptación automática del tráfico entre microservicios.
  • Lograr el mismo nivel de rendimiento que se obtiene con un enfoque monolítico puede resultar difícil debido a las latencias entre servicios.
  • El comportamiento de tu sistema no se debe a un solo servicio, sino a muchos de ellos y a sus interacciones. Por esta razón, es más difícil comprender cómo se comporta tu sistema en la producción (su observabilidad). Istio también representa una solución a este problema.

Si bien algunas empresas, como Google, usan microservicios desde hace muchos años, el concepto (y su relación con la arquitectura orientada a servicios o SOA) se formalizó con James Lewis, Martin Fowler y Sam Newman en su artículo Microservices (Microservicios).

Descripción general del proceso de migración

El proceso de migración que se describe en este artículo es largo y puede tomar meses en completarse, ya que es un proyecto a gran escala. En esta sección, se detalla la ruta de una aplicación monolítica local a una aplicación que está alojada en su totalidad en Google Cloud y compilada con microservicios.

El punto de partida: una app local y monolítica

En este artículo, se supone que, en este momento, ejecutas una aplicación monolítica local. Es probable que el siguiente diagrama de arquitectura de alto nivel sea similar a tu arquitectura actual.

Arquitectura de una aplicación monolítica típica

Con este diagrama, no se busca representar tus sistemas actuales por completo. Algunas de las tecnologías que se suelen encontrar en una arquitectura como esta son las siguientes:

  • Bases de datos relacionales, como la base de datos de Oracle® o SAP HANA
  • Plataformas de comercio electrónico, como Oracle ATG o SAP Hybris
  • Apache Solr u otros motores de búsqueda

El punto final: una app basada en microservicios en Google Cloud

El proceso de migración que se describe aquí va de una arquitectura monolítica local a una arquitectura basada en microservicios que se ejecuta en Google Cloud. La arquitectura de destino se describe en Cargas de trabajo de comercio escalables con microservicios. Se ve del siguiente modo:

Arquitectura después de migrar a contenedores y GKE

En la arquitectura de destino, ejecutarás microservicios en Google Kubernetes Engine (GKE). Kubernetes es una plataforma para administrar, alojar, escalar e implementar contenedores. Los contenedores son una forma portátil de empaquetar y ejecutar código. Son adecuados para el patrón de microservicios, en el que cada microservicio puede ejecutarse en su propio contenedor. Los microservicios pueden realizar llamadas a los sistemas de backend a través de una conexión de red privada que crea Cloud Interconnect o Cloud VPN. Como alternativa, puedes usar Apigee para exponer los servicios de backend y proteger el acceso a ellos. Más adelante en este documento, en la sección Apigee, Cloud VPN y Cloud Interconnect, puedes obtener más información sobre estos productos y sobre cómo elegirlos.

Los microservicios también pueden interactuar con otros productos de Google Cloud, según sus necesidades. Cloud Storage y Cloud SQL son dos de los productos de Google Cloud más comunes para una aplicación de comercio electrónico. Pub/Sub se puede usar como un bus de mensajes entre diferentes microservicios para el trabajo asíncrono.

Puedes exponer tu aplicación a Internet de dos maneras: directamente por medio de un balanceador de cargas de Google Cloud para recursos como imágenes y, como alternativa, a través de Apigee para las API públicas.

En las siguientes tablas, se enumeran los productos que puedes usar en tu arquitectura de destino. No todos son obligatorios y su uso depende del caso práctico exacto.

Herramientas de redes

Producto de Google Cloud Uso Notas
Nube privada virtual Una VPC es una red privada definida por software en la que se encuentran tus recursos (como un clúster de GKE). La VPC incluye funciones como el uso de firewall y el enrutamiento. Una VPC está disponible a nivel global, lo que permite la conectividad privada en todo el mundo, además de las redes de fibra óptica de Google. La VPC de Cloud es obligatoria en esta arquitectura.
Cloud Interconnect Cloud Interconnect extiende tu red local a la red de Google a través de una conexión de latencia baja con alta disponibilidad. Puedes usar la interconexión dedicada para conectarte directamente a Google o usar una interconexión de socio a fin de conectarte a Google por medio de un proveedor de servicios admitido. Es probable que el sitio web de comercio electrónico necesite llamar a los servicios de backend locales, como un sistema de administración de almacenes o un sistema de facturación.
Cloud Interconnect es una de las soluciones para esto.
Cloud VPN Cloud VPN extiende de forma segura la red local a la red de Google por medio de un túnel VPN con IPsec. El tráfico se encripta y fluye entre las dos redes a través de la Internet pública. Cloud VPN es útil para conexiones de datos de bajo volumen. Es probable que el sitio web de comercio electrónico necesite llamar a los servicios de backend locales, como un sistema de administración de almacenes o un sistema de facturación.
Cloud VPN es una de las soluciones para esto.
Cloud Load Balancing Cloud Load Balancing es la solución de balanceo de cargas administrada de Google. Es compatible con el balanceo de cargas L4 (TCP/UDP) y L7 (HTTP). También puede funcionar como un extremo de finalización de SSL (HTTPS). La creación de un balanceador de cargas de Google Cloud te brinda una sola IP anycast. Todos los usuarios que intentan acceder a esta IP se enrutan de forma automática al punto de presencia de Google más cercano para reducir la latencia de la red, gracias a la red de nivel Premium de Google. Cloud Load Balancing es obligatorio en esta arquitectura.
Cloud CDN Cloud CDN (red de distribución de contenidos) usa los puntos de presencia perimetrales de Google distribuidos en todo el mundo para almacenar en caché el contenido del balanceo de cargas de HTTP(S) que se encuentre cerca de los usuarios. El almacenamiento en caché del contenido en los puntos perimetrales de la red de Google proporciona una distribución más rápida del contenido a los usuarios y, además, reduce los costos de entrega. Cloud CDN no es obligatorio en este caso, pero se recomienda su uso, en particular para el contenido estático.

Plataforma

Producto de Google Cloud Uso Notas
Google Kubernetes Engine (GKE) GKE es el producto de Kubernetes administrado de Google que permite implementar aplicaciones en contenedores. GKE es compatible en su totalidad con Kubernetes de código abierto y proporciona muchas características avanzadas, como clústeres regionales para alta disponibilidad, clústeres privados, ajuste de escala automático vertical de pods, ajuste de escala automático de clústeres, GPU y nodos interrumpibles. GKE es obligatorio en esta arquitectura.
Istio Istio disminuye la complejidad relacionada con la administración de las implementaciones de microservicios, gracias a que brinda una manera uniforme para proteger, conectar y supervisar estos microservicios. Istio no es obligatorio en esta arquitectura, pero proporciona funciones útiles como la supervisión mejorada, la encriptación de tráfico y el enrutamiento, así como la inserción de fallas para probar la resiliencia de tu aplicación.
Apigee Apigee es una puerta de enlace de API administrada. Con Apigee, puedes diseñar, publicar, supervisar y monetizar las API de forma segura.
Puedes usar Apigee para exponer las API públicas y privadas. En una arquitectura de microservicios, las API públicas se alojan en Google Cloud y los sistemas locales de backend se pueden exponer como API privadas que solo consumen los microservicios en Google Cloud.
Apigee no es obligatorio en esta arquitectura, pero se recomienda que las API públicas entreguen todo el contenido del sitio. Una puerta de enlace de API como Apigee proporciona muchas características de la administración de API, como cuotas, control de versiones y autenticación.
Pub/Sub Pub/Sub es un sistema de mensajería y transmisión. Si se usa en esta arquitectura, funciona como un bus de mensajes entre microservicios, lo que permite flujos de trabajo asíncronos entre los microservicios. Pub/Sub no es obligatorio, pero el patrón de publicador y suscriptor puede ayudar a mitigar los problemas de escalamiento.

Storage

Producto de Google Cloud Uso Notas
Cloud Storage Cloud Storage proporciona una API de almacenamiento unificado de objetos. Es adecuado para muchos casos prácticos, como la entrega de contenido de sitios web, la creación de copias de seguridad, el archivo de datos, y la distribución de objetos grandes. En un sitio web de comercio electrónico, el uso principal que se le da a Cloud Storage es almacenar y entregar recursos estáticos, como imágenes y videos de productos. La escalabilidad sin interrupciones de Cloud Storage, junto con Cloud CDN, lo convierten en una buena alternativa para la arquitectura de microservicios que se describe en este documento. Por lo tanto, lo recomendamos.
Firestore Firestore es una base de datos de documentos NoSQL rápida y completamente administrada. Firestore no es obligatorio en esta arquitectura, pero es adecuado para varios casos prácticos habituales en sitios web de comercio electrónico. Por ejemplo, puedes usarlo para almacenar los carritos de compras y los metadatos de los usuarios.
Cloud SQL Cloud SQL es el producto administrado de Google para MySQL y PostgreSQL. Estas bases de datos relacionales tienen diversos usos. Cloud SQL no es obligatorio en esta situación, pero sí es adecuado para varios casos prácticos habituales en sitios web de comercio electrónico. Por ejemplo, puedes usar Cloud SQL para almacenar pedidos, lo que permite realizar agregaciones y cálculos con facilidad.
Cloud Spanner Spanner es una base de datos relacional con coherencia sólida, escalable de forma horizontal y disponible a nivel global. Con un ANS para un tiempo de actividad del 99.999% (en el caso de instancias multirregionales), garantiza una disponibilidad muy alta de datos que son fundamentales para tu empresa. Spanner no es obligatorio en esta situación. Debido a que es una base de datos relacional que ofrece coherencia transaccional a escala global, Spanner es ideal para sitios web de comercio electrónico orientados a un público internacional.
Memorystore Memorystore para Redis es una versión administrada de Redis, una base de datos de clave-valor en la memoria. Redis es adecuado para los datos a los que se accede con frecuencia, ya que es una base de datos de baja latencia. Memorystore no es obligatorio en esta situación, pero es adecuado para varios casos prácticos habituales de sitios web, como las tareas de almacenar información de sesión de usuario y proporcionar memoria caché para una aplicación.

También puedes usar otros productos de Google Cloud, pero esta lista representa los más comunes que se encuentran en una arquitectura de comercio electrónico. Además debes considerar que la evolución natural de esta arquitectura es agregar componentes para recopilar inteligencia a partir de datos mediante productos como Cloud Bigtable, BigQuery, AutoML y AI Platform.

También puedes seguir usando algunas de tus tecnologías actuales en esta arquitectura de destino si son relevantes para ti o si el costo de moverlas es demasiado alto. Estos son algunos ejemplos de cómo puedes ejecutar tecnologías de comercio electrónico comunes en Google Cloud:

  • Los socios de Google Cloud pueden administrar las cargas de trabajo de Oracle para que haya una latencia de menos de un milisegundo entre esas cargas de trabajo y la infraestructura de Google Cloud. Esto también te permite reutilizar las licencias existentes.
  • Gracias a la asociación entre SAP y Google Cloud, puedes ejecutar diversas cargas de trabajo de SAP en Google Cloud, incluidas Hybris y HANA.
  • Puedes ejecutar Apache Solr en GKE o usar algunas de las soluciones de Solr disponibles en Google Cloud Marketplace.
  • Puedes ejecutar Elasticsearch en GKE (por ejemplo, mediante la solución de Cloud Marketplace) o usar el servicio administrado que Elastic compiló en Google Cloud.

Apigee, Cloud VPN y Cloud Interconnect

Una de las decisiones más importantes que debes tomar al comienzo de este proyecto es cómo controlar la comunicación entre los microservicios nuevos alojados en GKE y tu sistema heredado local. Existen dos soluciones principales que pueden coexistir: la comunicación basada en una API o la comunicación basada en una conexión privada.

En la solución basada en una API, se usa una solución de administración de API, como Apigee, como proxy entre los dos entornos. Esto te brinda un control preciso sobre qué partes de tus sistemas heredados expones y cómo los expones. También te permite refactorizar sin problemas la implementación de una API (es decir, migrar de un servicio heredado a un microservicio) sin afectar a los consumidores de la API. En el siguiente diagrama, se muestran las interacciones entre Apigee, los sistemas locales y los sistemas de Google Cloud.

Apigee como proxy frente a una combinación de sistemas locales y basados en GCP

Los patrones para implementar las API de Kubernetes a gran escala con Apigee y el libro electrónico de Apigee Beyond ESB Architecture with APIs (Más allá de la arquitectura ESB con las API) pueden ayudarte en este diseño.

En una solución basada en la conectividad privada, debes conectar los entornos locales y de Google Cloud mediante una conexión de red privada. Los microservicios se comunican con tus sistemas heredados por medio de esta conexión. Puedes configurar túneles VPN basados en IPSec con Cloud VPN. Si se necesita un ancho de banda mayor, Cloud Interconnect proporciona una conexión de baja latencia con alta disponibilidad. Consulta Elige un tipo de interconexión para obtener una descripción general de las diferentes opciones.

En el siguiente diagrama, se muestran las interacciones entre los sistemas locales y los sistemas de Google Cloud a través de Cloud VPN o Cloud Interconnect.

Cloud Interconnect o Cloud VPN con una conexión entre un sistema local y un sistema basado en Google Cloud

Los equipos de aplicaciones implementan una solución basada en la API. Esta requiere una mayor integración desde el comienzo del proyecto en la aplicación heredada que una solución basada en la conectividad privada. Sin embargo, una solución basada en una API brinda más opciones de administración a largo plazo. Un equipo de herramientas de redes es el que implementará una solución basada en Cloud VPN o Cloud Interconnect, que, al principio, requiere menos integración en la aplicación heredada. Sin embargo, esta solución no proporciona ningún valor agregado a largo plazo.

El proceso de migración

En esta sección, se describen los pasos de alto nivel que debes seguir para migrar a la nueva arquitectura.

Requisitos

Antes de migrar el primer microservicio a GKE, debes preparar el entorno de Google Cloud en el que trabajarás. Para ello, haz lo siguiente:

  • Configura la organización de Google Cloud, que es el entorno global en el que se alojarán tus recursos de Google Cloud. Como parte de este paso, también debes configurar tus identidades de Google, es decir, las cuentas que los empleados de tu organización necesitan para usar los productos de Google. Este proceso se describe en Prácticas recomendadas para organizaciones empresariales.
  • Diseña las políticas de Google Cloud para controlar sus recursos. Puedes ayudarte con el artículo Diseño de políticas para clientes empresariales.
  • Crea un plan para implementar los recursos de Google Cloud, incluidos los clústeres de GKE, mediante la infraestructura como código (IaC). Esto te brindará entornos que se pueden estandarizar, reproducir y auditar. Para esto, se recomienda usar Cloud Deployment Manager o Terraform. Todos los recursos para la IaC en Google Cloud están disponibles en la página Infraestructura como código.
  • Analiza las diferentes funciones de GKE y ajústalas según sea necesario. Si quieres una aplicación fundamental para una empresa, se recomienda cambiar algunos de los valores predeterminados y endurecer los clústeres. A fin de obtener más información sobre cómo lograrlo, consulta Prepara un entorno de GKE para la producción y Endurece la seguridad del clúster.
  • Compila las herramientas de integración continua/entrega continua (CI/CD) para Kubernetes. Puedes usar Cloud Build para compilar tus imágenes de contenedor y Container Registry a fin de almacenarlas y detectar vulnerabilidades. También puedes combinar esos productos con tus herramientas de CI/CD existentes. Aprovecha este trabajo a fin de implementar las prácticas recomendadas para compilar y trabajar con contenedores. Si haces esto al comienzo del proyecto, se evitarán problemas cuando estés en producción.
  • Según la opción que elijas, configura tu cuenta de Apigee o establece una conexión privada entre Google Cloud y el centro de datos local con Cloud VPN o Cloud Interconnect.

Migra por etapas

Debes migrar las funciones del sitio web de comercio electrónico una por una al nuevo entorno y crear microservicios cuando sea necesario. Esos microservicios pueden volver a llamar al sistema heredado cuando sea necesario.

Este enfoque equivale a transformar este proyecto de migración y refactorización principal en varios proyectos más pequeños. Continuar con este procedimiento tiene dos ventajas:

  • Cada proyecto pequeño es más fácil de limitar y de comprender que el proyecto de migración general. La migración de todo el sitio web de una sola vez requiere que los equipos tengan más conocimientos sobre las interacciones entre los sistemas, las restricciones y los sistemas de terceros que dependen de la disponibilidad del sitio web, entre otros aspectos. Esto aumenta el riesgo de errores.
  • Tener varios proyectos más pequeños te brinda flexibilidad. Si tienes un equipo más pequeño, este puede abordar esos proyectos uno tras otro sin sobrecargarse. Si tienes varios equipos, existe la posibilidad de paralelizar parte del trabajo y dirigir varias migraciones a la vez.

Elegir qué funciones migrar y cuándo migrarlas es una de las decisiones más importantes que tomarás durante esta etapa del proyecto. Cuando tomes esta decisión, debes tener en cuenta la red de dependencias entre las funciones. Algunas funciones pueden depender en gran medida de que otras funcionen de forma correcta, y otras pueden ser bastante independientes. Cuantas menos dependencias tenga una función, más fácil será la migración. El problema de las dependencias, junto con otros factores que deberás considerar cuando decidas qué función migrar, se trata en detalle más adelante en la sección ¿Qué funciones debes migrar primero?

Ejemplo: Migra un carrito de compras

Para ilustrar el proceso de migración, en esta sección se incluye la migración de una sola función, en concreto, la del carrito de compras de un sitio web de comercio electrónico.

Para descubrir las dependencias de esta función, considera un recorrido típico del usuario y cómo se puede implementar en una aplicación monolítica:

  1. El usuario está navegando por el sitio web y encuentra un artículo que le interesa.
  2. Hace clic en Agregar a mi carrito de compras. Esta acción activa una llamada a la API desde el navegador del usuario hasta la función del carrito de compras. Esta es la primera dependencia: el frontend actúa sobre el carrito de compras.
  3. Cuando la función del carrito de compras recibe la llamada, comprueba si el artículo está en stock. Este evento activa una llamada a la API desde la función del carrito de compras hasta el sistema que controla el stock. Esta es la segunda dependencia: el carrito de compras depende del subsistema de stock.
  4. Si el artículo está en stock, la función del carrito de compras almacena información como “el usuario A tiene 1 instancia del artículo X en su carrito”. Esta es la tercera dependencia: el carrito de compras necesita una base de datos para almacenar esta información.
  5. Cuando el usuario finaliza la compra y realiza el proceso de pago, el subsistema de pago consulta el carrito de compras para calcular el total. Cuando se completa el pago, el subsistema de pago notifica a la función del carrito de compras, y el carrito se vacía. Esta es la cuarta dependencia: el subsistema de pago consulta el carrito de compras.

En resumen, el frontend y el subsistema de pago llaman a la función del carrito de compras, y esta consulta una base de datos y el subsistema de stock.

Una base de datos de documentos es adecuada para almacenar carritos de compras. No necesitas la eficacia de las bases de datos relacionales para los datos del carrito de compras, y los carritos de compras se pueden indexar con facilidad en función de los ID de los usuarios. Firestore es una base de datos de documentos NoSQL administrada y sin servidores, y es adecuada en este caso práctico en particular, por lo que es el almacén de datos que se sugiere para la arquitectura de destino.

Puedes migrar los datos del carrito de compras de varias maneras. Esto se analiza con más detalle más adelante, en la sección Estrategias de migración de datos. En este documento, se supone que el enfoque de mantenimiento programado que se describe más adelante es aceptable. En estas condiciones, puedes migrar la función del carrito de compras si sigues estos pasos de alto nivel:

  1. Crea un microservicio nuevo que implemente una API de carrito de compras. Usa Firestore para almacenar los carritos de compras. Asegúrate de que este microservicio nuevo pueda llamar al subsistema de stock.
  2. Crea una secuencia de comandos que extraiga los carritos de compras del subsistema heredado de carrito de compras y los escriba en Firestore. Escribe la secuencia de comandos para que puedas volver a ejecutarla tantas veces como sea necesario y haz que copie solo los carritos de compras que cambiaron desde la última ejecución.
  3. Crea una secuencia de comandos que haga lo mismo, pero al revés: que copie los carritos de compras de Firestore en el sistema heredado. Usa esta secuencia de comandos solo si necesitas revertir la migración.
  4. Expón la API de carrito de compras con Apigee.
  5. Prepara y prueba las modificaciones en el frontend y el subsistema de pago para que puedan llamar al nuevo microservicio de carrito de compras.
  6. Ejecuta la secuencia de comandos de migración de datos que creaste en el paso 2.
  7. Pon tu sitio web en modo de mantenimiento.
  8. Vuelve a ejecutar la secuencia de comandos de migración de datos.
  9. Implementa las modificaciones del paso 5 en el sistema de producción heredado.
  10. Inhabilita el modo de mantenimiento en el sitio web.

La función de carrito de compras del sitio web ahora es un microservicio alojado en Google Cloud. El paso 5 es quizás el paso más difícil de este proceso, ya que requiere que modifiques el sistema heredado. Sin embargo, este paso es más fácil a medida que migras más funciones a los microservicios, porque cada vez más dependencias serán microservicios. Como microservicios, se vinculan con mayor flexibilidad que en una aplicación monolítica y son más fáciles de modificar y de implementar.

Como alternativa, puedes hacer que el microservicio nuevo llame a la base de datos original del carrito de compras y, luego, migrar los datos a Firestore. Para elegir entre esos dos modelos de migración, considera la latencia entre el microservicio y la base de datos original, la complejidad de la refactorización del esquema y la estrategia de migración de datos que deseas adoptar.

¿Qué funciones debes migrar primero?

Esta sección te ayuda a identificar las funciones que se deben migrar primero, el primer paso de la migración. Siempre es preferible un enfoque de “divide y triunfarás” para reducir los riesgos inherentes a las migraciones complejas.

Cuando planificas tu migración, puede que te tiente comenzar con funciones que son triviales para la migración. Esto puede representar una victoria a corto plazo, pero no sería la mejor experiencia de aprendizaje para tu equipo. En lugar comenzar a migrar directamente, deberías dedicar tiempo a evaluar todas las funciones y planificar la migración.

Puedes usar la siguiente lista de áreas de evaluación como framework para el análisis de cada función:

  • Procesos empresariales
  • Diseño y desarrollo
  • Operaciones
  • Personas y equipos

Evaluación de procesos empresariales

El equipo de migración aprenderá y desarrollará procesos durante los primeros intentos de migración, y es probable que cometan errores. Por este motivo, estos primeros intentos no deberían incluir sistemas esenciales para la empresa (a fin de evitar que se afecte la línea principal de negocios), pero deberían representar casos prácticos importantes (para que el equipo tenga una oportunidad de aprendizaje). Cuando evalúes los procesos empresariales, también considera los procesos relacionados con el cumplimiento y las licencias, no solo los de desarrollo.

Evaluación del diseño y el desarrollo

Desde el punto de vista del diseño y el desarrollo, los intentos de migración iniciales que resultan ideales son los que tienen la menor cantidad de dependencias en otras funciones y datos, y que resultan más fáciles de refactorizar si es necesario.

Debes analizar cada función y tener en cuenta sus dependencias y el esfuerzo necesario para la refactorización. Para el análisis de dependencias de cada función, ten en cuenta lo siguiente:

  • El tipo de dependencia: dependencias de datos o de otras funciones
  • El escalamiento de la dependencia: cuántas funciones podrían verse afectadas por un cambio en las dependencias de esta función

Migrar una función con dependencias de datos fuertes suele ser una tarea no trivial por los siguientes motivos:

  • Si decides migrar funciones y migrar los datos relacionados más adelante, debes tener en cuenta el aumento de la latencia de red entre los productores, los consumidores y los almacenes de datos después del intento de migración inicial.
  • Los desafíos de integridad y sincronización de los datos surgirán durante la fase de migración, ya que es posible que leas y escribas datos en varias ubicaciones de forma temporal.

Otra evaluación es la cantidad de refactorización necesaria para cada función. La refactorización potencial depende del diseño actual de la función y de su dirección futura. Para hacer una estimación, considera cómo ese intento podría tener un impacto en los procesos empresariales relacionados.

Estas son las preguntas más importantes que debes responder durante esta evaluación:

  • ¿Qué datos usa esta función?
  • ¿Cuántos datos usa esta función?
  • ¿Esta función necesita otras para que su operación sea correcta?
  • ¿Cuántas otras funciones se ven afectadas por un cambio en esta función?
  • ¿Existen requisitos de red o conectividad para esta función?
  • ¿Cómo afecta el diseño actual de esta función a la refactorización?

Evaluación de las operaciones

Desde el punto de vista de las operaciones, también debes tener en cuenta qué funciones pueden permitir el tiempo de inactividad de un período de migración. Si necesitas minimizar el tiempo de inactividad, la migración de funciones que requieren alta disponibilidad puede implicar trabajo adicional.

Evaluación de las personas y los equipos

Es preferible elegir equipos que tengan procesos bien definidos para dirigir los intentos iniciales de migración. Además, los equipos deben estar dispuestos a preparar el camino para la migración y comprender que encontrarán nuevos desafíos para los que deben encontrar soluciones.

Elige un intento de migración inicial

Según este framework de evaluación, el candidato ideal para el intento de migración inicial debe ser lo suficientemente complejo para que sea significativo, pero lo suficientemente simple como para minimizar el riesgo de falla. En el proceso de migración inicial también se debe cumplir con estas cualidades:

  • Debe requerir una pequeña refactorización, a la vez que tiene en cuenta la función en sí y los procesos empresariales relacionados.
  • No debe tener estado, es decir, no debe tener requisitos de datos externos.
  • Debe tener pocas dependencias o no tener ninguna.

Un ejemplo de un plan de migración

En la siguiente lista, se muestra un ejemplo de un orden de migración:

  1. Frontend de la plataforma, es decir, la interfaz de usuario
  2. Funciones sin estado, como un servicio de conversión de monedas
  3. Funciones con conjuntos de datos independientes (conjuntos de datos que no dependen de otros), como un servicio para enumerar tus tiendas físicas
  4. Funciones con conjuntos de datos compartidos: la lógica empresarial de la plataforma de comercio electrónico

Funciones sin estado y frontend de la plataforma

Los frontends de la plataforma y las funciones sin estado suelen tener pocas dependencias. Ambos son alternativas ideales para la migración inicial, ya que son componentes que no son triviales para la arquitectura. Necesitan una refactorización limitada, porque en la fase de migración inicial, la API de backend aún se entrega desde el centro de datos heredado o desde el entorno de ejecución alojado por otro proveedor de servicios en la nube.

Para las funciones de frontend de la plataforma y las funciones sin estado, enfócate en la canalización de integración y de implementación. Debido a que las cargas de trabajo de GKE deben estar en contenedores, es posible que debas realizar más trabajo operativo.

Funciones con conjuntos de datos independientes

Los siguientes componentes que se deben migrar son funciones cuyos conjuntos de datos son independientes de otros conjuntos de datos. Estos conjuntos de datos independientes son más fáciles de extraer de tu sistema heredado que los conjuntos de datos que tienen dependencias. Por supuesto, en comparación con la migración de funciones sin estado, la migración de funciones que tienen conjuntos de datos independientes requiere trabajo adicional, en concreto, crear y administrar el nuevo almacén de datos, además se migrar los datos.

Cuando planificas la migración de datos, puedes elegir entre distintos sistemas de almacenamiento. Si deseas modernizar tus aplicaciones, puedes usar lo siguiente:

  • Servicios de almacenamiento de datos administrados, como Cloud Storage o Filestore, para almacenar archivos
  • Cloud SQL para migrar datos desde un RDBMS
  • Firestore para migrar datos desde una base de datos NoSQL

Funciones con conjuntos de datos compartidos

Las funciones con conjuntos de datos compartidos son las más difíciles de migrar. Esto se debe a que, como se detalla más adelante, la migración de datos es una tarea complicada debido a los requisitos de coherencia, distribución, acceso y latencia.

Estrategias de migración de datos

Cuando migres datos, puedes seguir este enfoque general:

  1. Transfiere datos del sitio heredado al nuevo.
  2. Resuelve cualquier problema de integración de datos que surja, como, por ejemplo, la sincronización de los mismos datos de varias fuentes.
  3. Valida la migración de datos.
  4. Promociona el sitio nuevo como la copia principal.
  5. Cuando ya no necesites el sitio heredado como opción de resguardo, quítalo.

Debes basar tu estrategia de migración de datos en las siguientes preguntas:

  • ¿Cuántos datos necesitas migrar?
  • ¿Con qué frecuencia cambian estos datos?
  • ¿Puedes permitirte el tiempo de inactividad que representa un período de migración mientras migras datos?
  • ¿Cuál es su modelo de coherencia de datos actual?

No hay un enfoque mejor que otro. La elección de uno depende del entorno y de tus necesidades.

En las siguientes secciones, se presentan cuatro enfoques de migración de datos:

  • Mantenimiento programado
  • Replicación continua
  • Y (escritura y lectura)
  • Microservicio de acceso a los datos

Cada enfoque aborda diferentes problemas, según el escalamiento y los requisitos de la migración de datos.

El enfoque de microservicio de acceso a datos es la opción preferida en una arquitectura de microservicios. Sin embargo, los otros enfoques son útiles para la migración de datos. También son útiles durante el período de transición necesario para modernizar la infraestructura a fin de usar el enfoque de microservicio de acceso a los datos.

En el siguiente gráfico, se describen las extensiones de los períodos de migración correspondientes, el esfuerzo de refactorización y las propiedades de flexibilidad de cada uno de estos enfoques.

Gráfico de barras en el que cada barra muestra los valores relativos de flexibilidad, esfuerzo de refactorización y extensiones de períodos de migración de cada uno de los 4 enfoques

Antes de seguir cualquiera de estos enfoques, asegúrate de haber configurado la infraestructura requerida en el nuevo entorno.

Mantenimiento programado

El enfoque de mantenimiento programado es ideal si tus cargas de trabajo pueden permitir un período de migración. Está programado para que puedas planificar cuándo se produce el período de migración.

En este enfoque, la migración consta de estos pasos:

  1. Copia los datos que se encuentran en el sitio heredado en el sitio nuevo. Con esta copia inicial, se minimiza el período de migración. Después de ella, debes copiar solo los datos que cambiaron durante este período.
  2. Realiza validaciones de datos y verificaciones de coherencia para comparar los datos del sitio heredado con los datos copiados en el sitio nuevo.
  3. Detén las cargas de trabajo y los servicios que tienen acceso de escritura a los datos copiados para que no se produzcan más cambios.
  4. Sincroniza los cambios que se produjeron después de la copia inicial.
  5. Refactoriza las cargas de trabajo y los servicios para que usen el sitio nuevo.
  6. Inicia tus cargas de trabajo y servicios.
  7. Cuando ya no necesites el sitio heredado como opción de resguardo, quítalo.

El enfoque de mantenimiento programado coloca la mayor parte de la carga en las operaciones, ya que se necesita una refactorización mínima de la carga de trabajo y los servicios.

Replicación continua

Debido a que no en todas las cargas de trabajo se puede permitir un período de migración largo, puedes compilar con el enfoque de mantenimiento programado si proporcionas un mecanismo de replicación continua después de los pasos iniciales de copia y validación. Cuando diseñas un mecanismo como este, también debes tener en cuenta la velocidad a la que se aplican los cambios a tus datos, ya que puede ser difícil mantener dos sistemas sincronizados.

El enfoque de replicación continua es más complejo que el enfoque de mantenimiento programado. Sin embargo, el enfoque de replicación continua minimiza el tiempo del período de migración requerido, ya que minimiza la cantidad de datos que necesitas sincronizar. La secuencia para una migración de replicación continua es la siguiente:

  1. Copia los datos que se encuentran en el sitio heredado en el sitio nuevo. Con esta copia inicial, se minimiza el período de migración. Después de ella, debes copiar solo los datos que cambiaron durante este período.
  2. Realiza validaciones de datos y verificaciones de coherencia para comparar los datos del sitio heredado con los datos copiados en el sitio nuevo.
  3. Configura un mecanismo de replicación continua desde el sitio heredado hasta el sitio nuevo.
  4. Detén las cargas de trabajo y los servicios que tienen acceso a los datos que se migrarán (es decir, a los datos involucrados en el paso anterior).
  5. Refactoriza las cargas de trabajo y los servicios para que usen el sitio nuevo.
  6. Espera a que la replicación sincronice por completo el sitio nuevo con el sitio heredado.
  7. Inicia tus cargas de trabajo y servicios.
  8. Cuando ya no necesites el sitio heredado como opción de resguardo, quítalo.

Al igual que con el enfoque de mantenimiento programado, el enfoque de replicación continua coloca la mayor parte de la carga en las operaciones.

Y (escritura y lectura)

Si las cargas de trabajo tienen requisitos estrictos de alta disponibilidad y no puedes permitirte el tiempo de inactividad que representa un período de migración, debes adoptar un enfoque diferente. Para esta situación, puedes usar un enfoque que en este documento se conoce como Y (escritura y lectura), que es una forma de migración paralela. Con este enfoque, la carga de trabajo escribe y lee datos en el sitio heredado y en el sitio nuevo durante la migración. La letra Y se usa aquí como una representación gráfica del flujo de datos durante el período de migración.

Este enfoque se resume de la siguiente manera:

  1. Refactoriza cargas de trabajo y servicios para escribir datos en el sitio heredado y en el sitio nuevo, y leer desde el sitio heredado.
  2. Identifica los datos que se escribieron antes de habilitar las operaciones de escritura en el sitio nuevo y cópialos del sitio heredado al nuevo. Junto con la refactorización anterior, esto garantiza que los almacenes de datos estén alineados.
  3. Realiza la validación de datos y verificaciones de coherencia que comparen los datos del sitio heredado con los del sitio nuevo.
  4. Cambia las operaciones de lectura del sitio heredado al nuevo.
  5. Realiza otra ronda de validación de datos y verificaciones de coherencia para comparar los datos del sitio heredado con el sitio nuevo.
  6. Inhabilita la escritura en el sitio heredado.
  7. Cuando ya no necesites el sitio heredado como opción de resguardo, quítalo.

A diferencia de los enfoques de mantenimiento programado y replicación continua, el enfoque Y (escritura y lectura) coloca la mayoría de los esfuerzos en el desarrollo, en lugar de en las operaciones, debido a las múltiples refactorizaciones.

Microservicio de acceso a los datos

Si deseas reducir el esfuerzo de refactorización necesario para seguir el enfoque Y (escritura y lectura), puedes centralizar las operaciones de lectura y escritura de datos mediante la refactorización de cargas de trabajo y servicios para que usen un microservicio de acceso a datos. Este microservicio escalable se convierte en el único punto de entrada a tu capa de almacenamiento de datos y actúa como un proxy para esa capa. De los enfoques que se analizan aquí, este te brinda la máxima flexibilidad, ya que puedes refactorizar este componente sin afectar a otros componentes de la arquitectura y sin requerir un período de migración.

Usar un microservicio de acceso a los datos es muy similar al enfoque Y (escritura y lectura). La diferencia es que los esfuerzos de refactorización se enfocan solo en el microservicio de acceso a los datos, en lugar de tener que refactorizar todas las cargas de trabajo y los servicios que acceden a la capa de almacenamiento de datos. Este enfoque se resume de la siguiente manera:

  1. Refactoriza el microservicio de acceso a los datos para escribir datos en el sitio heredado y en el sitio nuevo. Las lecturas se realizan en el sitio heredado.
  2. Identifica los datos que se escribieron antes de habilitar las operaciones de escritura en el sitio nuevo y cópialos del sitio heredado al nuevo. Junto con la refactorización anterior, esto garantiza que los almacenes de datos estén alineados.
  3. Realiza la validación de datos y verificaciones de coherencia para comparar los datos del sitio heredado con los del sitio nuevo.
  4. Refactoriza el microservicio de acceso a los datos para leer desde el sitio nuevo.
  5. Realiza otra ronda de validación de datos y verificaciones de coherencia para comparar los datos en el sitio heredado con los datos en el sitio nuevo.
  6. Refactoriza el microservicio de acceso a los datos para escribir solo en el sitio nuevo.
  7. Cuando ya no necesites el sitio heredado como opción de resguardo, quítalo.

Al igual que el enfoque Y (escritura y lectura), el enfoque de microservicio de acceso a los datos coloca la mayor parte de la carga en el desarrollo. Sin embargo, es mucho más liviano en comparación con el enfoque Y (escritura y lectura), ya que los esfuerzos de refactorización se enfocan en el microservicio de acceso a los datos.

Prácticas recomendadas para los microservicios

En las siguientes secciones, se incluye un conjunto de prácticas recomendadas para diseñar, escribir e implementar microservicios.

Diseña microservicios

Cuando diseñes los microservicios, sigue un patrón de diseño que te permita determinar de forma correcta el contexto y el límite de cada microservicio. Esto te protege de la fragmentación innecesaria de las cargas de trabajo de microservicios. También te permite definir un contexto preciso (o dominio) en el que tus microservicios son válidos. Un ejemplo de ese patrón de diseño es el diseño basado en dominios (DDD).

Contratos de API

Cada microservicio debe invocarse solo desde un conjunto de interfaces. A su vez, cada interfaz debe estar definida con claridad por un contrato que se pueda implementar mediante un lenguaje de definición de API como la especificación de OpenAPI Initiative (antes Swagger) o RAML. Tener interfaces y contratos de API bien definidos te permite desarrollar pruebas como un componente principal de tu solución (por ejemplo, mediante la aplicación del desarrollo basado en pruebas) en estas interfaces de API.

Administra los cambios

Si debes introducir cambios rotundos en tus contratos de API, debes prepararte con anticipación a fin de minimizar el esfuerzo de refactorización necesario para que los clientes hagan frente a los cambios. Hay dos formas de abordar este problema:

  • Control de versiones
  • Implementa un microservicio nuevo

Control de versiones

Para brindarte flexibilidad en la administración de actualizaciones que podrían interrumpir a los clientes existentes, debes implementar un esquema de control de versiones para los microservicios. El control de versiones te permite implementar versiones actualizadas de un microservicio sin afectar a los clientes que usan una versión existente. Si usas el control de versiones, debes crear una versión nueva cada vez que introduzcas un cambio que rompa cualquier parte de un contrato existente, incluso si el cambio es mínimo.

Puedes implementar un esquema de control de versiones con los siguientes esquemas:

  • Control de versiones global
  • Control de versiones de recursos

Cuando implementas un esquema de control de versiones global, se da a entender que el control de versiones es para toda la API. Una implementación es compilar la información de la versión en los URI de recursos, como se muestra a continuación:

/api/v1/entities or api.v3.domain.tld/entities

Este tipo de esquema de control de versiones es fácil de implementar, ya que se administra en un solo lugar. Pero no es tan flexible como un esquema de control de versiones de recursos. Para ver un ejemplo de un esquema de control de versiones global, consulta el esquema de control de versiones de la API de Kubernetes.

Un esquema de control de versiones de recursos te permite crear versiones independientes de cada recurso que entrega el microservicio. Para obtener la máxima flexibilidad, incluso puedes crear una versión de un recurso determinado según la operación en ese recurso (por ejemplo, el verbo HTTP). Puedes implementar este esquema de control de versiones de varias maneras. Puedes usar URI o encabezados de solicitud HTTP estándar o personalizados, como en el siguiente ejemplo:

Accept: application/tld.domain.entities.v2+json

Un esquema de control de versiones de recursos puede ser más difícil de diseñar que un esquema de control de versiones global, ya que necesitas un enfoque generalizado para todas tus entidades. Sin embargo, es más flexible, ya que puedes implementar políticas de actualización independientes y diferentes para los recursos.

Implementa un microservicio nuevo

Otro enfoque para abordar los cambios rotundos es implementar un microservicio nuevo que tenga un contrato nuevo. Puedes hacer esto si la nueva versión del microservicio requiere tantos cambios que tiene más sentido escribir una versión nueva que actualizar la existente. Si bien este enfoque brinda la máxima flexibilidad, es posible que presente microservicios con funciones y contratos superpuestos. Después de implementar el microservicio nuevo, puedes refactorizar de forma gradual los clientes para que lo usen y migren desde el anterior.

Próximos pasos