SwordArMor

Faire tenir la charge à une instance mastodon malgré Musk

Si vous lisez ce blog, vous savez sûrement qu’un riche mégalomane étasunien a racheté twitter, et qu’on en attendait pas moins de son mode de gestion. Cela a entraîné un exode vers mastodon, qui ronronnait tranquillement dans son coin jusqu’ici.
Je vais ici partager les différentes actions que j’ai dû entreprendre ces derniers jours afin de maintenir hostux.social à flots. Avant toutes choses, il convient de remercier Leonora Tindall d’avoir écrit Scaling Mastodon in the Face of an Exodus, car cet article m’a beaucoup aidé, pour la configuration sidekiq notamment.

Afin de contextualiser ce billet, hostux.social hébergeait environ 4000 personnes (pour moins de 1000 actives) avant la vague d’arrivées. Le tout tenait sur une VM avec 8 vCPU et 12 Go de RAM.
Je précise également que gérer des serveurs n’est pas mon métier, je suis dans le réseau à la base. Alors même si ça reste « de l’informatique », ce n’est pas quelque chose avec lequel je suis forcément à l’aise, ni qui me fait particulièrement plaisir. (Proposer une alternative libre et décentralisée, même à mon échelle, ça me fait plaisir, par contre.)

La première action afin de gérer l’afflux de personnes a simplement été de fermer les inscriptions, et ce pour une raison totalement non technique, mais par manque de temps pour traiter toutes les demandes. Nous réouvrions les vannes de temps en temps, quand la vague précédente était passée.

Ensuite, quelque chose qui avait l’air d’être une bonne idée mais qui en fait n’en est pas une : supprimer les anciens comptes inactifs afin d’alléger un peu la base de données. Ce n’est en fait pas une bonne idée car celà crée plein de tâches dans le sidekiq et ralenti considérablement l’instance le temps que ça soit traité. Nous sommes monté à plus de deux millions de tâches en attente à cause de ça alors que même durant les périodes de charge dépasser les 100 000 reste rare.

L’étape suivante qui me venait naturellement à l’esprit a été de séparer la VM de base de données des autres services afin qu’il ait de la RAM dédiée qu’il puisse utiliser en cache. Et ceci a été efficace puisque l’intégralité de la RAM a directement été utilisée à cet effet, permettant de dépiler plus vite le sidekiq.

Arrivé ici, l’interface web a commencé a devenir de plus en plus lente, en raison de nombre de requêtes en parallèle à traiter. Nous avions à ce moment déjà plus ou moins doublé le nombre de comptes actifs. J’ai donc augmenté le nombre de threads de puma à 5 et le nombre de processus par thread à 8.

Environment="WEB_CONCURRENCY=8"
Environment="MAX_THREADS=5"

Cela a augmenté le nombre de connexions à la base de données, je suis donc passé de 100 à 500 connexions maximum dans PostgreSQL (histoire d’avoir un peu de marge, changer cette valeur nécessitant de redémarrer le service).

La charge continuant d’augmenter, tous les soirs la queue sidekiq montait à plus de 10 000 tâches avec parfois des pics aux alentours des 20 000, induisant une latence énorme sur la réception de messages d’autres instances. Là j’ai commencé à regarder comment optimiser sidekiq, et le moins que l’on puisse dire, c’est que la littérature n’est pas très fournie. Dans le doute, j’ai décidé de mettre redis dans sa VM à lui, ça ne pouvait pas faire de mal… C’est préconisé dans la doc de mastodon, mais je ne suis pas sûr que ça ait réellement changé grand chose.

Je suis ensuite tombé sur PGTune qui m’a permis d’ajuster quelques paramètres de PostgreSQL, évitant ainsi à sidekiq d’attendre les réponses au lieu de travailler. Ceci a été très efficace, la taille de la queue a été divisée par deux en quelques minutes, mais je restais pourtant à environ un millier de tâches en attente.
Cela a par contre considérablement augmenté la charge de cette VM. Là où elle tournait avec un load average d’environ 5 avant, il a doublé désormais.

Valère (sur IRC) m’a partagé l’aricle de Leonora Tindall cité en introduction qui explique comment configurer son sidekiq de manière efficace (enfin !). J’ai adapté leurs conseils à ma configuration (je n’utilise pas docker mais des units systemd directement) et j’ai ajouté une target systemd afin de ne pas avoir besoin de démarer les six services sidekiq à la main, ce qui nous donne donc ceci :

root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq.target 
[Unit]
Description=mastodon-sidekiq
After=network.target
Wants=mastodon-sidekiq-default-push-pull.service mastodon-sidekiq-mailers.service mastodon-sidekiq-pull-default-push.service mastodon-sidekiq-push-default-pull.service mastodon-sidekiq-scheduler.service mastodon-sidekiq-default-pull-push.service

[Install]
WantedBy=multi-user.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-default-pull-push.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q default -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-default-push-pull.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q default -q push -q pull
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-mailers.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q mailers -q scheduler
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-pull-default-push.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q pull -q default -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-push-default-pull.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q push -q default -q pull
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target


root@hostux:~# cat /etc/systemd/system/mastodon-sidekiq-scheduler.service 
[Unit]
Description=mastodon-sidekiq
After=network.target
PartOf=mastodon-sidekiq.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
ExecStart=/opt/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 -q scheduler
TimeoutSec=15
Restart=always

[Install]
WantedBy=mastodon-sidekiq.target

Avec tout ceci l’instance fonctionne de nouveau aussi bien que mastodon le permet. On verra donc dans sur la duré. Pour donner un ordre de grandeur des ressources utilisées, voici ce que j’ai :

  • VM « web » : 12 Go de RAM disponibles, 6 Go utilisés par les applications, le reste en cache. 8 vCPU disponibles, 2 utilisés
  • VM de DB : 12 Go de RAM disponibles, tout est utilisé en cache, 8 vCPU disponibles, 3 utilisés
  • VM ES : 4 Go de RAM disponibles, 3 utilisés, quasi pas de CPU utilisé
  • VM redis : quelques Mo de RAM utilisés et une utilisation CPU négligeable
L’instance comporte environ 2 500 comptes actifs et génère environ 20 Mbps de trafic. Je suis toujours sur la branche 3, passer en 4 nécessiretra de revoir la configuration sidekiq car une queue supplémentaire a été ajoutée.

Si vous avez des questions, je vous invite à me contacter sur mastodon : @alarig@hostux.social.

Avoir une sorte de flowspec sous linux avec ipset

Internet étant truffé de machines plus ou moins vérolées qui scannent le monde entier, j’ai eu envie de les bloquer à l’échelle complète de mon réseau ; un genre de fail2ban2bgp.
Avec netfilter il n’existe pas de mécanisme pour avoir des règles dynamiques, et donc pas de moyen …

Lire la suite

Remonter le constructeur et le modèle d’un serveur en SNMP

Par défaut, la configuration de net-snmpd ne remonte pas le constructeur et le modèle d’un serveur, ce qui fait que LibreNMS (ou Observium) vont afficher « Generic x86 64-bit » là où on pourrait avoir « Dell Inc. [PowerEdge R510] » ou « HP [ProLiant DL320e Gen8] », ce qui rend plus facile l’inventorisation …

Lire la suite

Installer son instance mastodon sous Gentoo

Suite à l’annonce de Valère d’arrêter les services hostux, et donc l’instance mastodon, j’ai décidé de monter la mienne. La principale raison est que je n’ai pas trouvé d’autre instance en Europe avec un TLSA. Je ne vais pas ici expliquer toute l’installation …

Lire la suite

Désactivation de la souris pour urxvt et terminator (et autres terminaux basés sur VTE)

Depuis quelques temps, le support de la souris est activé par défaut sur certaines applications CLI, notamment vim. Ceci empêche d’utiliser le presse-papier « clic milieu » et ne sert en outre à rien, c’est donc fort ennuyeux.

J’ai un peu cherché comment désactiver le support de la souris …

Lire la suite

Vérification RPKI avec routinator, bird et IOS-XE

Quand on fait partie de la DFZ, on peut en gros annoncer n’importe quel préfixe tant que ça passe les filtres de nos pairs, et on est censé leur faire plus ou moins confiance. Sauf que croire encore à ce système de la confiance en 2020, c’est aussi …

Lire la suite

Remplir la table SNMP ifAlias sous Linux

Il m’arrive encore de jouer avec des routeurs soft, et comme à chaque fois il faut bricoler pour avoir ce que l’on veut, ici avoir une table ifAlias correcte. À une époque je le faisais avec FreeBSD, et j’avais trouvé la parade en utilisant un proxy vers …

Lire la suite

Afficher correctement les AS 32 bits avec mtr

mtr est un outil très pratique qui permet de faire un traceroute, mais en mieux. La sortie est beaucoup plus lisible, et ça utilise de l’ICMP par défaut (au lieu de l’UDP sous Linux/*BSD). Seulement, dans les anciennes versions, les AS 32 bits sont mal gérés : ils …

Lire la suite

Sauvegarder la configuration de ses switches et routeurs avec expect

Si vous ne connaissez pas expect, c’est un outil qui permet d’écrire des scripts en TCL qui iront interagir avec telnet/ssh et d’autres trucs du genre, sans que vous ayez à le faire vous même. C’est donc très pratique pour écrire des scripts qui seront …

Lire la suite

Utiliser socat pour contourner les limiations SSL d’un navigateur moderne

Depuis quelque temps, les navigateurs bloquent les requêtes SSL/TLS utilisant des versions de protocoles et suites cryptographiques jugées trop faibles.

Cependant, on peut avoir besoin de se connecter à de vieux équipements qui eux ne supportent que ces vielles versions ; en l’occurrence, un PDU. En théorie, ils supportent …

Lire la suite

Page 1 / 5 »