- Operar más de 2,800 servicios con MSA les ha dado mucho valor
- Pero esta arquitectura también tiene dificultades. Una de ellas es hacer cambios de librerías en todos los servicios
- Normalmente hay que elegir dos de estas tres cosas: librerías actualizadas, versiones de librerías consistentes y bajo esfuerzo de actualización
Estrategia de migración centralizada
- En Monzo creen que, mediante un enfoque de migración impulsado de forma centralizada, pueden lograr en alto grado las tres propiedades mencionadas arriba
- En lugar de pasar la responsabilidad de la migración a los dueños de cada servicio, prefieren que un solo equipo lidere la migración
- Esto les permite evitar la alta sobrecarga de coordinación (que lleva a migraciones lentas) y el riesgo de que el proyecto se estanque (lo que lleva a inconsistencias)
- Para que un solo equipo pueda completar una migración en un tiempo razonable, dependen mucho de lo siguiente:
- elecciones tecnológicas clave (por ejemplo, un alto nivel de consistencia y uso de monorepo)
- uso extensivo de automatización (por ejemplo, herramientas de despliegue masivo de servicios y verificaciones automatizadas de rollback)
Migración del SDK de OpenTracing al SDK de OpenTelemetry
- Recientemente llevaron la estrategia de Monzo a la práctica con un proyecto para pasar del SDK de OpenTracing al SDK de OpenTelemetry
- Todos los servicios exportan datos de trazas a Jaeger. Antes lo hacían mediante OpenTracing y el SDK de Jaeger para Go
- Estas librerías ya no se usan y la comunidad se consolidó en torno a OpenTelemetry
- Para sentar las bases de mejoras en su sistema de trazas, primero quisieron reemplazar las librerías en desuso por el SDK de OpenTelemetry
Principios de migración
- Migración centralizada y transparente para los dueños de los servicios. Prefieren una estrategia que pueda ejecutarse de forma centralizada por un solo equipo para minimizar la sobrecarga de coordinación y reducir el riesgo de que la migración se estanque
- Sin tiempo de inactividad. La mayoría de las migraciones involucran servicios críticos para funciones centrales del banco, así que no pueden permitir downtime
- Roll forward gradual y rollback rápido. En cambios grandes, debe ser posible hacer roll forward gradualmente para reducir el radio de impacto si aparece un problema. Pero también debe ser posible revertir todo rápidamente si hace falta
- Regla 80/20 para la automatización. En migraciones a gran escala, normalmente una alta proporción de los cambios encaja en una plantilla común. Esos cambios se pueden automatizar fácilmente. Para los casos de uso más particulares, la automatización ofrece rendimientos decrecientes y es más eficiente resolverlos caso por caso. Para evitar sorpresas desagradables y facilitar el seguimiento del progreso, conviene clasificar por adelantado los cambios necesarios en estas dos categorías
Estrategia de migración
- En Monzo tienen una serie de pasos de migración que usan de forma sistemática
1. Planeación y alineación
- Las migraciones implican un riesgo considerable. No solo afectan una gran cantidad de servicios, sino que además el equipo que ejecuta la migración puede no tener mucho contexto (o ninguno) sobre los servicios que está migrando
- Buscan consistencia entre servicios, pero siempre hay excepciones, y quieren detectar esas sorpresas lo antes posible
- Por eso es muy importante que el proceso de planeación sea transparente y que todos los ingenieros tengan la oportunidad de contribuir
- Para eso tienen dos procesos:
- Propuesta: En Monzo escriben muchas. Prácticamente todo termina compartiéndose en un solo canal de Slack donde cualquiera en la empresa puede opinar
- Revisión de arquitectura: Para los cambios más grandes, hacen reuniones sincrónicas de revisión de arquitectura para profundizar en áreas específicas más polémicas o riesgosas. El objetivo no es obtener aprobación o firma, sino avanzar de forma significativa el estado del diseño para acelerar el proyecto
2. Envolver la librería anterior
-
En lugar de instalar la nueva librería y actualizar el código de los servicios para llamarla, decidieron primero envolver la librería anterior
- Pueden interceptar las llamadas a la librería subyacente y decidir qué implementación usar en función de una configuración dinámica. Esto permite hacer roll forward y rollback fácilmente sin necesidad de volver a desplegar todos los servicios
- La nueva librería tenía tipos/funciones bastante diferentes. Actualizar todos los puntos de llamada requería mucho esfuerzo y, en algunos casos, los beneficios de la nueva API eran mínimos. Al envolver la librería anterior, en estos casos podían mantener una interfaz similar a la anterior para actualizar más fácilmente los puntos de llamada
-
Otras ventajas de envolver la librería:
- pueden instrumentarla con su propia librería de telemetría
- pueden ofrecer una interfaz más opinionada
3. Actualizar los puntos de llamada
-
El uso de esta librería seguía un patrón común:
- había una pequeña cantidad de funciones/tipos referenciados muchas veces a lo largo del codebase
- después venía una larga cola de funciones/tipos referenciados solo en unos pocos lugares
-
En este caso trataron cada uno de forma distinta:
- Para el pequeño número de funciones/tipos referenciados en muchos lugares, automatizaron todo lo posible. Para esta librería, dependieron principalmente de
goplsygorenamepara hacer refactorizaciones automatizadas - Para manejar la larga cola de funciones/tipos referenciados solo en unos pocos lugares, adoptaron un enfoque manual caso por caso. En algunos casos los migraron manualmente. En otros, se dieron cuenta de que podían lograr lo mismo usando APIs más existentes, así que cambiaron a esas. Eso significó que ya no era necesario tratarlos como casos especiales y tuvo el beneficio adicional de mantener pequeña y opinionada la API de la librería wrapper
- Para el pequeño número de funciones/tipos referenciados en muchos lugares, automatizaron todo lo posible. Para esta librería, dependieron principalmente de
-
Además de envolver la librería anterior, también bloquearon la aparición de nuevas dependencias hacia esa librería. Lo hicieron agregando una verificación en CI con semgrep
4. Envolver la nueva librería
- Una vez envuelta la librería anterior, pudieron empezar a agregar la nueva librería detrás de la librería wrapper
- Al principio, la nueva implementación estaba desactivada mediante configuración. Esto significaba que no se esperaba un cambio de comportamiento y podían seguir fusionando cambios gradualmente a la rama principal
5. Despliegue masivo de servicios
- Antes de empezar a activar la nueva implementación, necesitaban asegurarse de que todos los servicios en ejecución pudieran soportarla
- En otros tipos de cambios de librería, puede desplegarse a la vez solo un subconjunto de servicios con la nueva funcionalidad. Pero en el caso de la librería de trazas, si un servicio ya migró para usar la nueva librería, entonces todos los servicios a los que pueda llamar (de forma transitoria) también deben soportar la nueva funcionalidad
- Para gestionar el despliegue de una gran cantidad de servicios, construyeron una herramienta de despliegue masivo capaz de empujar cambios de librerías a todos los servicios como trabajos por lotes asíncronos
- Para mitigar el impacto de posibles despliegues erróneos:
- usan verificaciones automatizadas de rollback
- despliegan primero a los servicios menos críticos. Etiquetaron todos los servicios con un tag de “tier”, y la herramienta de despliegue masivo lo usa para dar prioridad a los despliegues menos riesgosos
6. Control del rollout mediante configuración
- El problema con la herramienta de despliegue masivo es que es relativamente lenta. Lo que de verdad quieren evitar es desplegar todos los servicios y luego descubrir que la nueva librería tiene un problema y no pueden hacer rollback rápido
- Por eso, en vez de desplegar activando la nueva implementación, desplegaron la capacidad de activar la nueva implementación a través del sistema de configuración
- Comparado con un despliegue normal, la ventaja de usar aquí el sistema de configuración es la velocidad. Todos los servicios actualizan su configuración cada 60 segundos, así que si hace falta pueden revertir rápidamente
- También les da mucho más control sobre cuándo se usa la nueva implementación. Por ejemplo, pueden activarla solo para un conjunto específico de usuarios o para un porcentaje aleatorio de solicitudes
- En este caso, eligieron hacer el rollout solo para endpoints de API que pertenecen al equipo, y la activaron con una probabilidad que fue aumentando gradualmente
7. Limpieza
- Una vez completado el cambio total a la nueva implementación, hacen la satisfactoria tarea de eliminar la implementación anterior de la librería wrapper
Superpoderes de migración
- Este tipo de migraciones centralizadas fue posible gracias a decisiones tecnológicas fundamentales que Monzo tomó y a las herramientas en las que sigue invirtiendo
- Tecnología consistente: todos los servicios estaban escritos en Go y usaban la misma versión de la librería anterior. Esto hace mucho más fácil automatizar los cambios. Por ejemplo, solo hace falta una única herramienta de refactorización (en vez de una por lenguaje)
- Monorepo: todo el código de los servicios está en un solo monorepo, lo que hace mucho más fácil realizar refactorizaciones a gran escala en un solo commit. Además, ayuda a mantener la consistencia porque permite aplicar globalmente en CI verificaciones sobre el uso de ciertas librerías
- Despliegue masivo: cuando hay muchos componentes desplegables, se necesita un proceso de despliegue automatizado para empujar cambios de librerías
- Servicio de configuración ligero y flexible: el proceso de despliegue es seguro, pero lento (varios minutos por despliegue). Se necesita un proceso más ligero y flexible para activar/desactivar rápidamente nuevas funcionalidades a gran escala en muchos servicios
Conclusión
- En el pasado intentaron distribuir las migraciones, pero eso inevitablemente llevó a migraciones sin terminar y a mucho esfuerzo de coordinación
- Por eso Monzo tiene una fuerte preferencia por las migraciones centralizadas. Un solo equipo tiene que pagar un costo relativamente alto, pero en general se requiere menos esfuerzo y aumenta mucho la probabilidad de mantener la consistencia
- Este enfoque crea un círculo virtuoso:
- el equipo que ejecuta las migraciones tiene un fuerte incentivo para invertir en herramientas que permitan automatizarlas
- también mantiene la consistencia técnica (lo que facilita crear herramientas)
- aun así, siguen siendo prácticos respecto al grado de automatización: aplican la regla 80/20
- Además de las herramientas en las que Monzo sigue invirtiendo, este enfoque solo es posible gracias a algunas decisiones tecnológicas clave tomadas desde el principio
- principalmente, por el hecho de usar un conjunto tecnológico opinionado y limitado
Aún no hay comentarios.