Personne* ne met Java dans un containeur
28 oct. 2017
Ne ratez pas nos articles sur l'open source, le big data et les systèmes distribués, fréquence faible d’un email tous les deux mois.
Cette discussion porte sur les problèmes de mettre Java dans un conteneur et comment, dans sa dernière version 9, le JDK est maintenant plus conscient du conteneur dans lequel il s’exécute. La présentation est dirigée par Joerg Schad, ingénieur logiciels distribués de Mesosphere, à l’Open Source Summit 2017 de Prague.
Quels sont les problèmes de mettre Java dans un conteneur ?
Comment la JVM interagit-elle avec l’isolation fournie par le conteneur ?
Conteneurs
Un conteneur est un moyen pratique de déployer des applications facilement. En s’appuyant directement sur le noyau, l’isolation fournie par un conteneur est plus faible qu’une machine virtuelle en échange de performances supérieures.
Un conteneur est donc très rapide au lancement et lors de son exécution et utilise moins de ressources (mémoire, cpu, réseau) qu’une VM.
Deux technologies complémentaires permettent d’isoler un conteneur dans le système : les CGroups et les espace de nom appelés Namespaces. Combinés, ils offrent une solution légère et puissante pour isoler les processus du reste du système.
Namespaces
Un Namespace fournit à un processus sa propre vue du système. Par exemple, un processus ne connaît que ses propres PID, points de montage et systèmes de fichiers.
CGroups
Alors que les Namespaces contrôlent la vue du système pour un processus, les CGroups limitent les ressources utilisées par un processus (ou un groupe de processus). De tels exemples incluent :
- Utilisation du processeur
- Limites de mémoire
- Réseau et disque IO
- Hardware
Java
Les problèmes actuels concernent la façon dont la JVM interagit avec les CGroups pour rassembler les ressources système.
Mémoire
Un programme Java n’utilise pas seulement la mémoire pour la taille du tas, mais aussi pour les threads Garbage Collector, le compilateur just in time (JIT), …
Avant Java 8u131, pour obtenir la quantité de mémoire disponible sur l’hôte, la JVM déterminait la taille de la stack en regardant dans /proc
. Cependant, dans un environnement conteneur, cette valeur reflète la mémoire du système et non la mémoire disponible pour le conteneur, en contradiction avec la valeur allouée au conteneur par sa définition CGroups.
La conséquence d’un tel comportement est simple mais problématique. La taille maximale de la stack est par défaut de 1/4 de la mémoire physique. Comme cette mémoire est basée sur la mémoire physique de l’hôte, un processus Java s’exécutant à l’intérieur du conteneur pourrait bien allouer plus que la mémoire disponible à l’intérieur du conteneur. Ainsi, il recevra un signal d’arrêt à la suite du dépassement des limites imposées par le CGroup.
Deux solutions existent :
- Utilisez les options JAVA
-Xmx
pour définir manuellement la taille maximale du tas, ce qui réduit la portabilité offerte par les conteneurs - Utilisez JDK8u131 ou JDK9 et
-XX:+UseCGroupMemoryLimitForHeap -XX:+UnlockExperimentalVMOptions
afin de rendre la taille de la JVM consciente du CGroup
CPU
Le problème est très similaire pour l’utilisation du processeur. Il existe deux façons de limiter l’utilisation du processeur d’un processus à l’aide des CGroups :
- Unités de CPU : attribuez un nombre de CPU à un processus
- Partages CPU : attribuez un poids qui agit pour limiter en pourcentage l’utilisation de la CPU.
La JVM ne sait pas qu’elle s’exécute dans un conteneur et basera ses paramètres par défaut sur le nombre de CPU disponibles sur l’hôte au lieu de ceux appliqués par le CGroup. Sur la base de cette valeur, la machine virtuelle Java démarrera trop de threads GC et JIT, ce qui entraînera des problèmes de performances.
Ce problème est également résolu dans le JDK le plus récent. Puisque la JVM est maintenant consciente du nombre de processeurs disponibles via CGroups. Il n’est donc pas recommandé d’utiliser des partages de CPU.
Conclusion
Contrairement à la technologie VM, les conteneurs ne cachent pas le hardware sous-jacent du processus. Le problème avec les conteneurs et CGroups est principalement résolu depuis la JDK8u131.
Les références
- Java and containers - Red Hat Blog
- Java is a first class citizen in a Docker ecosystem now - Cloakable
- Java SE supports for Docker CPU and memory limits - Oracle Blog
- [*Nobody puts baby in the corner](https://www.urbandictionary.com/define.php?term=nobody puts Baby in the corner)