14 KiB
title | description | menu | ||||||
---|---|---|---|---|---|---|---|---|
Amélioration des performances | Comment améliorer Mastodon de manière horizontale pour gérer plus de requêtes |
|
Gérer la simultanéité des processus
Mastodon possède trois types de processus :
- Web (Puma)
- API de streaming
- Tâches de fond (Sidekiq)
Web (Puma)
Le processus web sert les requêtes HTTP à durée de vie courte sur la majorité de l'application. Les variables d'environnement suivantes contrôlent ce processus :
WEB_CONCURRENCY
définit le nombre de processus créésMAX_THREADS
définit le nombre de threads par processus
Les threads se partagent la mémoire RAM du processus parent. Les processus se réservent de la mémoire RAM, même s'ils peuvent en partager entre eux via le copy-on-write. Un plus grand nombre de threads va faire monter votre processeur à 100% d'utilisation plus rapidement, un plus grand nombre de processus va remplir votre RAM plus rapidement.
Ces valeurs agissent sur le nombre de requêtes HTTP qui pourront être traitées simultanément.
En termes de rendement, avoir plus de processus est mieux qu'avoir plus de threads.
API de streaming
L'API de streaming gère les connexions HTTP et WebSocket à durée de vie longue, à travers lesquelles les clients reçoivent en temps réel les mises à jour de statuts. Les variables d'environnement suivantes contrôlent ce processus :
STREAMING_CLUSTER_NUM
définit le nombre de processus créésSTREAMING_API_BASE_URL
définit l'URL de l'API de streaming
Un seul processus peut gérer un nombre considérable de connexions. L'API de streaming peut être hébergée sur un sous-domaine différent si vous le voulez, par exemple pour gagner du temps lorsque nginx relaie les connexions.
Tâches de fond (Sidekiq)
Beaucoup de tâches dans Mastodon sont reléguées à l'arrière-plan afin de s'assurer que les requêtes HTTP soient rapidement traitées, et pour empêcher que des requêtes HTTP abandonnées n'affectent l'exécution de ces tâches. Sidekiq est un processus unique, avec un nombre définissable de threads.
Nombre de threads
Tandis que le nombre de threads dans le processus Web a un impact sur la réactivité de l'instance Mastodon pour l'utilisateur·ice final·e, le nombre de threads alloués aux tâches de fond a un impact sur la rapidité à laquelle les posts seront envoyés de l'instance aux autres, sur le temps qu'il faudra pour qu'un courriel soit expédié, etc.
Le nombre de threads n'est pas défini par une variable d'environnement ici, mais par un argument lors de l'invocation de Sidekiq, par exemple :
bundle exec sidekiq -c 15
ferait démarrer le processus sidekiq avec 15 threads. Gardez à l'esprit que chaque thread doit être en mesure de se connecter à la base de données PostgreSQL, ce qui signifie que le pool de connexions à la base de données doit être suffisamment large pour supporter tous les threads. La taille de ce pool est définie avec l'environnement de variable DB_POOL
et doit être au moins égale au nombre de threads.
Queues
Sidekiq utilise différentes queues pour les tâches d'importance variable, où l'importance est définie par l'impact sur l'expérience utilisateur qu’aurait une queue dysfonctionnelle, par ordre décroissant.
Queue | Importance |
---|---|
default |
Toutes les tâches qui affectent les utilisateur·ice·s de l'instance |
push |
La délivrance de contenus aux autres instances |
mailers |
L'envoi des courriels |
pull |
La récupération de contenus depuis d'autres instances |
Les queues par défaut et leurs priorités sont stockées dans config/sidekiq.yml
, mais elles peuvent être outrepassées lors de l'invocation de Sidekiq via la ligne de commandes, par exemple :
bundle exec sidekiq -q default
n'exécutera que la queue default
.
Sidekiq travaille d'une certaine façon avec les queues, il vérifie d'abord s'il y a des tâches à effectuer dans la première queue, et s'il n'y en a pas, il vérifie dans la queue suivante. Cela signifie que, si la première queue est pleine à craquer, les autres queues devront patienter.
En guise de solution, il est possible de démarrer plusieurs processus Sidekiq pour assurer une vraie exécution parallèle des queues, par exemple en créant plusieurs services systemd pour Sidekiq avec différents arguments.
Mise en commun des transactions avec PgBouncer
Pourquoi vous avez potentiellement besoin de PgBouncer
Si vous commencez à manquer de connexions vers PostgreSQL (la valeur par défaut étant de 100 connexions), alors vous pourriez trouver en PgBouncer une bonne solution. Cette section détaille des pièges récurrents ainsi que des exemples de configurations optimales pour Mastodon.
Notez que vous pouvez aller voir "PgHero" dans la partie administration de votre instance pour savoir combien de connexions à PostgreSQL sont actuellement utilisées. Habituellement, Mastodon utilise autant de connexions qu'il y a de threads dans Puma, Sidekiq et l'API de streaming réunis.
Installer PgBouncer
Sur Debian et Ubuntu:
sudo apt install pgbouncer
Configurer PgBouncer
Définir un mot de passe
Tout d'abord, si l'utilisateur mastodon
dans PostgreSQL n'a pas de mot de passe, vous devrez en définir un.
Voilà comment vous pourriez redéfinir le mot de passe :
psql -p 5432 -U mastodon mastodon_production -w
Puis (évidemment, utilisez un autre mot de passe que le mot "password") :
ALTER USER mastodon WITH PASSWORD 'password';
Puis faites \q
pour quitter.
Configurer userlist.txt
Modifiez /etc/pgbouncer/userlist.txt
Du moment que vous spécifiez un utilisateur et un mot de passe dans pgbouncer.ini plus tard, les valeurs dans userlist.txt n'ont pas besoin de correspondre à de vrais utilisateurs dans PostgreSQL. Vous pouvez définir arbitrairement des utilisateurs et leurs mots de passe, mais vous pouvez aussi réutiliser les "vrais" identifiants pour plus de simplicité. Ajoutez l'utilisateur mastodon
dans le fichier userlist.txt
:
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
On utilise ici le schéma MD5, où le mot de passe est juste la somme MD5 de mot de passe + utilisateur
avec le préfixe md5
. Par exemple, pour générer le hash avec l'utilisateur mastodon
et le mot de passe password
, vous pouvez faire :
# Ubuntu, Debian, etc.
echo -n "passwordmastodon" | md5sum
# macOS, OpenBSD, etc.
md5 -s "passwordmastodon"
Et vous n'avez plus qu'à ajouter md5
au début de la ligne.
Vous voudrez aussi créer un administrateur pgbouncer
afin de vous connecter à la base de données administrateur PgBouncer. Voici un exemple de fichier userlist.txt
:
"mastodon" "md5d75bb2be2d7086c6148944261a00f605"
"pgbouncer" "md5a45753afaca0db833a6f7c7b2864b9d9"
Dans les deux cas le mot de passe est password
.
Configurer pgbouncer.ini
Modifiez /etc/pgbouncer/pgbouncer.ini
Ajoutez une ligne en dessous de [databases]
qui liste les bases de données PostgreSQL auxquelles vous voulez vous connecter. PgBouncer utilisera ici le même utilisateur/mot de passe et le même nom de base de données pour se connecter à PostgreSQL :
[databases]
mastodon_production = host=127.0.0.1 port=5432 dbname=mastodon_production user=mastodon password=password
Les variables listen_addr
et listen_port
indiquent à PgBouncer sur quelle adresse/port accepter les connexions. Ces valeurs par défaut sont convenables.
listen_addr = 127.0.0.1
listen_port = 6432
Mettez md5
comme valeur pour auth_type
(en supposant que vous utilisez le format MD5 dans userlist.txt
) :
auth_type = md5
Assurez-vous que l'utilisateur pgbouncer
est un administrateur :
admin_users = pgbouncer
Ce qui suit est très important ! Le mode par défaut du pool de connexions est "par session", tandis que Mastodon utilise le mode "par transaction". Autrement dit, une connexion PostgreSQL est initiée quand une transaction est créée, et se ferme quand la transaction est terminée. Vous voudrez donc changez la valeur de la variable pool_mode
de session
à transaction
:
pool_mode = transaction
Ensuite, max_client_conn
définit combien de connexions PgBouncer lui-même va accepter, et default_pool_size
pose une limite sur le nombre de connexions à PostgreSQL qui pourront être initiées. (Dans PgHero, le nombre de connexions affichées correspondra à default_pool_size
parce qu'il ignore la présence de PgBouncer.)
Les valeurs par défaut sont bonnes pour débuter, vous pourrez toujours les augmenter plus tard :
max_client_conn = 100
default_pool_size = 20
N'oubliez pas de recharger ou redémarrer PgBouncer après avoir sauvegardé vos modifications :
sudo systemctl reload pgbouncer
Vérifier que tout fonctionne
Vous devriez être en mesure de vous connecter à PgBouncer de la même manière qu'avec PostgreSQL :
psql -p 6432 -U mastodon mastodon_production
Et utiliser votre mot de passe pour vous connecter.
Vous pouvez aussi regarder les logs de PgBouncer ainsi :
tail -f /var/log/postgresql/pgbouncer.log
Configurer Mastodon afin qu'il puisse communiquer avec PgBouncer
Dans le fichier .env.production
, assurez-vous que la variable suivante est définie ainsi :
PREPARED_STATEMENTS=false
Vu qu'on fait une mise en commun des transactions, on ne peut pas utiliser des déclarations prédéfinies.
Ensuite, configurez Mastodon pour qu'il utilise le port 6432 (PgBouncer) au lieu du port 5432 (PostgreSQL) et vous devriez pouvoir utiliser votre instance par la suite :
DB_HOST=localhost
DB_USER=mastodon
DB_NAME=mastodon_production
DB_PASS=password
DB_PORT=6432
Hop hop hop ! Vous ne pouvez pas utiliser PgBouncer pour effectuer les tâches
db:migrate
. Mais il est facile de contourner le problème. Si PostgreSQL et PgBouncer sont sur la même machine, il suffit de définirDB_PORT=5432
en plus deRAILS_ENV=production
quand vous invoquez la tâche. Par exemple :RAILS_ENV=production DB_PORT=5432 bundle exec rails db:migrate
(vous pouvez aussi définirDB_HOST
si l'un des deux services n'est pas sur la même machine, etc.)
Administrer PgBouncer
La façon la plus simple de redémarrer PgBouncer est de faire ainsi :
sudo systemctl restart pgbouncer
Mais si vous avez créé un utilisateur avec les droits administrateurs dans PgBouncer, vous pouvez aussi vous connecter en tant qu'administrateur :
psql -p 6432 -U pgbouncer pgbouncer
Puis faire :
RELOAD;
Et enfin quitter en faisant \q
.
Un Redis séparé pour le cache
Redis est largement utilisé à travers l'application, mais certaines utilisations de Redis sont plus importantes que d'autres. Les timelines personnelles et des listes, les queues Sidekiq ainsi que l'API de streaming sont fournies par Redis et ce sont des données importantes que vous ne voudriez pas perdre (même si cette perte peut être surmontée, contrairement à celle de la base de données PostgreSQL – ne la perdez jamais !). Cependant, Redis est aussi utilisé pour le cache volatile. Si vous en êtes arrivés au stade dans l'amélioration des performances où vous avez peur que Redis ne puisse tout gérer, vous pouvez utiliser une autre base de données Redis pour le cache. Dans l'environnement d'exécution, vous pouvez spécifier la variable CACHE_REDIS_URL
ou des variables plus restreintes comme CACHE_REDIS_HOST
, CACHE_REDIS_PORT
, etc. Les variables non définies auront les valeurs par défaut, celles utilisées s'il n'y avait pas de deuxième cache.
Si vous voulez configurer la base de données Redis, vous pouvez vous débarrasser de l'écriture en arrière-plan des données sur le disque, puisque ce n'est pas très important si les données sont perdues au redémarrage, et vous pourrez ainsi économiser des accès lecture/écriture sur le disque. Vous pouvez également ajouter une limite d'utilisation de mémoire et une règle de suppression de données obsolètes, mais pour ça, il faudra lire cette page (en anglais) : Using Redis as an LRU cache
Réplication des données
Pour réduire la charge sur votre serveur PostgreSQL, vous pourriez mettre en place une réplication des données (read replica). Lisez ce guide pour voir un exemple. Vous pouvez utiliser la réplication dans Mastodon de différentes manières :
- L'API de streaming ne génère pas d'écritures sur le disque, vous pouvez donc connecter l'API directement au serveur répliquant. Mais puisque l'API n’interroge pas souvent la base de données, l'impact sur les performances sera moindre.
- Utiliser Makara dans les processus Web et Sidekiq, ainsi les écritures iront sur le serveur principal tandis que les lectures iront sur le serveur répliquant. Voyons cela plus en détail.
Vous aurez à modifier le fichier config/database.yml
et remplacer la section production
ainsi :
production:
<<: *default
adapter: postgresql_makara
prepared_statements: false
makara:
id: postgres
sticky: true
connections:
- role: master
blacklist_duration: 0
url: postgresql://db_user:db_password@db_host:db_port/db_name
- role: slave
url: postgresql://db_user:db_password@db_host:db_port/db_name
Assurez-vous que les URL pointent au bon endroit. Vous pouvez ajouter plusieurs serveurs pour répliquer les données. Vous pouvez avoir PgBouncer installé sur le serveur hébergeant Mastodon, avec une configuration connectant PgBouncer à deux serveurs PostgreSQL différents en fonction du nom de la base de données appelée, par exemple "mastodon" irait sur le serveur principal et "mastodon_replica" irait sur le répliquant. Dans le fichier ci-dessus, les deux URL pointeraient sur PgBouncer avec le même utilisateur, le même mot de passe, le même hôte et port, mais avec un nom de base de données différent. Les possibilités sont infinies ! Pour plus d'informations sur Makara, consultez leur documentation.