{
  "data": [
    {
      "type": "blog",
      "id": "fr/blog/ubuntu-25-10-docker-java-cgroupv2-crash",
      "url": "https://ktherage.github.io/fr/blog/ubuntu-25-10-docker-java-cgroupv2-crash/",
      "attributes": {
        "title": "Ubuntu 25.10 : La mise à jour qui a brické mes conteneurs Docker Java",
        "description": "Comment une simple mise à jour Ubuntu a fait planter mes conteneurs Selenium, et pourquoi le NullPointerException de cgroupv2 est le symptôme d'un problème de compatibilité entre le noyau Linux et les anciennes versions de Java.",
        "cover": {"image":"img/pexels-docker-port.jpg","alt":"Container cranes at a bustling port during sunset","caption":"Photo by <a href=\"https://www.pexels.com/@thorl5/\">thorl5</a> on <a href=\"https://www.pexels.com\">Pexels</a>"},
        "published": true,
        "tags": ["Ubuntu","Docker","Java","DevOps","Debug","cgroupv2"],
        "excerpt": "Après la mise à jour de mon système d'exploitaion d'Ubuntu 24.04 vers 25.10, mon conteneur Docker selenium/standalone-chrome s'est mis à planter avec un NullPointerException incompréhensible. Voici mon histoire.",
        "body": "## La mise à jour tranquille qui tourne au cauchemar\n\nC&#039;était un vendredi soir, comme les autres. Je décide enfin de faire ce que tout bon développeur évite : **mettre à jour son OS**. Ubuntu 24.04 LTS → 25.10, la petite mise à jour de routine. *&quot;Ça ne peut que s&#039;améliorer&quot;*, me dis-je avec ce pessimisme propre à ceux qui ont vu trop de mises à jour mal se passer (Ubuntu 24.10 je te vois !).\n\nJe lance la mise à jour en confiance. Tout se passe bien. Redémarrage. Tout fonctionne. Nickel.\n\nSauf que le lundi suivant, mes tests E2E ne passent plus. Le conteneur Docker `selenium/standalone-chrome:4.5.3` qui fonctionnait parfaitement la veille refuse désormais de démarrer. Il plante en boucle avec ce magnifique message d&#039;erreur :\n\n```\nchrome-1  | java.lang.reflect.InvocationTargetException\nchrome-1  |     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\nchrome-1  |     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\nchrome-1  |     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\nchrome-1  |     at java.base/java.lang.reflect.Method.invoke(Method.java:566)\nchrome-1  |     at org.openqa.selenium.grid.Bootstrap.runMain(Bootstrap.java:77)\nchrome-1  |     at org.openqa.selenium.grid.Bootstrap.main(Bootstrap.java:70)\nchrome-1  | Caused by: java.lang.NullPointerException\nchrome-1  |     at java.base/jdk.internal.platform.cgroupv2.CgroupV2Subsystem.getInstance(CgroupV2Subsystem.java:81)\nchrome-1  |     at java.base/jdk.internal.platform.CgroupSubsystemFactory.create(CgroupSubsystemFactory.java:113)\nchrome-1  |     at java.base/jdk.internal.platform.CgroupMetrics.getInstance(CgroupMetrics.java:167)\nchrome-1  |     at java.base/jdk.internal.platform.SystemMetrics.instance(SystemMetrics.java:29)\nchrome-1  |     at java.base/jdk.internal.platform.Metrics.systemMetrics(Metrics.java:58)\nchrome-1  |     at java.base/jdk.internal.platform.Container.metrics(Container.java:43)\nchrome-1  |     at jdk.management/com.sun.management.internal.OperatingSystemImpl.&lt;init&gt;(OperatingSystemImpl.java:182)\nchrome-1  |     at jdk.management/com.sun.management.internal.PlatformMBeanProviderImpl.getOperatingSystemMXBean(PlatformMBeanProviderImpl.java:281)\nchrome-1  |     at jdk.management/com.sun.management.internal.PlatformMBeanProviderImpl$3.nameToMBeanMap(PlatformMBeanProviderImpl.java:198)\nchrome-1  |     at java.management/java.lang.management.ManagementFactory.lambda$getPlatformMBeanServer$0(ManagementFactory.java:487)\nchrome-1  |     at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)\nchrome-1  |     at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)\nchrome-1  |     at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1693)\nchrome-1  |     at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)\nchrome-1  |     at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)\nchrome-1  |     at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)\nchrome-1  |     at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)\nchrome-1  |     at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)\nchrome-1  |     at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)\nchrome-1  |     at java.management/java.lang.management.ManagementFactory.getPlatformMBeanServer(ManagementFactory.java:488)\nchrome-1  |     at org.openqa.selenium.grid.jmx.JMXHelper.register(JMXHelper.java:29)\nchrome-1  |     at org.openqa.selenium.grid.server.BaseServerOptions.&lt;init&gt;(BaseServerOptions.java:48)\nchrome-1  |     at org.openqa.selenium.grid.commands.Standalone.createHandlers(Standalone.java:124)\nchrome-1  |     at org.openqa.selenium.grid.TemplateGridServerCommand.asServer(TemplateGridServerCommand.java:41)\nchrome-1  |     at org.openqa.selenium.grid.commands.Standalone.execute(Standalone.java:245)\nchrome-1  |     at org.openqa.selenium.grid.TemplateGridCommand.lambda$configure$4(TemplateGridCommand.java:129)\nchrome-1  |     at org.openqa.selenium.grid.Main.launch(Main.java:83)\nchrome-1  |     at org.openqa.selenium.grid.Main.go(Main.java:57)\nchrome-1  |     at org.openqa.selenium.grid.Main.main(Main.java:42)\nchrome-1  |     ... 6 more\n```\n\n**Ma première pensée :**\n&lt;div class=&quot;d-flex flex-row m-2 justify-content-center&quot;&gt;\n  &lt;iframe src=&quot;https://giphy.com/embed/4ZxicT7ZQYcLShHOiz&quot; width=&quot;480&quot; height=&quot;274&quot; style=&quot;&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen&gt;&lt;/iframe&gt;\n&lt;/div&gt;\n\n&gt; Je suis développeur PHP, pas Java.\n\n**Mon reflexe:**\n&lt;div class=&quot;d-flex flex-row m-2 justify-content-center&quot;&gt;\n  &lt;iframe src=&quot;https://giphy.com/embed/pUVOeIagS1rrqsYQJe&quot; width=&quot;480&quot; height=&quot;288&quot; style=&quot;&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen&gt;&lt;/iframe&gt;\n&lt;/div&gt;\n\n&gt; Demandons à plus fort que soi. Gemini cricket (🤖🦗) 🥲.\n\n## L&#039;Enquête\n\nMon ami le cricket met le doigt sur **cgroupv2** dans la ligne `CgroupV2Subsystem.java:81` et fait le lien avec la mise à jour de mon système. Mais **cgroupv2**, c&#039;est quoi ?\n\n🤖🦗:\n&gt; Pour faire court, c&#039;est ce qui permet à Docker 🐋 de limiter le CPU ou la RAM d&#039;un container.\n\nConcrètement :\n* Docker dit à la JVM : *&quot;Tu as le droit à 2Go de RAM&quot;*.\n* Java lit ces infos dans les **cgroups** (fichiers de gestion des ressources).\n* Java ajuste son comportement (mémoire Heap, etc.) en conséquence.\n\n**C&#039;est censé être une bonne chose.** Cela évite que Java ne se fasse abattre par le *OOM Killer* du système hôte. Mais Java doit parser ces fichiers, et c&#039;est là que le bât blesse.\n\n&gt; **Pourquoi un crash et pas juste une erreur ?**\n&gt; Dans le code source des anciennes JVM, si le chemin retourné par l&#039;interface système n&#039;est pas exactement celui attendu, la variable `mountPoint` reste à `null`. La JVM tente ensuite d&#039;appeler une méthode sur cet objet inexistant. C&#039;est l&#039;arroseur arrosé : la fonction censée protéger votre application devient la cause de son exécution sommaire.\n\n## Le Plot Twist : La différence subtile entre Ubuntu 24.04 et 25.10\n\nC&#039;est ici que l&#039;affaire devient fascinante.\n\nLe véritable coupable, c&#039;est l&#039;évolution de systemd (passé en version 258 sur la version d&#039;Ubuntu 25.10). Depuis la v256, systemd impose un &#039;durcissement&#039; de la hiérarchie cgroup v2. Il ne se contente plus d&#039;exposer les contrôleurs ; il les organise de façon beaucoup plus granulaire pour isoler les services. Les anciennes versions de Java, conçues à une époque où la hiérarchie était plus prévisible et moins protégée, se retrouvent littéralement &#039;aveugles&#039; face à cette nouvelle structure.\n\n**Et devinez quoi ?** La vieille logique d&#039;initialisation de Java (avant Java 17) est beaucoup trop rigide pour comprendre ce nouveau format.\n\nAu démarrage, le Java de notre conteneur fouille dans le système, ne trouve pas le contrôleur &quot;memory&quot; exactement là où il l&#039;attendait, et assigne silencieusement null à sa variable interne. À la ligne suivante, le code tente d&#039;appeler la méthode .getMountPoint() sur cet objet vide.\n\n**BOOM**. NullPointerException. Mort instantanée du processus.\n\nLe coupable n&#039;était ni notre code, ni notre configuration Docker, mais une ancienne JVM incapable de s&#039;adapter à la nouvelle hiérarchie d&#039;un noyau Linux moderne. Normale aussi vous me direz.\n\n## LA Solution\n\nLa solution propre, celle qu&#039;on devrait toujours utiliser en production :\n\n```bash\n# Mettre à jour vers une version récente de l&#039;image\ndocker pull selenium/standalone-chrome:4.20.0\n\n# OU utiliser une image avec Java 17+\ndocker pull selenium/standalone-chrome:latest\n```\n\n## Le Hack de survie — Désactiver UseContainerSupport\n\nSi vous ne pouvez pas mettre à jour l&#039;image (contraintes legacy, validation QA, etc.), vous pouvez désactiver la détection de conteneur :\n\n```bash\n# Option 1 : Via variable d&#039;environnement Docker\ndocker run -d \\\n  -e JAVA_OPTS=&quot;-XX:-UseContainerSupport&quot; \\\n  selenium/standalone-chrome:4.5.3\n\n# Option 2 : Via docker-compose.yml\nservices:\n  chrome:\n    image: selenium/standalone-chrome:4.5.3\n    environment:\n      - JAVA_OPTS=-XX:-UseContainerSupport\n```\n\n**⚠️ Avertissement important :**\n\n- **NE FAITES PAS ÇA EN PRODUCTION.**, dans mon cas il s&#039;agit d&#039;un container de **developpement** en locale.\n- Sans `UseContainerSupport`, Java ne connaît pas ses limites et peut se faire killer par le OOM Killer du système hôte.\n- Cette solution est un **pansement temporaire** en attendant la mise à jour.\n\n## La morale de cette histoire\n\nNos écosystèmes logiciels sont **fragiles**. Une simple mise à jour du noyau Linux — via une mise à jour d&#039;Ubuntu dans mon cas — peut casser des conteneurs qui fonctionnaient parfaitement d&#039;une version à l&#039;autre.\n\n**Les conseils philosophiques :**\n\n&gt; *Penser à nettoyer sa chambre régulièrement (je suis sûr que vous comprenez l&#039;image) peut faire gagner beaucoup de temps.*\n\n&gt; *Ne faites jamais de mise à jour d&#039;OS un vendredi.*\n\n&gt; *Prenez soin de toujours tester vos conteneurs Docker sur un environnement de staging après une mise à jour système.*\n\n&gt; *&quot;Works on my machine&quot; — jusqu&#039;à la mise à jour du kernel.*\n\n\n## Sources et Références\n\n- [Oracle Docs : Java Container Support](https://docs.oracle.com/en/java/javase/17+containers/) \n- [Ubuntu 25.10 Release Notes](https://ubuntu.com/blog/ubuntu-25-10)\n- [Docker &amp; Java : Best Practices](https://docker-java.readthedocs.io/)\n- [cgroup v2 kernel documentation](https://www.kernel.org/doc/Documentation/cgroup-v2.txt)\n- [Systemd News : Changes in unified cgroup hierarchy handling (v256+)](https://systemd.io/)",
        "date": "2026-04-28T00:00:00+00:00"
      }
    }
  ]
}