Évolution de Microsoft Teams sur Azure – Opérer à l’échelle mondiale en temps de pandémie

Publié le 16 juin, 2020

Chief Technology Officer, Microsoft Azure

« La pandémie de COVID-19 a remis en question notre façon de travailler, d’étudier et d’échanger avec les autres. Comme beaucoup d’entre nous, je m’appuie sur Microsoft Teams pour communiquer avec mes collègues. Dans ce billet, nos amis du groupe produit Microsoft Teams,Rish Tandon (vice-président corporate), Aarthi Natarajan (manager ingénierie de groupe) et Martin Taillefer (architecte), partagent certaines de leurs apprentissages concernant la gestion et la mise à l’échelle d’une application de productivité sécurisée de classe Entreprise. » - Mark Russinovich, directeur technique, Azure


 

La mise à l’échelle, la résilience et les performances ne se produisent pas du jour au lendemain. Cela nécessite des efforts délibérés et soutenus, jour après jour, ainsi qu’une mentalité orientée performances pour créer des produits qui enchantent nos utilisateurs. Depuis son lancement, Teams a connu une forte croissance : du lancement en 2017 aux 13 millions d’utilisateurs quotidiens en juillet 2019 pour atteindre 20 millions en novembre 2019. En avril, nous avons annoncé que Teams dispose de plus de 75 millions d’utilisateurs actifs quotidiens, de 200 millions de participants quotidiens à des réunions et de 4,1 milliards de minutes de réunion quotidiennes. Nous pensions que nous étions habitués aux efforts nécessaires pour mettre à l’échelle le service à un tel rythme, étant donné la croissance rapide qu’a connue Teams jusqu’à ce jour. La crise du COVID-19 a remis en cause ce postulat. Cette expérience nous permettrait-elle de faire en sorte que le service soit opérationnel au milieu d’une période de croissance qui était jusqu’ici inconcevable ?

Des bases solides

Teams s’appuie sur une architecture de microservices : plusieurs centaines de microservices fonctionnent ensemble pour fournir les nombreuses fonctionnalités de notre produit, notamment la messagerie, les réunions, les fichiers, le calendrier et les applications. L’utilisation de microservices permet à chacune de nos équipes de travailler et de publier leurs modifications indépendamment.

Azure est la plateforme cloud qui sous-tend tous les services cloud de Microsoft, notamment Microsoft Teams. Nos charges de travail s’exécutent sur des machines virtuelles Azure, nos anciens services étant déployés via Services cloud Azure et les plus récents via Azure Service Fabric. Notre pile de stockage principale est Azure Cosmos DB, certains services utilisant Stockage Blob Azure. Nous comptons sur Azure Cache pour Redis pour optimiser le débit et la résilience. Nous tirons parti de Traffic Manager et d’Azure Front Door pour router le trafic là où nous le voulons. Nous utilisons Stockage File d’attente et Event Hubs pour communiquer et nous dépendons d’Azure Active Directory pour gérer nos locataires et nos utilisateurs.

 

	Schéma montrant qu’Azure est la plateforme qui sous-tend les services Teams et le service principal Office 365

 

Bien que cette publication traite principalement de notre backend cloud, il est à noter que les applications clientes Teams utilisent également des modèles et des frameworks de conception modernes, offrant ainsi une expérience utilisateur riche et une prise en charge des expériences connectées par intermittence ou hors connexion. La capacité principale à mettre à jour nos clients rapidement et en tandem avec le service est un composant clé d’une itération rapide. Si vous souhaitez en apprendre davantage sur notre architecture, consultez cette session de la conférence Microsoft Ignite 2019.

Développement agile

Nos pipelines CI/CD sont basés sur Azure Pipelines. Nous utilisons une stratégie de déploiement basée sur des anneaux avec des contrôles basés sur une combinaison de tests de bout en bout automatisés et de signaux de télémétrie. Nos signaux de télémétrie s’intègrent aux pipelines de gestion des incidents pour fournir des alertes sur les mesures définies par le client et le service. Nous nous appuyons fortement sur Azure Data Explorer pour l’analytique.

Nous utilisons par ailleurs un pipeline d’expérimentation avec des tableaux de bord qui évaluent le comportement des fonctionnalités par rapport aux métriques de produit clés telles que le taux d’incident, la consommation de mémoire, la réactivité de l’application, les performances et l’engagement des utilisateurs. Cela nous permet de déterminer si les nouvelles fonctionnalités fonctionnent comme nous le souhaitons.

Tous nos services et clients utilisent un service centralisé de gestion de la configuration. Ce service fournit l’état de configuration permettant d’activer et de désactiver les fonctionnalités produit, d’ajuster les valeurs de durée de vie du cache, de contrôler les fréquences des demandes réseau et de définir des points de terminaison réseau pour contacter les API. Cela fournit un framework flexible pour « effectuer un lancement dans l’ombre » et pour effectuer des tests A/B, de façon à mesurer précisément l’impact de nos modifications afin de garantir leur sécurité et leur efficacité pour tous les utilisateurs.

Stratégies de résilience clés

Nous utilisons plusieurs stratégies de résilience dans notre flotte de services :

  • Systèmes de tolérance de panne actif/actif : un système de tolérance de panne actif/actif est défini comme au moins deux chemins d’accès hétérogènes indépendants des opérations, chaque chemin d’accès fournissant le trafic actif à un état stable et étant capable de servir 100 % du trafic attendu, tout en tirant parti de la sélection de chemin du client et du protocole pour un basculement transparent. Nous adoptons cette stratégie dans les cas où il existe un très important domaine de défaillance ou immense impact sur le client avec un coût raisonnable pour justifier la création et la gestion de systèmes hétérogènes. Par exemple, nous utilisons le système DNS Office 365 pour tous les domaines clients visibles de l’extérieur. En outre, les données statiques de la classe CDN sont hébergées sur Azure Front Door et Akamai.
  • Caches optimisés pour la résilience : nous exploitons largement les caches entre nos composants, aussi bien pour les performances que pour la résilience. Les caches permettent de réduire la latence moyenne et de fournir une source de données en cas d’indisponibilité d’un service en aval. Conserver des données dans des caches pendant une longue période présente des problèmes d’actualisation des données. Cela constitue toutefois la meilleure défense contre les défaillances en aval. Nous portons notre attention sur le délai d’actualisation de nos données de cache ainsi que sur la durée de vie (TTL). En définissant une durée de vie longue et un délai d’actualisation plus faible, nous pouvons affiner la façon dont les données sont actualisées et la durée pendant laquelle nous souhaitons que les données restent chaque fois qu’une dépendance en aval échoue.
  • Disjoncteur : il s’agit d’un modèle de conception courant qui empêche un service d’effectuer une opération qui est susceptible d’échouer. Il permet au service en aval de récupérer sans être submergé par les demandes de nouvelle tentative. Il améliore également la réponse d’un service quand ses dépendances rencontrent des problèmes, ce qui permet au système d’être plus tolérant aux conditions d’erreur.
  • Isolation sous forme de cloisonnement : nous partitionnons certains de nos services critiques dans des déploiements totalement isolés. En cas de problème dans un déploiement, l’isolation sous forme de cloisonnement est conçue pour aider les autres déploiements à continuer à fonctionner. Cette atténuation préserve les fonctionnalités pour le plus grand nombre de clients possible.
  • Limitation du débit au niveau de l’API : nous garantissons que nos services critiques peuvent limiter les demandes au niveau de l’API. Ces limites de débit sont gérées via le système centralisé de gestion de la configuration décrit ci-dessus. Cette fonctionnalité nous a permis de limiter le débit des API non critiques au plus fort de la demande lors de la pandémie de COVID-19.
  • Modèles de nouvelles tentatives efficaces : nous garantissons et validons que tous les clients d’API implémentent une logique de nouvelle tentative efficace, ce qui empêche les pics de trafic lors d’une défaillance du réseau.
  • Délais d’expiration : une utilisation cohérente de la sémantique de délai d’expiration empêche le travail d’être retardé lorsqu’une dépendance en aval rencontre des problèmes.
  • Gestion sans heurts des défaillances réseau : nous avons réalisé des investissements à long terme pour améliorer notre expérience client en mode hors connexion ou en cas de connexions médiocres. Des améliorations majeures dans ce domaine ont été lancées en production juste avant l’accélération de la demande lors de la crise du COVID-19. Cela a permis à notre client de fournir une expérience cohérente, quelle que soit la qualité du réseau.

Si vous avez vu les modèles de conception cloud Azure, un grand nombre de ces concepts peuvent vous être familiers.  Nous utilisons également amplement la bibliothèque Polly dans nos microservices, car elle fournit des implémentations pour certains de ces modèles.

Notre architecture fonctionnait bien, l’utilisation de Teams augmentait mois après mois et la plateforme s’adaptait facilement à la demande. Toutefois, la scalabilité est à prendre en compte en continu. Elle nécessite une attention permanente pour traiter les comportements émergents qui se manifestent dans les systèmes complexes.

Lorsque des mesures de confinement COVID-19 ont commencé à être mises en place à travers le monde, nous avions besoin de tirer parti de la flexibilité architecturale intégrée à notre système et de tourner tous les boutons possibles pour répondre efficacement à la demande qui augmentait rapidement.

Prévision de la capacité

Comme pour tout produit, nous créons et itérons constamment des modèles pour anticiper là où la croissance aura lieu, à la fois en termes d’utilisateurs bruts et de modèles d’utilisation. Les modèles sont basés sur les données historiques, les modèles cycliques, les nouveaux clients importants entrants et un large éventail d’autres signaux.

Dès que l’urgence s’est fait sentir, il est devenu évident que nos précédents modèles de prévision devenaient rapidement obsolètes. Nous devions donc en créer de nouveaux qui tenaient compte de la croissance exceptionnelle de la demande à l’échelle mondiale. Nous observions de nouveaux modèles d’utilisation de la part d’utilisateurs existants, une nouvelle utilisation de la part d’utilisateurs existants mais dormants, et de nombreux nouveaux utilisateurs intégrant le produit, tous en même temps. De plus, nous avons dû prendre des décisions accélérées concernant les ressources pour faire face à d’éventuels goulots d’étranglement en matière de calcul et de mise en réseau. Nous utilisons plusieurs techniques de modélisation prédictive (ARIMA, cumulatif, multiplicatif, logarithmique). À cela, nous avons ajouté des plafonds de base par pays pour éviter une prévision excessive. Nous avons ajusté les modèles en essayant de comprendre les modèles d’inflexion et de croissance par utilisation par secteur et zone géographique. Nous avons incorporé des sources de données externes, notamment l’étude de Johns Hopkins sur les dates d’impact du COVID-19 par pays, afin d’augmenter les prévisions de pics de charge pour les régions à goulot d’étranglement.

Tout au long du processus, nous avons fait preuve de prudence et avons privilégié le sur-provisionnement. Néanmoins, à mesure que les modèles d’utilisation se sont stabilisés, nous avons également revu les chiffres à la baisse si nécessaire.

Mise à l’échelle de nos ressources de calcul

En général, nous concevons Teams pour résister aux catastrophes naturelles. L’utilisation de plusieurs régions Azure nous aide à atténuer les risques en cas de problème dans un centre de données et en cas d’interruptions dans une zone géographique majeure. Cependant, cela signifie que nous provisionnons des ressources supplémentaires pour être prêt à assumer la charge d’une région affectée au cours d’une telle éventualité. Pour effectuer un scale-out, nous avons rapidement étendu le déploiement de chaque microservice critique à des régions supplémentaires dans chaque zone géographique Azure majeure. En augmentant le nombre total de régions par zone géographique, nous avons diminué la quantité totale de capacité disponible que chaque région devait détenir pour absorber la charge d’urgence, réduisant ainsi nos besoins totaux en capacité. Le traitement de la charge à cette nouvelle échelle nous a permis de découvrir plusieurs façons d’améliorer notre efficacité :

  • Nous avons constaté qu’en redéployant certains de nos microservices pour favoriser un plus grand nombre de clusters de calcul plus petits, nous avons pu éviter certaines considérations de mise à l’échelle par cluster, nous avons accéléré nos déploiements et nous avons fourni un équilibrage de charge plus précis.
  • Auparavant, nous dépendions des types de machines virtuelles spécifiques que nous utilisons pour nos différents microservices. En étant plus flexible concernant le type de machine virtuelle ou de processeur, et en nous concentrant sur la puissance de calcul ou la mémoire globale, nous avons pu utiliser plus efficacement les ressources Azure dans chaque région.
  • Nous avons trouvé des opportunités d’optimisation dans le code de notre service lui-même. Par exemple, quelques améliorations simples ont conduit à une réduction substantielle de la quantité de temps processeur nécessaire pour générer des avatars (ces petites bulles avec des initiales, utilisées quand aucune image utilisateur n’est disponible).

Optimisation de la mise en réseau et du routage

La majeure partie de la consommation de capacité de Teams se produit en journée pour une zone géographique Azure donnée, ce qui entraîne des ressources inactives la nuit. Nous avons implémenté des stratégies de routage pour tirer parti de cette capacité inactive (tout en continuant à respecter les exigences de conformité et de résidence des données) :

  • Le travail en arrière-plan non interactif est migré dynamiquement vers la capacité actuellement inactive. Cela se fait en programmant des routes propres à l’API dans Azure Front Door pour garantir que le trafic arrive au bon endroit.
  • Le trafic engendré par les appels et les réunions était routé dans plusieurs régions pour gérer les pics. Nous avons utilisé Azure Traffic Manager pour répartir efficacement la charge, en tirant parti des modèles d’utilisation observés. Nous avons également créé des runbooks qui effectuaient un équilibrage de charge en journée pour empêcher la limitation du réseau étendu (WAN).

Une partie du trafic client de Teams se termine dans Azure Front Door. Cependant, comme nous avions déployé plus de clusters dans plus de régions, nous avons constaté que les nouveaux clusters ne recevaient pas suffisamment de trafic. Il s’agissait d’un artefact de la distribution de l’emplacement de nos utilisateurs et de l’emplacement des nœuds Azure Front Door. Pour remédier à cette répartition inégale du trafic, nous avons utilisé la capacité d’Azure Front Door pour router le trafic au niveau d’un pays. Dans cet exemple, vous pouvez voir ci-dessous que nous obtenons une meilleure répartition du trafic après avoir routé le trafic supplémentaire en France vers la région Royaume-Uni Ouest pour l’un de nos services.

	Graphique montrant une meilleure répartition du trafic après avoir routé le trafic supplémentaire en France vers la région Royaume-Uni Ouest 
Figure 1 : Amélioration de la distribution du trafic après routage du trafic entre les régions.

Améliorations du cache et du stockage

Nous utilisons beaucoup de caches distribués, dont beaucoup sont très volumineux. Au fur et à mesure que notre trafic augmentait, la charge qui pesait sur nos caches augmentait au point que les caches individuels ne pouvaient pas être mis à l’échelle. Nous avons déployé quelques modifications simples ayant un impact significatif sur notre utilisation du cache :

  • Nous avons commencé à stocker l’état du cache dans un format binaire plutôt que au format JSON brut. Nous avons utilisé le format de tampon de protocole pour cela.
  • Nous avons commencé à compresser les données avant de les envoyer dans le cache. Nous avons utilisé la compression LZ4 en raison de son excellent ratio vitesse/compression.

Nous avons pu réduire de 65 % la taille de la charge utile, de 40 % le temps de désérialisation et de 20 % le temps de sérialisation. Cela constitue une vraie victoire.

Nos investigations ont révélé que plusieurs de nos caches avaient des paramètres de durée de vie trop agressifs, ce qui entraînait une éviction de données superflue. L’augmentation de ces durée de vie a permis à la fois de réduire la latence moyenne et la charge sur les systèmes en aval.

Dégradation intentionnelle (coupures de fonctionnalités)

Comme nous ne savions pas vraiment jusqu’où nous devions aller, nous avons décidé qu’il était prudent de mettre en place des mécanismes qui nous permettaient de réagir rapidement aux pics de demande imprévus. Et ce afin de nous faire gagner du temps pour mettre en ligne une capacité Teams supplémentaire.

Toutes les fonctionnalités n’ont pas la même importance pour nos clients. Par exemple, l’envoi et la réception de messages sont plus importants que la possibilité de voir que quelqu’un d’autre tape actuellement un message. Pour cette raison, nous avons désactivé l’indicateur de frappe pour une durée de deux semaines pendant que nous travaillions à l’extension de nos services. Cela a réduit le pic de trafic de 30 % vers certaines parties de notre infrastructure.

Nous utilisons normalement la prérécupération agressive sur de nombreuses couches de notre architecture, de sorte que les données nécessaires sont à portée de main, ce qui réduit la latence moyenne de bout en bout. La prérécupération peut cependant être coûteuse, car elle entraîne une certaine quantité de travail gaspillé lors de l’extraction de données qui ne seront jamais utilisées. Elle nécessite par ailleurs des ressources de stockage pour contenir les données prérécupérées. Dans certains scénarios, nous avons décidé de désactiver la prérécupération, en libérant de la capacité sur certains de nos services au prix d’une latence plus élevée. Dans d’autres cas, nous avons augmenté la durée des intervalles de synchronisation de prérécupération. Par exemple, nous avons supprimé la prérécupération du calendrier sur mobile, ce qui a réduit le volume de demandes de 80 % :
 

	Graphique indiquant que la suppression de la prérécupération du calendrier a réduit le volume des demandes sur mobile de 80 %
Figure 2 : Désactiver la prérécupération des détails des événements du calendrier sur les mobiles.

Gestion des incidents

Bien que nous ayons un processus de gestion des incidents mature que nous utilisons pour suivre et entretenir l’intégrité de notre système, cette expérience était différente. Nous avons eu affaire à une énorme augmentation du trafic et nos ingénieurs et collègues eux-mêmes étaient confrontés à des problèmes personnels et émotionnels pendant qu’ils s’adaptaient au télétravail.

Pour veiller à prendre en charge nos clients et également nos ingénieurs, nous avons mis en place quelques modifications :

  • Nous avons basculé nos rotations de gestion des incidents d’une cadence hebdomadaire à une cadence quotidienne.
  • Chaque ingénieur d’astreinte a eu au moins 12 heures de congé entre deux shifts.
  • Nous avons fait appel à des gestionnaires d’incidents supplémentaires dans l’ensemble de l’entreprise.
  • Nous avons différé toutes les modifications non critiques dans nos services.

Avec ces modifications, nous avons pu nous assurer que tous nos gestionnaires d’incidents et nos ingénieurs d’astreinte avaient suffisamment de temps pour se concentrer sur leurs besoins personnels tout en répondant aux besoins de nos clients.

L’avenir de Teams

Il est fascinant de regarder en arrière et de se demander ce que cette situation aurait été si elle s’est produite il y a quelques années. Sans le cloud computing, il aurait été impossible d’effectuer une mise à l’échelle comme nous l’avons fait. Ce que nous pouvons faire aujourd’hui en modifiant simplement les fichiers de configuration pouvait auparavant nécessiter d’acheter de nouveaux équipements ou même de nouveaux bâtiments. À mesure que la situation de mise à l’échelle actuelle se stabilise, nous regardons vers l’avenir. Nous pensons qu’il existe de nombreuses opportunités d’améliorer notre infrastructure :

  • Nous prévoyons de passer des déploiements basés sur des machines virtuelles à des déploiements basés sur des conteneurs à l’aide d’Azure Kubernetes Service. Cela va nous permettre de réduire nos coûts d’exploitation, d’améliorer notre agilité et de nous aligner sur le secteur.
  • Nous pensons réduire au minimum l’utilisation de REST et favoriser des protocoles binaires plus efficaces tels que gRPC. Nous allons remplacer plusieurs instances d’interrogation dans le système par des modèles plus efficaces basés sur les événements.
  • Nous adoptons systématiquement les pratiques d’ingénierie du chaos pour garantir que tous les mécanismes que nous mettons en place pour que notre système de fiabilité soient toujours entièrement fonctionnels et prêts à être utilisés.

En gardant notre architecture alignée sur les approches du secteur et en tirant parti des meilleures pratiques de l’équipe Azure, lorsque nous avions besoin de demander de l’aide, les experts pouvaient nous aider à résoudre rapidement les problèmes allant de l’analyse des données à la supervision, de l’optimisation des performances à la gestion des incidents. Nous sommes reconnaissants de l’ouverture d’esprit de nos collègues chez Microsoft et au sein de la communauté étendue de développement de logiciels. Bien que les architectures et les technologies soient importantes, c’est l’équipe de personnes qui est à vos côtés qui garantit l’intégrité de vos systèmes.

 


Publication associée : Azure répond à la crise du COVID-19.
Article associé :
Augmentation de la capacité d’Azure pour aider les clients - Le rôle de Microsoft pendant la pandémie de COVID-19.