Monitoring JVM avec Prometheus

Cet article présente comment superviser des JVMs grâce à la solution open-source Prometheus.

Ce produit s’apparente à une solution de supervision et d’alerte des composants régulièrement rencontrés dans un SI : bases de données, MOM, systèmes de fichiers, répartiteurs  de charge, serveurs d’application, serveurs HTTP, etc…

Le produit récupères les infos auprès des différents composants qu’il supervise et les stocke dans sa base de données de séries chronologiques (time-series database)

La suite de l’article montre comment configurer Prométheus pour superviser un serveur WebLogic.

Prometheus interroge l’instance WebLogic Server afin de récupérer les métriques pour les stocker dans sa base.

 

Installation

Télécharger et installer le produit en suivant ces instructions.

 

Configuration de Prometheus

La configuration s’effectue dans le fichier de configuration YAML prometheus.yml situé à la racine de l’installation de Prometheus.

[pastacode lang= »haml » manual= »%23%20my%20global%20config%0Aglobal%3A%0A%20%20scrape_interval%3A%20%20%20%20%2015s%20%23%20Set%20the%20scrape%20interval%20to%20every%2015%20seconds.%20Default%20is%20every%201%20minute.%0A%20%20evaluation_interval%3A%2015s%20%23%20Evaluate%20rules%20every%2015%20seconds.%20The%20default%20is%20every%201%20minute.%0A%20%20%23%20scrape_timeout%20is%20set%20to%20the%20global%20default%20(10s).%0A%0A%23%20Alertmanager%20configuration%0Aalerting%3A%0A%20%20alertmanagers%3A%0A%20%20-%20static_configs%3A%0A%20%20%20%20-%20targets%3A%0A%20%20%20%20%20%20%23%20-%20alertmanager%3A9093%0A%0A%23%20Load%20rules%20once%20and%20periodically%20evaluate%20them%20according%20to%20the%20global%20’evaluation_interval’.%0Arule_files%3A%0A%20%20%23%20-%20%22first_rules.yml%22%0A%20%20%23%20-%20%22second_rules.yml%22%0A%0A%23%20A%20scrape%20configuration%20containing%20exactly%20one%20endpoint%20to%20scrape%3A%0A%23%20Here%20it’s%20Prometheus%20itself.%0Ascrape_configs%3A%0A%20%20%23%20The%20job%20name%20is%20added%20as%20a%20label%20%60job%3D%3Cjob_name%3E%60%20to%20any%20timeseries%20scraped%20from%20this%20config.%0A%20%20-%20job_name%3A%20’weblogic’%0A%0A%20%20%20%20%23%20metrics_path%20defaults%20to%20’%2Fmetrics’%0A%20%20%20%20%23%20scheme%20defaults%20to%20’http’.%0A%20%20%20%20metrics_path%3A%20%2Fmetrics%0A%20%20%20%20static_configs%3A%0A%20%20%20%20%20%20-%20targets%3A%20%5B’localhost%3A7001’%5D » message= »Configuration de Prometheus » highlight= » » provider= »manual »/]

Le job « weblogic » définit les composants à superviser (targets). Dans notre exemple, il sagit d’un serveur WebLogic Server qui écoute sur le port 7001.

Prometheus interrogera l’URL http://localhost:7001/metrics pour récupérer les métriques toutes les 15 secondes.

Le context root /metrics correspond à une Servlet déployée dans le serveur WebLogic. Son code est fournit ci-après.

Servlet /metrics

 

La servlet récupères les informations auprès de MBeans JMX et les expose via la méthode service() qui sera invoquée par Prometheus.

L’exemple donné ci-après remonte la mémoire disponible ainsi que la capacité maximale actuelle de la HEAP de la JVM.

 

Code source

[pastacode lang= »java » manual= »package%20fr.corsaireconsulting.metrics%3B%0A%0Aimport%20java.io.IOException%3B%0Aimport%20java.io.PrintWriter%3B%0Aimport%20java.lang.management.ManagementFactory%3B%0A%0Aimport%20javax.management.AttributeNotFoundException%3B%0Aimport%20javax.management.InstanceNotFoundException%3B%0Aimport%20javax.management.MBeanException%3B%0Aimport%20javax.management.MBeanServer%3B%0Aimport%20javax.management.MalformedObjectNameException%3B%0Aimport%20javax.management.ObjectName%3B%0Aimport%20javax.management.ReflectionException%3B%0Aimport%20javax.servlet.ServletException%3B%0Aimport%20javax.servlet.http.HttpServlet%3B%0Aimport%20javax.servlet.http.HttpServletRequest%3B%0Aimport%20javax.servlet.http.HttpServletResponse%3B%0A%0A%2F**%0A%20*%20Servlet%20implementation%20class%20PrometheusExporter%0A%20*%2F%0Apublic%20class%20PrometheusExporter%20extends%20HttpServlet%20%7B%0A%09private%20static%20final%20long%20serialVersionUID%20%3D%201L%3B%0A%09%0A%09private%20MBeanServer%20mbs%20%3D%20null%3B%0A%09private%20String%20domainName%20%3D%20null%3B%0A%09private%20String%20serverName%20%3D%20null%3B%0A%09private%20javax.management.ObjectName%20jvmRuntime%20%3D%20null%3B%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20%40see%20HttpServlet%23HttpServlet()%0A%20%20%20%20%20*%2F%0A%20%20%20%20public%20PrometheusExporter()%20%7B%0A%20%20%20%20%20%20%20%20super()%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20private%20void%20trace(String%20message)%20%7B%0A%20%20%20%20%09System.out.println(%22PrometheusExporter%3A%22%2Bmessage)%3B%0A%20%20%20%20%7D%0A%0A%09%2F**%0A%09%20*%20%40see%20HttpServlet%23doGet(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%0A%09%20*%2F%0A%09protected%20void%20doGet(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%20throws%20ServletException%2C%20IOException%20%7B%0A%09%09service(request%2C%20response)%3B%0A%09%7D%0A%0A%09%2F**%0A%09%20*%20%40see%20HttpServlet%23doPost(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%0A%09%20*%2F%0A%09protected%20void%20doPost(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%20throws%20ServletException%2C%20IOException%20%7B%0A%09%09service(request%2C%20response)%3B%0A%09%7D%20%0A%0A%09%40Override%0A%09protected%20void%20service(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%0A%09%09%09throws%20ServletException%2C%20IOException%20%7B%0A%0A%09%09trace(%22servce.enter%22)%3B%0A%09%09trace(%22Request%20received%22)%3B%0A%09%09trace(request.getMethod()%2B%22%20%22%2Brequest.getQueryString())%3B%09%0A%09%09%0A%09%09response.setContentType(%22text%2Fplain%22)%3B%0A%09%09PrintWriter%20out%20%3D%20response.getWriter()%3B%0A%09%09%0A%09%09try%20%7B%0A%09%09%09Long%20heapFree%20%3D%20(Long)(mbs.getAttribute(jvmRuntime%2C%20%22HeapFreeCurrent%22))%3B%0A%09%09%09Long%20heapCurrent%20%3D%20(Long)(mbs.getAttribute(jvmRuntime%2C%20%22HeapSizeCurrent%22))%3B%0A%09%09%09out.println(%22jvm_heap_free_current%7Bdomain%3D%5C%22%22%2BdomainName%2B%22%5C%22%2Cname%3D%5C%22%22%2BserverName%2B%22%5C%22%7D%20%22%2BheapFree.longValue())%3B%0A%09%09%09out.println(%22jvm_heap_size_current%7Bdomain%3D%5C%22%22%2BdomainName%2B%22%5C%22%2Cname%3D%5C%22%22%2BserverName%2B%22%5C%22%7D%20%22%2BheapCurrent.longValue())%3B%0A%09%09%7D%20catch%20(AttributeNotFoundException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(InstanceNotFoundException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(MBeanException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(ReflectionException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%0A%09%09%0A%09%09%0A%09%09out.flush()%3B%0A%09%09out.close()%3B%0A%09%09trace(%22servce.exit%22)%3B%0A%0A%09%7D%0A%0A%09%40Override%0A%09public%20void%20init()%20throws%20ServletException%20%7B%0A%09%09super.init()%3B%0A%09%09trace(%22init.enter%22)%3B%0A%09%09mbs%20%3D%20ManagementFactory.getPlatformMBeanServer()%3B%0A%09%09%0A%09%09try%20%7B%0A%09%09%09javax.management.ObjectName%20service%20%3D%20new%20javax.management.ObjectName(%22com.bea%3AName%3DRuntimeService%2CType%3Dweblogic.management.mbeanservers.runtime.RuntimeServiceMBean%22)%3B%0A%09%09%09javax.management.ObjectName%20dconf%20%3D%20(javax.management.ObjectName)mbs.getAttribute(service%2C%20%22DomainConfiguration%22)%3B%0A%09%09%09%0A%0A%09%09%09javax.management.ObjectName%20serverConf%20%3D%20(javax.management.ObjectName)mbs.getAttribute(service%2C%20%22ServerRuntime%22)%3B%0A%09%09%09%0A%09%09%09domainName%20%3D%20(String)(mbs.getAttribute(dconf%2C%20%22Name%22))%3B%0A%09%09%09serverName%20%3D%20(String)(mbs.getAttribute(serverConf%2C%20%22Name%22))%3B%0A%09%09%09%0A%09%09%09jvmRuntime%20%3D%20new%20ObjectName(%22com.bea%3AServerRuntime%3D%22%2BserverName%2B%22%2CName%3D%22%2BserverName%2B%22%2CType%3DJRockitRuntime%22)%3B%0A%0A%09%09%09trace(%22domaineName%3A%22%2BdomainName)%3B%0A%09%09%09trace(%22serverName%3A%22%2BserverName)%3B%0A%0A%09%09%7D%20catch%20(MalformedObjectNameException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(NullPointerException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(AttributeNotFoundException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(InstanceNotFoundException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(MBeanException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%20catch%20(ReflectionException%20e)%20%7B%0A%09%09%09e.printStackTrace()%3B%0A%09%09%7D%0A%09%09trace(%22init.exit%22)%3B%0A%09%7D%0A%0A%7D%0A » message= » » highlight= » » provider= »manual »/]

Configuration

La déclaration de la servlet dans le fichier web.xml de l’application web :

[pastacode lang= »markup » manual= »%20%20%3Cservlet%3E%0A%20%20%20%20%3Cdescription%3E%3C%2Fdescription%3E%0A%20%20%20%20%3Cdisplay-name%3EPrometheusExporter%3C%2Fdisplay-name%3E%0A%20%20%20%20%3Cservlet-name%3EPrometheusExporter%3C%2Fservlet-name%3E%0A%20%20%20%20%3Cservlet-class%3Efr.corsaireconsulting.metrics.PrometheusExporter%3C%2Fservlet-class%3E%0A%20%20%3C%2Fservlet%3E%0A%20%20%3Cservlet-mapping%3E%0A%20%20%20%20%3Cservlet-name%3EPrometheusExporter%3C%2Fservlet-name%3E%0A%20%20%20%20%3Curl-pattern%3E%2F*%3C%2Furl-pattern%3E%0A%20%20%3C%2Fservlet-mapping%3E » message= »extrait du fichier web.xml » highlight= » » provider= »manual »/]

 

Exécution

Créer un domaine WebLogic Server avec un seul serveur d’administration.

Démarrer le serveur WevLogic  et y déployer l’application web (avec la console d’administration par exemple)

Lancer le serveur Prometheus.

Prometheus démarre directement la capture des données. Vérifier dans la sortie standard du serveur WebLogic que la servlet est bien appelée par Prometheus.

Lancer la console Prometheus : http://localhost:9090/graph

 

Le reste est histoire d’imagination et de créativité Enjoy !