[DOCKER] Dégradation des performances du conteneur Java dans l'environnement Menikoa

Post-scriptum du 08/04/2018: Ajout d'une description de Java 10 au résumé.

Post-scriptum du 02/06/2017: Le contenu de l'enquête a été résumé et la description a été considérablement mise à jour.

Addendum du 01/06/2017: Ajout de "Efforts pour ce problème après JDK 8/9" à la fin de l'article. Il semble que cela ait été corrigé dans 8u121.b34 et 8u131.b06 d'OpenJDK8 (je vérifierai séparément si c'est vraiment corrigé). Les deux solutions de contournement ont été publiées depuis 2017, c'est donc une bonne idée de vérifier si vous ne mettez pas à jour votre JDK / JRE fréquemment.

Résumé (migration Java 10)

Java10 prend désormais officiellement en charge les conteneurs Docker. Le programme Java sur le conteneur Docker peut désormais saisir les paramètres de ressources tels que le processeur et la mémoire définis dans le conteneur Docker, de sorte que le problème Menikoa décrit dans cet article sera résolu.

Concernant la prise en charge de Java 10 et Docker, l'article Qiita suivant résume en détail, y compris la vérification des opérations.

https://qiita.com/c9katayama/items/b427a008623f0e228c50

Résumé (Java 8/9)

Si vous limitez le nombre de processeurs à un petit nombre avec Docker, ensemble de tâches, etc. dans l'environnement Menicoa, les performances des programmes Java peuvent chuter considérablement. Il y a deux manières de le gérer.

Si vous n'utilisez que l'option pour spécifier l'ID du cœur du processeur, comme le --cpuset-cpus de la commande --docker, vous pouvez le gérer avec une mise à jour JDK. (Java 8 8u121.b34 ou version ultérieure ou 8u131.b06 ou version ultérieure) --Si vous utilisez une option qui ne spécifie pas d'ID de cœur de processeur, telle que la commande docker --cpus, vous devez régler les paramètres de paramètres JVM appropriés lorsque vous démarrez le programme Java.

Événement d'occurrence

Dans un environnement menicoa (une machine avec un grand nombre de cœurs de processeur), les performances chuteront considérablement lorsqu'un programme Java est exécuté sur un Docker qui a des restrictions de ressources telles que le nombre de cœurs de processeur. Il en va de même lorsqu'un programme Java est exécuté à l'aide d'une technologie ou d'une commande de conteneur (par exemple, la commande Tasket) qui utilise des restrictions de ressources par des groupes de contrôle autres que Docker.

Cause

En Java, les paramètres JVM suivants sont définis en fonction des ressources matérielles de la machine.

--Nombre de threads GC (garbage collection) --Nombre de threads de compilation JIT

C'est bien en soi. Cependant, en Java, même si les ressources matérielles sont limitées par un groupe de contrôle tel que Docker, les paramètres JVM sont déterminés en fonction des ressources matérielles de la machine hôte et non des ressources matérielles limitées.

Par conséquent, par exemple, si un programme Java est exécuté sur un «conteneur Docker avec 20 cœurs de processeur» sur une machine hôte avec 20 cœurs de processeur, Java déterminera les paramètres JVM en fonction des 20 cœurs. .. Cependant, le fait qu'un seul cœur de processeur puisse être utilisé dans le conteneur Docker reste le même, de sorte que les programmes Java souffrent de la surcharge d'un grand nombre de threads GC et de threads de compilation JIT en plus du traitement de l'application, ce qui entraîne une diminution significative des performances. ..

Faites attention à ceux qui remplissent les conditions suivantes.

solution de contournement

Il existe deux approches principales pour y faire face.

Solution de contournement 1: mise à jour JDK

Comme décrit dans «(Référence) Résolution de ce problème après JDK8 / 9» de cet article, les problèmes liés aux groupes de contrôle ont été résolus dans une certaine mesure dans la nouvelle version de JDK8 / 9. Dans le cas de JDK8, il sera résolu en mettant à jour vers la version suivante ou ultérieure.

--8u121.b34 ou version ultérieure --8u131.b06 ou version ultérieure

Les résultats de la confirmation dans le conteneur Docker avec le JDK avant et après l'action sont répertoriés ci-dessous. Je l'ai confirmé dans un environnement avec 8 cœurs de processeur logiques, mais dans le JDK après y avoir traité, la limite de ressources est correctement reflétée dans le paramètre JVM par la commande docker --cpuset-cpus.

#--------------------------------------------------------------
#Dans le JDK pré-action--cpuset-Le réglage de l'option cpus n'est pas reflété dans le paramètre JVM
#--------------------------------------------------------------
$ docker run --rm --cpuset-cpus 0-1 openjdk:8u77-b03 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
     intx CICompilerCount                          := 4                                   {product}
    uintx ParallelGCThreads                         = 8                                   {product}
openjdk version "1.8.0_03-Ubuntu"
OpenJDK Runtime Environment (build 1.8.0_03-Ubuntu-8u77-b03-3ubuntu3-b03)
OpenJDK 64-Bit Server VM (build 25.03-b03, mixed mode)

#--------------------------------------------------------------
#Après avoir traité avec JDK--cpuset-Le réglage de l'option cpus est reflété dans le paramètre JVM
#--------------------------------------------------------------
$ docker run --rm --cpuset-cpus 0-1 openjdk:8u131-b11 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
     intx CICompilerCount                          := 2                                   {product}
    uintx ParallelGCThreads                         = 2                                   {product}
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

Cependant, dans le cas d'une option qui ne spécifie pas un ID de cœur de processeur spécifique tel que --cpus de la commande docker, il semble que le problème ne puisse pas être évité même avec Java après manipulation. Je ne l'ai pas essayé, mais il semble probable que ce problème ne puisse pas être évité pour les options --cpu-quota et --cpu-period également. Dans ce cas, il est nécessaire de régler les paramètres des paramètres JVM, ce qui constitue une autre solution de contournement.

#--------------------------------------------------------------
#Après y avoir traité, même en JDK--Aucun effet avec les paramètres des options de processeur
#--------------------------------------------------------------
docker run --rm --cpus 2.0 openjdk:8u131-b11 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
     intx CICompilerCount                          := 4                                   {product}
    uintx ParallelGCThreads                         = 8                                   {product}
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

Solution de contournement 2: réglages des paramètres JVM

Il peut être résolu en réglant en définissant le paramètre JVM lors de l'exécution du programme Java sur le conteneur Docker. Cela prend plus de temps qu'une mise à jour JDK, mais c'est la plus fiable.

Changer GC en GC série

Vous souhaiterez peut-être définir le GC sur un GC série si les conditions suivantes sont remplies:

#Changer GC en GC série
java -XX:+UseSerialGC  ...

Cependant, il est dit que Serial GC sera aboli dans le futur, donc soyez prudent si vous prévoyez de mettre à jour Java.

Changer le nombre de threads GC

Si vous souhaitez utiliser Parallel GC, qui est le GC par défaut de Java8, vous pouvez le gérer en réduisant le nombre de threads de Parallel GC. Le nombre approprié de threads dépend de la limite de ressources CPU spécifiée dans la commande docker, un réglage est donc nécessaire.

#Définissez le nombre de threads parallelGC sur 1
java XX:ParallelGCThreads=1  ...

Je n'ai pas défini CSM, G1, etc., donc je ne vais pas le décrire, mais en gros, vous pouvez définir le nombre de threads en fonction des ressources du processeur.

Réduisez le nombre de threads de compilation JIT.

La compilation JIT s'exécute également sur plusieurs threads, et un nombre raisonnable de threads sera généré dans l'environnement Menikoa. Par conséquent, cela doit également être réglé en fonction de la limite de ressources du processeur.

#Définissez le nombre de threads de compilation JIT sur 1
java -XX:CICompilerCount=1 ...

(Référence) Pour les machines virtuelles

Les paramètres JVM sont déterminés en fonction du programme Java sur la machine virtuelle et des ressources matérielles de la machine virtuelle. Par conséquent, même si la machine hôte se trouve dans un environnement menicoa, les paramètres JVM sont définis en fonction des ressources matérielles affectées à la machine virtuelle.

(Référence) Informations sur la version en cas de problème

(Référence) Efforts pour ce problème après JDK8 / 9

Ce problème a été reconnu depuis JDK9, et il semble que le problème soit rétroporté vers JDK8 en ce qui concerne l'état de prise en charge du problème. Et il a été traité dans JDK8 "8u121.b34" et "8u131.b06".

  1. JDK-8140793 - getAvailableProcessors may incorrectly report the number of cpus in Docker container
  1. JDK-8172318 - [linux] Experimental support for cgroup memory limits in container (ie Docker) environments

Recommended Posts

Dégradation des performances du conteneur Java dans l'environnement Menikoa
Construction de l'environnement JavaFX dans Java 13
Environnement Java Spring dans vs Code
Construction de l'environnement Play Framework 2.6 (Java) avec Eclipse
Environnement de développement d'applications Java créé dans un environnement VM
Partition en Java
[Java] Construction de l'environnement
Lorsqu'il y a des variables d'environnement dans les tests Java
Changements dans Java 11
La solution pour NetBeans 8.2 ne fonctionne pas dans l'environnement Java 9
Janken à Java
Environnement de développement Java
Taux circonférentiel à Java
Points bloqués dans la création d'un environnement de développement VS Code et Java
[Débutant] Installez l'outil de développement java dans l'environnement de développement cloud9.
FizzBuzz en Java
Remplacement des variables d'environnement système par réflexion en Java
[LeJOS] Programmons mindstorm-EV3 avec Java [Construction de l'environnement partie 2]
Comment créer un environnement Java en seulement 3 secondes
Maven échoue dans l'environnement Java7 (alerte fatale reçue: version_protocole)
[Java] Récupère le fichier dans le fichier jar quel que soit l'environnement
Lire JSON en Java
Implémentation de l'interpréteur par Java
Faites un blackjack avec Java
Application Janken en Java
Programmation par contraintes en Java
Mettez java8 dans centos7
NVL-ish guy en Java
Joindre des tableaux en Java
"Hello World" en Java
Mémo de l'environnement de développement Java
Interface appelable en Java
[Résumé] Par exemple, préparation de l'environnement Java
Commentaires dans la source Java
Fonctions Azure en Java
Formater XML en Java
Réglage des performances de l'application Java
Simple htmlspecialchars en Java
Implémentation Boyer-Moore en Java
Hello World en Java
Utiliser OpenCV avec Java
Mémorandum WebApi avec Java
Détermination de type en Java
Exécuter des commandes en Java (ping)
Divers threads en java
Implémentation du tri de tas (en java)
API Zabbix en Java
Art ASCII à Java
Comparer des listes en Java
POST JSON en Java
construction d'environnement de développement Java
Exprimer l'échec en Java
Créer JSON en Java
Manipulation de la date dans Java 8
Nouveautés de Java 8
Utiliser PreparedStatement en Java
Java Performance Chapitre 3 Boîte à outils Java Performance
Nouveautés de Java 9,10,11
Exécution parallèle en Java