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.
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 |
Pause | 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 | Tous | Tous | 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 recommandons deux cœurs de processeurs virtuels ou plus , ou au moins 2000m
cpu_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 :