Conteneuriser vos applications Java

Cet article fournit une vue d’ensemble des stratégies et paramètres recommandés pour la conteneurisation d’applications Java.

Lorsque vous conteneurisez une application Java, prenez soigneusement en compte le temps processeur disponible par le conteneur. Réfléchissez ensuite à la quantité de mémoire disponible en termes de quantité totale de mémoire et de taille de tas de la machine virtuelle Java (JVM). Dans les environnements conteneurisés, les applications peuvent avoir accès à tous les processeurs et peuvent donc exécuter plusieurs threads en parallèle. Toutefois, il est courant que les conteneurs aient un quota d’UC appliqué qui peut limiter l’accès aux processeurs.

La machine virtuelle JVM a des heuristiques pour déterminer le nombre de « processeurs disponibles » en fonction du quota d’UC, ce qui peut considérablement influencer les performances des applications Java. La mémoire allouée au conteneur lui-même et la taille de la zone de tas pour la machine virtuelle JVM sont aussi importantes que les processeurs. Ces facteurs déterminent le comportement du garbage collector (GC) et les performances globales du système.

Conteneuriser une nouvelle application

Lorsque vous conteneurisez une charge de travail Java pour une nouvelle application, vous devez prendre deux choses en compte lors de la réflexion sur la mémoire :

  • Mémoire allouée au conteneur lui-même.
  • Quantité de mémoire disponible pour le processus Java.

Comprendre l’ergonomie par défaut de JVM

Les applications ont besoin d’un point de départ et d’un paramètre. La machine virtuelle JVM a une ergonomie par défaut avec des valeurs prédéfinies basées sur le nombre de processeurs disponibles et la quantité de mémoire dans le système. Les valeurs par défaut indiquées dans les tableaux suivants sont utilisées lorsque la machine virtuelle JVM est démarrée sans indicateurs de démarrage ou paramètres spécifiques.

Le tableau suivant présente le gc par défaut utilisé pour les ressources disponibles :

Les ressources disponibles Gc par défaut
Nombre quelconque de processeurs
Jusqu’à 1791 Mo de mémoire
SerialGC
Processeurs 2+
1792 Mo ou plus de mémoire
G1GC

Le tableau suivant indique la taille maximale par défaut du tas en fonction de la quantité de mémoire disponible dans l’environnement où la machine virtuelle JVM est en cours d’exécution :

Mémoire disponible Taille de segment maximale par défaut
Jusqu’à 256 Mo 50 % de la mémoire disponible
256 Mo à 512 Mo ~127 Mo
Plus de 512 Mo 25 % de la mémoire disponible

La taille du tas initial par défaut est de 1/64 de mémoire disponible.

Ces valeurs sont valides pour OpenJDK 11 et versions ultérieures, et pour la plupart des distributions, notamment Microsoft Build of OpenJDK, Azul Zulu, Eclipse Temurin, Oracle OpenJDK, etc.

Déterminer la mémoire du conteneur

Choisissez une quantité de mémoire de conteneur qui servira le mieux votre charge de travail, en fonction des besoins de votre application et de ses modèles d’utilisation distinctifs. Par exemple, si votre application crée des graphiques d’objets volumineux, vous aurez probablement besoin de plus de mémoire que nécessaire pour les applications avec de nombreux graphiques d’objets de petite taille.

Conseil

Si vous ne savez pas combien de mémoire allouer, un bon point de départ est de 4 Go.

Déterminer la mémoire du tas JVM

Lorsque vous allouez de la mémoire du tas JVM, sachez que la machine virtuelle JVM a besoin de plus de mémoire que ce qui est utilisé pour le tas JVM. Lorsque vous définissez la mémoire maximale du tas JVM, elle ne doit jamais être égale à la quantité de mémoire du conteneur, car cela entraîne des erreurs de mémoire insuffisante du conteneur (OOM) et des blocages de conteneur.

Conseil

Allouez 75 % de la mémoire du conteneur pour le tas JVM.

Sur OpenJDK 11 et versions ultérieures, vous pouvez définir la taille du tas JVM de la manière suivante :

Description Indicateur Exemples
Valeur fixe -Xmx -Xmx4g
Valeur dynamique -XX:MaxRAMPercentage -XX:MaxRAMPercentage=75

Taille minimale/initiale du tas

Lorsque l’environnement est garanti avoir une certaine quantité de mémoire réservée à une instance JVM, comme dans un conteneur, vous devez définir la taille minimale du tas ( ou la taille initiale du tas) sur la même taille que la taille maximale du tas. Ce paramètre indique à la machine virtuelle JVM qu’elle ne doit pas effectuer la tâche de libérer de la mémoire sur le système d’exploitation.

Pour définir une taille minimale de tas, utilisez-la -Xms pour les montants absolus ou -XX:InitialRAMPercentage pour les quantités de pourcentage.

Important

L’indicateur-XX:MinRAMPercentage, malgré ce que le nom suggère, est utilisé pour définir le pourcentage maximal de RAM par défaut pour les systèmes avec jusqu’à 256 Mo de RAM disponible dans le système.

Chart showing the default heap size on OpenJDK 17.

Déterminer le GC à utiliser

Auparavant, vous avez déterminé la quantité de mémoire du tas JVM à utiliser. L’étape suivante consiste à choisir votre GC. La quantité maximale de mémoire de tas JVM que vous avez est souvent un facteur dans le choix de votre GC. Le tableau suivant décrit les caractéristiques de chaque GC.

Facteurs SerialGC ParallelGC G1GC ZGC ShenandoahGC
Nombre de mémoires à tores magnétiques 1 2 2 2 2
Multithread Non Oui Oui Oui Oui
Taille du tas Java <4 octets <4 octets >4 octets >4 octets >4 octets
Interrompre Oui Oui Oui Oui (<1 ms) Oui (<10 ms)
Frais généraux Minimal Minimal Modéré Modéré Modéré
Effet de latence de fin Élevé Élevé Élevé Faible Modéré
Version du JDK Tout Tout JDK 8+ JDK 17+ JDK 11+
Idéal pour Segments de mémoire de petite taille à cœur unique Charges de travail de petits tas ou de lots multicœurs avec n’importe quelle taille de tas Réactivité dans les tas de taille moyenne à grande (interactions de demande-réponse/base de données) Réactivité dans les tas de taille moyenne à grande (interactions de demande-réponse/base de données) Réactivité dans les tas de taille moyenne à grande (interactions de demande-réponse/base de données)

Conseil

Pour la plupart des applications de microservice à usage général, commencez par le GC parallèle.

Déterminer le nombre de cœurs d’UC nécessaires

Pour tout GC autre que SerialGC, nous vous recommandons deux cœurs de processeurs virtuels ou plus , ou au moins 2000mcpu_limit sur Kubernetes. Nous vous déconseillons de sélectionner un cœur de processeur virtuel inférieur à 1 sur des environnements conteneurisés.

Conseil

Si vous ne savez pas combien de cœurs commencer, un bon choix est de 2 cœurs de processeurs virtuels.

Choisir un point de départ

Nous vous recommandons de commencer par deux réplicas ou instances dans des environnements d’orchestration de conteneurs tels que Kubernetes, OpenShift, Azure Spring Apps, Azure Container Apps et Azure App Service. Le tableau suivant récapitule les points de départ recommandés pour la conteneurisation de votre nouvelle application Java.

Cœurs de processeur virtuel Mémoire du conteneur Taille du tas JVM GC Réplicas
2 4 Go 75 % ParallelGC 2

Les paramètres JVM à utiliser sont les suivants : -XX:+UseParallelGC -XX:MaxRAMPercentage=75

Conteneuriser une application existante (localement)

Si votre application s’exécute déjà localement ou sur une machine virtuelle dans le cloud, nous vous recommandons de commencer par :

  • Quantité de mémoire à laquelle l’application a actuellement accès.
  • Le même nombre de processeurs (cœurs de processeurs virtuels) que l’application a actuellement disponibles.
  • Les mêmes paramètres JVM que vous utilisez actuellement.

Si la combinaison de cœurs de processeur virtuel et/ou de mémoire de conteneur n’est pas disponible, choisissez la plus proche, arrondissant les cœurs de processeurs virtuels et la mémoire du conteneur.

Étapes suivantes

Maintenant que vous comprenez les recommandations générales pour conteneuriser des applications Java, passez à l’article suivant pour établir une base de référence de conteneurisation :