WebSphere Application Server PMI Metrics OpenShift Dashboard

Dashboard

Visualize PMI metrics data for your WebSphere Application Server on OpenShift
Last updated: 3 months ago

Start with Grafana Cloud and the new FREE tier. Includes 10K series Prometheus or Graphite Metrics and 50gb Loki Logs

Downloads: 4

Reviews: 0

  • os.jpg
    os.jpg

Starting in WebSphere Application Server 9.0.5.7 the IBM WebSphere team introduced a new capability where the statistical MBean data gathered by Performance Monitoring Infrastructure (PMI) is collected to produce metrics in Prometheus exposition format. This new capability allows the WebSphere Application Server topology to be monitored effectively using Prometheus and Grafana.

This dashboard leverages the Prometheus formatted metric data to visualize CPU, Memory Heap, Servlets, EJBs, Connection Pool, SIB, Sessions, Threadpool, Garbage Collection, and other JVM metrics of the entire cell.

This dashboard has been tested with the version 7.3.10 of Grafana provided by the Grafana Operator.

WebSphere Application Server:

To use this Grafana dashboard the WebSphere Application Server docker image deployed on OCP will need to have the metrics.ear deployed, GC profiler for the JVM configured (for the GC stats!) and have the PMI settings configured to specifically output the stats/metrics used by this Grafana dashboard.

  1. Provided below is the Dockerfile to build the customized WAS image and the required files/scripts for building the image.
  2. After building the image push it to the image registry of your choice and deploy it as an instance of the Runtime Component Operator
  3. You will then need to deploy Prometheus and Grafana on your OpenShift cluster and configure it to scrape metrics from your target service/deployment outlined at Application Monitoring on Red Hat OpenShift Container Platform (RHOCP) with Prometheus and Grafana. The documentation may be under the Open Liberty Operator repository , but the instructions for configuring Prometheus and Grafana are applicable here.

Dockerfile

FROM ibmcom/websphere-traditional:latest as base
FROM ibmcom/websphere-traditional:latest
COPY --from=base --chown=was:root /opt/IBM/WebSphere/AppServer/installableApps/metrics.ear /work/config/metrics.ear
COPY --chown=was:root install_app.py /work/config/
COPY --chown=was:root setPMI.py setGCProfiler.py /work/
COPY was-config.props /work/config/was-config.props
RUN env JVM_EXTRA_CMD_ARGS=-Xnoloa /work/configure.sh && /work/configure.sh /work/setGCProfiler.py && /work/configure.sh /work/setPMI.py

installApp.py

import sys
import os

global  AdminConfig

def getNodeId (prompt):
    nodeList = AdminConfig.list("Node").split("\n")

    if (len(nodeList) == 1):
        node = nodeList[0]
    else:
        print ""
        print "Available Nodes:"

        nodeNameList = []

        for item in nodeList:
            item = item.rstrip()
            name = getName(item) 

            nodeNameList.append(name)
            print "   " + name

        DefaultNode = nodeNameList[0]
        if (prompt == ""):
            prompt = "Select the desired node"

        nodeName = getValidInput(prompt+" ["+DefaultNode+"]:", DefaultNode, nodeNameList )

        index = nodeNameList.index(nodeName)
        node = nodeList[index]
    return node

def getServerId (prompt):
    serverList = AdminConfig.list("Server").split("\n")

    if (len(serverList) == 1):
        server = serverList[0]
    else:
        print ""
        print "Available Servers:"

        serverNameList = []

        for item in serverList:
            item = item.rstrip()
            name = getName(item)

            serverNameList.append(name)
            print "   " + name

        DefaultServer = serverNameList[0]
        if (prompt == ""):
            prompt = "Select the desired server"
        serverName = getValidInput(prompt+" ["+DefaultServer+"]:", DefaultServer, serverNameList )

        index = serverNameList.index(serverName)
        server = serverList[index]

    return server

def getName (objectId):
    endIndex = (objectId.find("(c") - 1)
    stIndex = 0
    if (objectId.find("\"") == 0):
        stIndex = 1
    return objectId[stIndex:endIndex+1]

print "Installing application ..."

node = getName(getNodeId(""))
server = getName(getServerId(""))

parms = "-appname Application"
parms += " -node " + node + " -server " + server
parms += " -nouseMetaDataFromBinary"
app = AdminApp.install("/work/config/metrics.ear", [parms])

AdminTask.setGenericJVMArguments('[-nodeName ' + node + ' -serverName ' + server + ' -genericJvmArguments "-Xnoloa"]')

AdminConfig.save()

was-config.props

ResourceType=ThreadPool
ImplementingResourceType=Server
ResourceId=Cell=!{cellName}:Node=!{nodeName}:Server=!{serverName}:ThreadPoolManager=:ThreadPool=

maximumSize=100
name=WebContainer
minimumSize=100
inactivityTimeout=60000

setGCProfiler.py

# -*- coding: utf-8 -*-

def main():

    #Goes through each node
    for node in AdminConfig.list('Node').split():
        #Acquire node name
        nodeName = AdminConfig.showAttribute(node, 'name')
        for server in AdminControl.queryNames("type=Server,node="+ nodeName + ",*").split():
            #Acquire server name
            serverName = AdminControl.getAttribute(server, 'name')
            print("serverName " + serverName)
            #Instrument JVM profiler
            AdminTask.setJVMProperties('[-nodeName ' + nodeName + ' -serverName ' + serverName + ' -genericJvmArguments "-agentlib:pmiJvmtiProfiler"]')
            AdminConfig.save()
            print("Finished instrumenting GC profiler")

if __name__ == "__main__":
    main()

setPMI.py

# -*- coding: utf-8 -*-

def main():

    #Enables all specified attributes
    customString = [append(), java.lang.Boolean ('true')]
    #Goes through each node
    for node in AdminConfig.list('Node').split():
        nName = AdminConfig.showAttribute(node, 'name')
        #Makes sure to not include the cell manager nodes
        for server in AdminControl.queryNames("type=Server,node="+ nName + ",*").split():
            #This obtains the Performance Mbean object.
            #First it gets the MBean name (i.e perfStr)
            #SEcond it obtains the Mbean Object (I.e perfObj)
            processName = AdminControl.getAttribute(server, 'name')
            perfStr = AdminControl.queryNames("type=Perf,process=" + processName + ",node=" + nName + ",*")
            perfObj = AdminControl.makeObjectName(perfStr)

            print("configuring:" + processName + ". From: " + nName)
            invoke(perfStr, perfObj, customString)
            print("done")

#Below is how you would ENABLE or DISABLE specific PMI stats/attributes.
#The attribute IDs/Numbers are derived from: https://www.ibm.com/support/knowledgecenter/en/SSAW57_9.0.5/com.ibm.websphere.nd.multiplatform.doc/ae/rprf_datacounter14.html
def append():
    string ='servletSessionsModule=6,1,2,7'
    string+=':beanModule=11,12'
    string+=':webAppModule=11,13'
    string+=':threadPoolModule=3,8,6,4,7'
    string+=':StatGroup.SIBService=8,14'
    string+=':StatGroup.SIBService>StatGroup.Communications>StatGroup.Clients>StatGroup.ClientsStandard=563,562'
    string+=':StatGroup.SIBService>StatGroup.Communications>StatGroup.MessagingEngines>StatGroup.MessagingEnginesStandard='
    string+=':StatGroup.SIBService>StatGroup.SIBMessagingEngines=513,512'
    string+=':systemModule=1,2,3'
    string+=':j2cModule=2,15,1,6,14,12,13,7'
    string+=':connectionPoolModule=2,15,1,6,14,12,13,7'
    string+=':jvmRuntimeModule=1,2,3,4,5,11,12,13'
    string+=':beanModule=11,12'
    return string

def invoke(perfStr, perfObj, customString):
    sigs = ['java.lang.String', 'java.lang.Boolean']
    AdminControl.invoke_jmx (perfObj, 'setCustomSetString', customString, sigs)
    AdminControl.invoke(perfStr,'savePMIConfiguration')

if __name__ == "__main__":
    main()