La montée en charge d'un serveur Mastodon

J'aide à administrer le serveur Mastodon tooting.ch qui est maintenu par l'association à but non lucratif FairSocialNet. Dans ce contexte, après l'acquisition d'un réseau social dont le logo était un oiseau bleu et les premiers chamboulements sur la plateforme, j'ai dû gérer l'allocation des ressources aux différents composants sous-jacents dont la consommation a inévitablement augmenté.

Cet article ne va pas rentrer dans les détails de qu'est-ce que Mastodon ou le réseau global, le Fedivers, aussi appelé la fédération. Il existe suffisamment d'articles et de vidéos sur le web qui expliquent ces deux concepts.

Cet article est écrit un an environ après les faits. Certains fins détails peuvent être omis pour simplifier la lecture de l'article, ou bien je ne me rappelle plus exactement comment ça s'est déroulé.

Les composants de Mastodon

Commençons par donner un peu de contexte avec ce qu'il y a sur la machine virtuelle qui gère ce service. Il y a une base de données PostgreSQL qui s'occupe de stocker les données du serveur local et des autres serveurs de la fédération. Ensuite, il y a toute la brique logicielle de Mastodon, que ce soit du Ruby ou du Javascript:

  • Un serveur web pour répondre aux demandes des utilisateurs ou autres serveurs (requêtes HTTP)
  • Un serveur Websocket pour envoyer les publications en direct aux utilisateurs connectés
  • Un serveur Redis (service externe) pour le cache et les files d'attente du service suivant
  • Un logiciel de gestion de files d'attentes pour exécuter des tâches en fond

Le dernier composant est important au bon fonctionnement du service. En effet, il envoie les publications aux serveurs distants, achève la réception des publications distantes, finalise les envois d'images ou de fichiers, traduit les publications sur demande et génère la prévisualisation des liens, entre autres. Autrement dit, ce composant fait beaucoup de tâches qui contribuent au bon fonctionnement de la plateforme.

En d'autres termes, si ce composant est surchargé, cela se fera sentir directement sur la plateforme sous la forme, par exemple, d'envois de fichiers qui prennent une éternité à être finalisés.

Le calme avant la tempête

Avant, Tooting était un service de petite taille (une centaine d'utilisateurs, je crois ?), quand on le compare à ce qu'il est actuellement. Chaque brique logicielle de la plateforme était configurée "par défaut", ce qui était largement suffisant à l'époque. L'espace disque est resté suffisamment grand pour conserver une copie des médias distants et locaux et les données sauvegardées dans PostgreSQL.

Comme les inscriptions sont derrière une validation manuelle par les administrateurs et modérateurs, histoire de chasser le spam et de se débarrasser du problème en amont, ils ont une idée plus ou moins directe de ce qui allait pouvoir arriver et s'il faut prévoir davantage de ressources. Le nombre d'inscriptions sur une période a déjà fait des sursauts avec ce qui se passait sur un autre bout d'Internet. Cependant, rien d'extraordinaire qui a nécessité une grosse intervention.

Le temps se gâte

Lorsque le rachat de Twitter a été conclu, de nombreuses personnes se sont jetées sur les divers serveurs du réseau global, créant une montée en charge plus ou moins brutale pour tout le monde. Certains serveurs qui arrivaient à capacité pour des raisons techniques ou de taille ont dû se résoudre à fermer les inscriptions libres et les limiter avec des invitations.

Avant le rachat et pour donner un ordre d'idée de la montée en charge soudaine sur Tooting, il y avait environ 20 inscriptions par mois; durant la vague, environ 100 utilisateurs par jour créaient un compte.

Le nouveau propriétaire commence à faire ses modifications qui finiront par résulter au site que l'on connaît. Ces changements contribuent à l'ampleur de la vague de migration qui s'abat déjà sur le Fedivers. Pour Tooting, les problèmes intéressants ne font que commencer.

La tempête, la rivière sort de son lit

Initialement, les conséquences se sont traduites en une augmentation du volume d'e-mails à envoyer, volume qui a explosé le quota de notre prestataire. Le serveur SMTP1 rejetait l'envoi et la tâche revenait au fond de la file d'attente. Pour accommoder le volume qui a explosé, une demande d'augmentation du quota a été faite à Infomaniak, éventuellement accordée. En attendant la réponse, il ne restait plus grand-chose à faire du côté des mails: désactiver l'envoi de mails superflus pour l'administration et modération, et attendre. Au rythme de 100 utilisateurs par jour, des boîtes mail ont pu respirer.

En clair, vous vous inscriviez sur Tooting, vous pouviez aller vous faire un café ou vaquer à vos occupations; la confirmation de l'adresse e-mail utilisée viendra par la poste !

Outre les mails qui avaient de la peine à partir, il y a d'autres conséquences directe d'une file d'attente surchargée. Vous vous souvenez de ce petit bout de phrase ?

Le dernier composant [de la brique logicielle de Mastodon] est important au bon fonctionnement du service

Il s'avère que le compteur de tâches en attente a atteint des sommets et continuait de grimper. La configuration "par défaut" de Sidekiq (j'explique plus bas ce que c'est !) ne suffisait plus. Je liste quelques pépins rencontrés avec une file d'attente qui ne cesse de grandir:

  • Les publications fédérées arrivent "en retard".
  • L'envoi des publications locales est retardée.
  • Le temps d'envoi des images est considérablement rallongé. Techniquement l'image ou la pièce jointe est envoyée, la tâche représentant le traitement était juste au fond de la todo-list.
  • La génération des cartes d'intégration des liens était retardée.
  • Les e-mails qu'on ne présente plus !

La liste n'est pas exhaustive, bien sûr.

Je pourrais continuer à écrire sur les divers problèmes qu'il y a eu sur Tooting, mais on a compris que la rivière est sortie de son lit et cause des inondations un peu partout.

Fonctionnement global de Sidekiq, le gestionnaire de files d'attente

Avant de continuer, il est intéressant de connaître le fonctionnement global du service de file d'attente, Sidekiq de son vrai nom. Quand l'administrateur lance un processus Sidekiq, la tâche principale crée autant de tâches secondaires (aussi appelés thread) que configuré. Le processus principal se charge de récupérer ce qu'il y a à faire depuis un service externe et de les distribuer pour réalisation. Les threads s'occupent de réaliser l'action demandée.

Une fois l'action exécutée correctement et sans erreur, un compteur est augmenté de un et le thread passe à la suite ou attend. En cas de problème, la tâche en erreur est remise dans une file "Tentatives" et sera ré-exécutée ultérieurement. S'il y a trop d'erreurs pour une tâche donnée, elle finit éventuellement dans une file "Mortes" et ne sera plus touchée à moins que l'administrateur les remette manuellement dans le circuit.

Par conséquent, il est dans l'intérêt de l'administrateur de garder une longueur de file d'attente réduite pour que la plateforme reste réactive. Sinon, on se retrouve avec les divers symptômes que j'ai décrit plus haut.

Sidekiq est aussi capable d'exécuter des actions programmées, mais cela ne nous importe pas dans ce contexte.

Gérer la montée en charge

La priorité a été de rétrécir la file d'attente de Sidekiq. Cela pouvait prendre le temps qu'il faudra, mais le compteur de tâches en attente devait descendre.

Une seule chose à faire: augmenter le nombre de threads à une bonne poignée en ne gardant qu'un seul processus principal. En théorie, c'est une bonne idée, en pratique, le chef d'orchestre se retrouve pleinement occupé, sans jamais occuper tous les threads. Une autre solution est de lancer plusieurs processus Sidekiq avec un nombre de threads limité.

Les autres composants n'ont pas eu besoin d'une attention particulière; ils semblaient capables de gérer ce qui leur arrive sans intervention externe. La configuration a été gardée plus ou moins "par défaut".

Durant la "purge" de la liste d'attente de Sidekiq, Tooting était… utilisable ? Ce que je veux dire, une page chargeait correctement, on pouvait consulter les publications et en publier. Naturellement, on reparlera plus tard de l'envoi des publications locales aux autres serveurs. Idem pour les publications distantes. Par contre envoyer une pièce jointe était mission impossible, pour la raison que j'ai citée plus haut. À un moment, je suis passé en force pour vider cette fichue file d'attente en ouvrant les vannes !

Le nouveau rythme de croisière

Pendant que la migration de masse se tassait, l'allocation des ressources à Tooting a été ajustée en augmentant ce qu'il y a à disposition: CPU, RAM, disque. Les exécuteurs de tâches de Sidekiq ont été paramétrés à leur nouveau rythme de croisière: 4 processus avec 15 threads chacun pour le total magique de 60. Cela suffit à traiter les nouvelles tâches et absorber rapidement des petits surplus de travaux qui surgissent.

Le fichier de configuration systemd pour le service a été légèrement modifié pour laisser la possibilité de démarrer des processus Sidekiq de 15 threads "au besoin"; il s'agit maintenant d'un service avec un chablon.

PostgreSQL — le serveur de bases de données — n'a pas vu de gros changements, ni le serveur HTTP2 d'ailleurs. La configuration presque par défaut de ces derniers suffit encore à satisfaire les requêtes entrantes, à ce que je sache.

Finalement, Tooting a "pris cher", mais s'en est quand même "bien sorti" considérant qu'on aurait pu avoir pire. Cette vague de migration est maintenant derrière, nous sommes prêts à gérer une nouvelle, qu'elle soit un peu plus grande ou plus petite.


  1. Protocole qui permet l'envoi et la livraison d'e-mails à leur destinataire; c'est la poste d'Internet ! ↩︎

  2. Le protocole du web. Il vous permet de demander des pages de vos sites préférés dans votre navigateur. D'ailleurs, ce protocole a été utilisé pour demander la page que vous lisez actuellement au serveur web (sans entrer dans les détails). ↩︎