10 min de lecture
Améliorez l'utilisation de la mémoire et les performances de votre application Ruby avec jemalloc
Cet article a été publié à l'origine sur le blog de Level Up Solutions par Clément Joubert, fondateur de Level Up Solutions, une société de conseil et de développement d'applications web. Avec sa permission, nous le partageons ici pour les lecteurs de Scalingo.

Cet article a été initialement publié sur le blog de Level Up Solutions par Clément Joubert, Fondateur de Level Up Solutions, une société de conseil et de développement d'applications Web. Avec sa permission bienveillante, nous le partageons ici pour les lecteurs de Scalingo.
Les applications Ruby ont généralement une empreinte mémoire assez importante, qui peut augmenter régulièrement, même si le ramasse-miettes entre en action. Votre application peut consommer 150 Mo de mémoire au lancement initial, mais elle dépassera probablement rapidement les 300 Mo.
Ruby utilise la bibliothèque C malloc pour allouer, libérer et réallouer dynamiquement de la mémoire, afin de stocker des objets. Cependant, d'autres implémentations existent telles que tcmalloc ou jemalloc. Cette dernière bibliothèque a prouvé sa valeur depuis 2005 dans FreeBSD. Le populaire et puissant Redis l'utilise également depuis plusieurs années. Une preuve rassurante de fiabilité.
Pour Ruby, l'utilisation de jemalloc vous permet d'allouer, de réutiliser ou de libérer plus efficacement la mémoire de votre application. En production, de nombreux utilisateurs ont constaté une augmentation de la performance d'environ 10 % (pour le code Ruby) ainsi qu'une consommation de mémoire réduite et stable. Certaines personnes — comme nous — ont trouvé les fuites de mémoire réduites voire éliminées. Ce dernier cas peut être un véritable soulagement si vous êtes la victime de ce type d'inconvénients. Nous sommes également passés par là !
Après que cet article a été initialement publié en français, le créateur de updown.io, Adrien Jarthon l'a déployé sur la moitié de ses serveurs et a constaté une consommation de mémoire réduite de -29 % pour l'application Rails, et de -46 % pour les travailleurs Sidekiq.
Installation de jemalloc
L'implémentation Ruby la plus populaire s'appelle Ruby MRI. Depuis la version 2.2, elle peut être compilée avec jemalloc au lieu de malloc. Aucun patch ne doit être appliqué : seule la présence de la bibliothèque jemalloc sur votre système est requise ainsi qu'un drapeau de compilation.
Sur Linux, installez jemalloc en utilisant votre gestionnaire de paquets préféré. Par exemple avec Ubuntu :
Sur macOS, le moyen le plus simple est d'utiliser Homebrew pour l'installer :
Compiler Ruby avec le support de jemalloc
Avec jemalloc installé, vous devez toujours compiler Ruby avec le drapeau approprié :
Si vous utilisez un gestionnaire Ruby comme rbenv ou RVM, vous devez simplement préfixer la commande d'installation avec ceci :
Pour vous assurer que votre Ruby utilise jemalloc, exécutez la commande suivante :
Si jemalloc est utilisé, vous devriez obtenir une réponse proche de celle-ci :
La présence de -ljemalloc indique que la bibliothèque du même nom est chargée lors du démarrage de Ruby.
Scalingo et d'autres plateformes où vous ne pouvez pas compiler Ruby
Un grand nombre d'applications sont déployées sur des plateformes qui prennent en charge l'hébergement et l'exécution de vos applications Ruby. Ils s'occupent de la plupart des tâches difficiles, et il n'est ni nécessaire ni souvent possible de compiler Ruby vous-même. Dans ce cas, nous devons forcer l'utilisation de jemalloc en utilisant des buildpacks.
Si vous ne savez pas ce que sont les buildpacks, ce sont une sorte de paquet logiciel contenant tout le nécessaire pour exécuter un programme sur une plateforme d'hébergement Linux. Il existe des buildpacks pour Ruby, Node.js, Scala, Java, PhantomJS, etc.
Le buildpack jemalloc
Un buildpack ne contient pas nécessairement un programme exécutable. Comme les paquets Linux, il peut ne contenir qu'une bibliothèque C, par exemple.
C'est là que le heroku-buildpack-jemalloc buildpack de Seth Fitzsimmons s'avère utile. Il contient jemalloc précompilé pour Linux x86 et stocké dans /app/vendor/jemalloc/lib/libjemalloc.so.1. Cela le rend facilement accessible par Ruby MRI.
Le buildpack jemalloc doit être chargé avant le buildpack Ruby. Vous devez configurer votre plateforme pour utiliser les deux buildpacks simultanément.
Configuration de plusieurs buildpacks sur Scalingo
Heroku a rendu les buildpacks immensément populaires. D'autres plateformes ont suivi la même philosophie. Nous utilisons Scalingo depuis quelques années maintenant. Ce fournisseur français de Platform-as-a-Service (PaaS) d'hébergement est très similaire au géant américain et continue d'ajouter des fonctionnalités utiles comme un horloge.
Scalingo n'autorise qu'un seul buildpack à être défini pour une application, donc nous devons un peu tricher en utilisant le buildpack multi-buildpack. Lorsqu'il est utilisé, il chargera plusieurs buildpacks, un après l'autre.
Pour l'utiliser, exécutez cette commande :
Ensuite, créez un fichier .buildpacks à la racine de votre application et énumérez les buildpacks à utiliser :
Lors du déploiement de votre conteneur, Scalingo recherchera ce fichier et chargera chaque buildpack séquentiellement.
Démarrer votre application Ruby avec jemalloc
Le buildpack jemalloc contient un script shell nommé jemalloc.sh. L'auteur recommande de préfixer votre ligne de commande Ruby habituelle avec lui. Par exemple, pour lancer le serveur web Puma, nous utiliserions :
L'utilisation de ce script a ses problèmes, en plus de l'apparence. Votre configuration de serveur devient spécifique et dépendante du script de ce buildpack. La même commande puma ne fonctionnera pas sur une plateforme qui n'inclut pas le script. Sur votre plateforme de développement, il est probablement non disponible. Cela poserait un problème si vous souhaitez utiliser Foreman pour tester votre Procfile, par exemple.
Une de nos applications est déployée en continu sur 4 PaaS différents, dont 3 utilisent le même fichier Procfile pour démarrer l'application Ruby. Nous devons utiliser la même configuration exacte partout, dans le même Procfile, donc nous ne pouvons pas appliquer d'optimisations spécifiques pour Heroku ou Scalingo à cet endroit.
La solution dont nous avons besoin suit la méthodologie 12factor en stockant la configuration dans l'environnement. Nous pourrons toujours dire à Ruby de charger et d'utiliser jemalloc, mais la ligne de commande ne changera pas.
La variable d'environnement LD_PRELOAD est recherchée par MRI pour déterminer quelles bibliothèques supplémentaires il doit précharger. Si la bibliothèque jemalloc est présente, elle sera utilisée au lieu de malloc. Cela fonctionnera avec bundle exec ruby ainsi qu'avec un exécutable gem tel que rubocop ou sidekiq.
jemalloc sur Scalingo
Le tableau de bord Scalingo peut être utilisé pour modifier une variable, ou toutes à la fois :

Vous pouvez également définir les variables d'environnement en utilisant l'outil CLI :
Redémarrez le conteneur de votre application pour bénéficier de cette optimisation agréable.
Expérience(s) personnelle(s) en production
Notre application la plus complexe (plus de 100 gems, 160 Mo au démarrage) consommait environ 500 Mo par travailleur Puma après traitement de dizaines de milliers de requêtes lourdes. Pour éviter de saturer la mémoire de notre serveur, les travailleurs Puma et Sidekiq devaient être régulièrement tués et redémarrés.
Avec les versions 2.3 et 2.4 compilées avec jemalloc, nous avons mesuré une consommation de mémoire stabilisée à environ 350 Mo. Les temps de réponse ont chuté de quelques pourcents et le nombre d'alertes « requêtes anormalement lentes » a également été largement réduit.
Nous avons réitéré l'expérience avec d'autres applications : une application Ruby légère dans un conteneur de 512 Mo et un CMS basé sur Rails fait maison plus lourd dans un conteneur de 2 Go. Le swap est désormais presque jamais utilisé car les conteneurs ont cessé de dépasser leurs ressources attribuées. Les temps de réponse se sont également légèrement améliorés sur le CMS.
La gem jemalloc : une alternative non fonctionnelle
Nous avons également essayé la gem jemalloc, un projet qui ne semble plus être maintenu depuis sa dernière activité en janvier 2015. Nous n'avons pas réussi à le faire fonctionner. De plus, il présente la même complexité d'utilisation que le script jemalloc.sh : toutes les lignes de commande doivent être préfixées par bundle exec je.
Un mot de Scalingo
Bien sûr, pour des raisons de conformité, la déclaration officielle de Scalingo concernant jemalloc est qu'ils utiliseront toujours les versions officielles du langage Ruby.
Cependant, avec l'extensibilité fournie par le mécanisme des buildpacks, vous pouvez facilement essayer des solutions divergentes par vous-même comme démontré dans cet article.

Yann Klis
Yann KLIS a fondé Scalingo en 2015 avec son associé Léo Unbekandt avec la vision de proposer une plateforme cloud d'hébergement web, véritable alternative européenne et souveraine aux géants américains. Aujourd'hui Scalingo héberge plusieurs milliers d'applications web déployées par des clients du monde entier ! L'objectif de Scalingo est de devenir la plateforme cloud de référence pour les développeurs web en Europe. Auparavant, il a fondé Novelys, un studio de développement spécialisé dans la technologie Ruby on Rails.
Restez informé
Recevez des articles et des mises à jour de la plateforme dans votre boîte de réception.
Prêt à déployer en toute confiance ?
Découvrez des déploiements sans temps d'arrêt, une mise à l'échelle automatique intelligente et une infrastructure entièrement gérée. Commencez à déployer vos applications sur Scalingo dès aujourd'hui.
Aucune carte de crédit requise • Déployez en quelques minutes • Annulez à tout moment





