3 points par xguru 2024-09-02 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Monzo exploite plus de 2 800 services en MSA et en tire beaucoup de valeur
  • Mais cette architecture comporte aussi des difficultés. L’une d’elles consiste à effectuer des changements de bibliothèque sur l’ensemble des services
  • En général, il faut choisir deux éléments parmi les trois suivants : bibliothèques à jour, versions de bibliothèques cohérentes, faible effort de mise à niveau

Stratégie de migration centralisée

  • Chez Monzo, on estime qu’une approche de migration pilotée de manière centralisée permet d’atteindre à un niveau élevé les trois propriétés ci-dessus
  • Au lieu de transférer la responsabilité de la migration aux propriétaires de services, l’entreprise préfère qu’une seule équipe pilote la migration
  • Cela permet d’éviter un fort surcoût de coordination (qui conduit à des migrations lentes) ainsi que le risque d’abandon du projet (qui mène à des incohérences)
  • Pour qu’une seule équipe puisse terminer une migration dans un délai raisonnable, Monzo s’appuie fortement sur les éléments suivants :
    • des choix technologiques fondamentaux (par ex. un haut niveau de cohérence, l’usage d’un monorepo)
    • un recours massif à l’automatisation (par ex. des outils de déploiement en masse de services, des vérifications automatisées de rollback)

Migration du SDK OpenTracing vers le SDK OpenTelemetry

  • Monzo a récemment mis sa stratégie en pratique à travers un projet de transition du SDK OpenTracing vers le SDK OpenTelemetry
  • Tous les services exportent des données de traçage vers Jaeger. Auparavant, cela se faisait via OpenTracing et le SDK Go de Jaeger
  • Ces bibliothèques sont désormais obsolètes et la communauté s’est unifiée autour d’OpenTelemetry
  • Afin de préparer le terrain pour améliorer le système de traçage, Monzo a d’abord voulu remplacer ces bibliothèques obsolètes par le SDK OpenTelemetry

Principes de migration

  • Migration centralisée et transparente pour les propriétaires de services. Monzo privilégie une stratégie pouvant être exécutée de façon centralisée par une seule équipe afin de minimiser le surcoût de coordination et de réduire le risque d’interruption de la migration
  • Aucune indisponibilité. La plupart des migrations concernent des services critiques pour les fonctions centrales de la banque, il est donc impossible d’accepter un temps d’arrêt
  • Roll forward progressif, rollback rapide. Pour les changements à grande échelle, il faut pouvoir faire un roll forward progressivement afin de limiter le rayon d’impact en cas de problème. Mais il faut aussi pouvoir tout rollback rapidement si nécessaire
  • Règle du 80/20 pour l’automatisation. Dans les migrations à grande échelle, une forte proportion des changements suit généralement un modèle commun. Ces changements sont faciles à automatiser. Pour les cas d’usage plus atypiques, l’automatisation offre un rendement décroissant, et il est plus efficace de les traiter au cas par cas. Pour éviter les mauvaises surprises et faciliter le suivi de l’avancement, il est utile de classer à l’avance les changements nécessaires dans ces deux catégories

Stratégie de migration

  • Chez Monzo, il existe une série d’étapes de migration utilisées de manière systématique

1. Planification et alignement

  • Une migration comporte des risques importants. Non seulement elle affecte un grand nombre de services, mais l’équipe qui l’exécute peut aussi avoir peu, voire pas du tout, de contexte sur les services qu’elle migre
  • Monzo recherche la cohérence entre les services, mais il y a toujours des exceptions, et l’objectif est de repérer ces surprises le plus tôt possible
  • Il est donc essentiel que le processus de planification soit transparent et que tous les ingénieurs aient la possibilité d’y contribuer
  • Pour cela, deux processus sont en place :
    • Proposition : Monzo en rédige beaucoup. Pratiquement tout finit par être partagé dans un canal Slack unique où chacun dans l’entreprise peut donner son avis
    • Revue d’architecture : pour les changements les plus importants, des réunions synchrones de revue d’architecture permettent d’examiner plus en profondeur les zones spécifiques les plus controversées ou risquées. L’objectif n’est pas d’obtenir une approbation ou une validation formelle, mais de faire progresser significativement la conception afin d’accélérer le projet

2. Encapsulation de l’ancienne bibliothèque

  • Au lieu d’installer directement la nouvelle bibliothèque et de mettre à jour le code des services pour l’appeler, Monzo a d’abord décidé d’encapsuler l’ancienne bibliothèque

    1. Cela permet d’intercepter les appels à la bibliothèque sous-jacente et de décider, selon une configuration dynamique, quelle implémentation utiliser. Il devient ainsi facile de faire un roll forward ou un rollback sans avoir à redéployer tous les services
    2. La nouvelle bibliothèque introduisait des types/fonctions très différents. Mettre à jour tous les points d’appel aurait demandé beaucoup d’efforts et, dans certains cas, les bénéfices de la nouvelle API étaient limités. En encapsulant l’ancienne bibliothèque, il a été possible de conserver dans ces cas une interface proche de l’ancienne, ce qui a simplifié la mise à jour des points d’appel
  • Autres avantages de l’encapsulation d’une bibliothèque :

    • possibilité d’instrumenter avec sa propre bibliothèque de télémétrie
    • possibilité de proposer une interface plus prescriptive

3. Mise à jour des points d’appel

  • L’usage de cette bibliothèque suivait un schéma commun :

    • un petit nombre de fonctions/types étaient référencés de nombreuses fois dans toute la base de code
    • puis venait une longue traîne de fonctions/types référencés seulement à quelques endroits
  • Ces deux cas ont été traités différemment :

    • pour le petit nombre de fonctions/types référencés dans de nombreux endroits, l’automatisation a été poussée au maximum. Dans ce cas, Monzo s’est surtout appuyé sur gopls et gorename pour effectuer des refactorings automatisés
    • pour traiter la longue traîne de fonctions/types référencés seulement à quelques endroits, Monzo a adopté une approche manuelle au cas par cas. Dans certains cas, la migration a été faite manuellement. Dans d’autres, l’équipe s’est rendu compte qu’il était possible de faire la même chose avec une API plus existante, et a donc basculé vers celle-ci. Cela évitait de devoir continuer à traiter ces cas comme des cas particuliers, avec l’avantage supplémentaire de garder l’API de la bibliothèque wrapper petite et prescriptive
  • En plus d’encapsuler l’ancienne bibliothèque, Monzo a aussi empêché l’apparition de nouvelles dépendances vers celle-ci. Cela a été fait en ajoutant une vérification CI avec semgrep

4. Encapsulation de la nouvelle bibliothèque

  • Une fois l’ancienne bibliothèque encapsulée, il devient possible de commencer à ajouter la nouvelle bibliothèque derrière la bibliothèque wrapper
  • Au départ, la nouvelle implémentation était désactivée via la configuration. Cela signifie qu’aucun changement de comportement n’était attendu et qu’il était possible de continuer à fusionner progressivement les changements dans la branche principale

5. Déploiement en masse des services

  • Avant de commencer à activer la nouvelle implémentation, il faut s’assurer que tous les services en cours d’exécution peuvent la prendre en charge
  • Pour d’autres types de changements de bibliothèque, il est possible de ne déployer qu’un sous-ensemble des services dotés de la nouvelle fonctionnalité à un moment donné. Mais dans le cas d’une bibliothèque de traçage, si un service a été migré pour utiliser la nouvelle bibliothèque, alors tous les services qu’il peut appeler (de manière transitoire) doivent eux aussi prendre en charge la nouvelle fonctionnalité
  • Pour gérer le déploiement d’un grand nombre de services, Monzo a construit un outil de déploiement en masse capable de pousser les changements de bibliothèque vers tous les services sous forme de tâches batch asynchrones
  • Pour atténuer l’impact de déploiements potentiellement erronés :
    • utilisation de vérifications automatisées de rollback
    • déploiement d’abord sur les services les moins critiques. Tous les services portent un tag de « tier », et l’outil de déploiement en masse l’utilise pour prioriser les déploiements les moins risqués

6. Contrôle du rollout par la configuration

  • Le problème de l’outil de déploiement en masse est qu’il est relativement lent. Ce que Monzo veut absolument éviter, c’est d’avoir déployé tous les services avant de découvrir qu’il y a un problème avec la nouvelle bibliothèque sans pouvoir rollback rapidement
  • Ainsi, au lieu de déployer en activant directement la nouvelle implémentation, l’équipe a déployé la capacité à activer la nouvelle implémentation via le système de configuration
  • L’avantage d’utiliser ici le système de configuration par rapport à un déploiement classique est la rapidité. Tous les services rafraîchissent leur configuration toutes les 60 secondes, ce qui permet un rollback rapide si nécessaire
  • Cela offre aussi bien plus de contrôle sur le moment où la nouvelle implémentation est utilisée. Par exemple, elle peut être activée uniquement pour un ensemble spécifique d’utilisateurs ou pour un pourcentage aléatoire de requêtes
  • Dans ce cas, l’équipe a choisi de ne faire le rollout que pour les endpoints API dont elle était propriétaire, avec une activation selon une probabilité progressivement croissante

7. Nettoyage

  • Une fois le basculement complet vers la nouvelle implémentation effectué, il reste la tâche satisfaisante de supprimer l’ancienne implémentation de la bibliothèque wrapper

Superpouvoirs de migration

  • Ce type de migration centralisée n’est possible chez Monzo que grâce à des choix technologiques fondamentaux et à des outils dans lesquels l’entreprise continue d’investir
  • Technologie cohérente : tous les services étaient écrits en Go et utilisaient la même version de l’ancienne bibliothèque. Cela facilite grandement l’automatisation des changements. Par exemple, un seul outil de refactoring suffit, au lieu d’en avoir un par langage
  • Monorepo : tout le code des services se trouve dans un monorepo unique, ce qui rend les refactorings à grande échelle bien plus faciles à réaliser dans un seul commit. Cela permet aussi d’imposer globalement dans les vérifications CI l’usage de certaines bibliothèques, ce qui aide à maintenir la cohérence
  • Déploiement en masse : lorsqu’il y a beaucoup de composants déployables, un processus de déploiement automatisé est nécessaire pour pousser les changements de bibliothèque
  • Service de configuration léger et flexible : le processus de déploiement est sûr, mais lent (plusieurs minutes par déploiement). Un processus plus léger et plus flexible est nécessaire pour activer/désactiver rapidement et immédiatement de nouvelles fonctionnalités sur un grand nombre de services

Conclusion

  • Par le passé, Monzo a essayé de distribuer les migrations, mais cela a inévitablement conduit à des migrations inachevées et à beaucoup d’efforts de coordination
  • C’est pourquoi Monzo préfère fortement les migrations centralisées. Une équipe doit en assumer un coût relativement élevé, mais globalement cela demande moins d’efforts et augmente fortement les chances de préserver la cohérence
  • Cette approche crée un cercle vertueux :
    • l’équipe qui exécute les migrations est fortement incitée à investir dans des outils pour les automatiser
    • elle maintient également la cohérence technique, ce qui facilite la création de ces outils
    • tout en restant pragmatique sur le degré d’automatisation, avec l’application de la règle du 80/20
  • Au-delà des outils dans lesquels Monzo continue d’investir, cette approche n’est possible que grâce à quelques choix technologiques clés faits dès le départ
    • principalement le fait d’utiliser un ensemble technologique prescriptif et limité

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.