CPU and Memory monitoring using docker environment

28 views
Skip to first unread message

Peter Ngimbwa

unread,
Jun 21, 2025, 11:51:29 AMJun 21
to jmeter-plugins
Hello,

I am trying to run JMeter in a Docker environment (using Docker Compose) to measure performance metrics (such as CPU and memory usage) of a database with the ServerAgent plugin (v.2.2.3). I use the .jmx file with the code shown below, hoping to return the metric values of CPU among others. However, it returns an empty file, as seen in the attached document. What could be wrong? I build and run the Dockerfile as follows:

docker compose build --no-cache
docker compose up

the perfmon_only.csv

timeStamp

elapsed

label

responseCode

responseMessage

threadName

dataType

success

failureMessage

bytes

sentBytes

grpThreads

allThreads

URL

Latency

IdleTime

Connect



the .jmx file
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Simple MongoDB Insert" enabled="true">
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="MongoDB Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">10</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.duration">60</longProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Insert MongoDB Document" enabled="true">
<stringProp name="cacheKey">true</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<stringProp name="script">
import com.mongodb.client.MongoClients
import org.bson.Document

def uri = "mongodb://staging:staging@mongodb:27017"
def databaseName = "HortPlant"
def collectionName = "plantFeature"

// Connect to MongoDB
def mongoClient = MongoClients.create(uri)
def database = mongoClient.getDatabase(databaseName)
def collection = database.getCollection(collectionName)

// Create and insert a document
def doc = new Document('plantId', "plant_001")
.append("height", 32.5)
.append("leafCount", 14)
collection.insertOne(doc)

log.info("Inserted document: ${doc.toJson()}")

// ⏱️ Add delay to allow PerfMon to collect data
Thread.sleep(10000) // 10 seconds

// Close connection
mongoClient.close()

</stringProp>
<stringProp name="scriptLanguage">groovy</stringProp>
</JSR223Sampler>
<hashTree/>
<ResultCollector guiclass="SimpleDataWriter" testclass="ResultCollector" testname="Simple Data Writer" enabled="true">
<boolProp name="ResultCollector.error_logging">true</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename">/jmeter_results/mongodb.jtl</stringProp>
</ResultCollector>
<hashTree/>
<kg.apc.jmeter.perfmon.PerfMonCollector guiclass="kg.apc.jmeter.perfmon.PerfMonCollectorGui" testclass="kg.apc.jmeter.perfmon.PerfMonCollector" testname="PerfMon CSV Collector" enabled="true">
<boolProp name="ResultCollector.error_logging">true</boolProp>
<stringProp name="interval">1000</stringProp>
<stringProp name="filename">/jmeter_results/perfmon_only.csv</stringProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<collectionProp name="nodes">
<stringProp name="0">server-agent;4444;cpu;total</stringProp>
<stringProp name="1">server-agent;4444;memory;used</stringProp>
<stringProp name="2">server-agent;4444;disks-io;sda</stringProp>
</collectionProp>
</kg.apc.jmeter.perfmon.PerfMonCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

Bówén

unread,
Jun 22, 2025, 12:49:14 AMJun 22
to jmeter-plugins

Your perfmon_only.csv is empty because the PerfMon listener never actually polled any metrics—your “nodes” block in the JMX isn’t being parsed, and there’s no ServerAgent for it to talk to. Two things to fix:


1. Run a ServerAgent in Docker Compose

The PerfMon plugin is just the client—you still need to launch the standalone ServerAgent on the machine (or container) you want to monitor. In a Compose setup you’ll want something like:

services:
  server-agent:
    image: openjdk:8-jre-alpine
    # mount the agent JAR you downloaded from the PerfMon plugin site
    volumes:
      - ./perfmon/ServerAgent.jar:/opt/serveragent/ServerAgent.jar
    working_dir: /opt/serveragent
    command: ["java","-jar","ServerAgent.jar"]
    ports:
      - "4444:4444"

  jmeter:
    image: your-jmeter-with-plugins
    depends_on:
      - server-agent
    volumes:
      - ./tests:/jmeter/tests
      - ./jmeter_results:/jmeter_results
    # other config…

Inside Compose, every service is reachable by its name over the default network, so pointing at server-agent:4444 from your JMeter container will work (docs.docker.com).


2. Correct the “nodes” entries in your JMX

Your <collectionProp name="nodes"> currently has stringProps like:

<stringProp name="0">server-agent;4444;cpu;total</stringProp>

But PerfMon expects five semicolon-separated fields:

alias ; host ; port ; metric ; parameter

You’ve provided only four, so the plugin can’t parse them and simply skips polling. You can fix it either by:

  1. Using a blank alias (most common):

  1. <stringProp name="0">;server-agent;4444;cpu;total</stringProp> <stringProp name="1">;server-agent;4444;memory;used</stringProp> <stringProp name="2">;server-agent;4444;disks-io;sda</stringProp>
  1. Giving each row an explicit alias:

    <stringProp name="0">agent1;server-agent;4444;cpu;total</stringProp> <stringProp name="1">agent1;server-agent;4444;memory;used</stringProp> <stringProp name="2">agent1;server-agent;4444;disks-io;sda</stringProp>

Once you’ve launched the ServerAgent container and updated your node definitions to include all five fields, rerun your test. The PerfMon listener will finally connect to port 4444 and you’ll start seeing columns like CPU %, Memory Used, and Disk IO in perfmon_only.csv (jmeter-plugins.org).

Peter Ngimbwa

unread,
Jun 23, 2025, 5:51:05 PMJun 23
to jmeter-...@googlegroups.com
I tried it, but it didn't work. However, I found another solution whereby I can now see other outputs except CPU, RAM, and Disk I/O

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Optimized MongoDB Insert Test" enabled="true">
<stringProp name="TestPlan.comments">
This Test Plan has been modified to:
1. Use User Defined Variables for flexibility.
2. Remove the Thread.sleep() anti-pattern from the sampler.
3. Generate dynamic and unique data for each MongoDB insert.
4. Include detailed comments in the Groovy script explaining database connection best practices (e.g., connection pooling).
5. Use a standardized and corrected PerfMon Metrics Collector configuration.
</stringProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="mongoURI" elementType="Argument">
<stringProp name="Argument.name">mongoURI</stringProp>
<stringProp name="Argument.value">mongodb://staging:staging@mongodb:27017</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<stringProp name="Argument.desc">MongoDB Connection String</stringProp>
</elementProp>
<elementProp name="mongoDatabase" elementType="Argument">
<stringProp name="Argument.name">mongoDatabase</stringProp>
<stringProp name="Argument.value">HortPlant</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<stringProp name="Argument.desc">Database Name</stringProp>
</elementProp>
<elementProp name="mongoCollection" elementType="Argument">
<stringProp name="Argument.name">mongoCollection</stringProp>
<stringProp name="Argument.value">plantFeature</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<stringProp name="Argument.desc">Collection Name</stringProp>
</elementProp>
<elementProp name="perfmonHost" elementType="Argument">
<stringProp name="Argument.name">perfmonHost</stringProp>
<stringProp name="Argument.value">mongodb</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<stringProp name="Argument.desc">Host running the PerfMon Server Agent (e.g., your DB server)</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="MongoDB Writers" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">true</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">10</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">60</stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Insert Dynamic MongoDB Document" enabled="true">
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<kg.apc.jmeter.perfmon.PerfMonCollector guiclass="kg.apc.jmeter.vizualizers.PerfMonGui" testclass="kg.apc.jmeter.perfmon.PerfMonCollector" testname="jp@gc - PerfMon Metrics Collector" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<stringProp name="filename">/jmeter_results/perfmon_results.csv</stringProp>
<longProp name="interval_grouping">1000</longProp>
<boolProp name="graph_aggregated">false</boolProp>
<stringProp name="include_sample_labels"></stringProp>
<stringProp name="exclude_sample_labels"></stringProp>
<stringProp name="start_offset"></stringProp>
<stringProp name="end_offset"></stringProp>
<boolProp name="include_checkbox_state">false</boolProp>
<boolProp name="exclude_checkbox_state">false</boolProp>
<collectionProp name="metricConnections">
<collectionProp name="cpu-metric">
<stringProp name="host">server-agent</stringProp>
<stringProp name="port">4444</stringProp>
<stringProp name="metric">CPU</stringProp>
<stringProp name="parameters"></stringProp>
</collectionProp>
<collectionProp name="memory-metric">
<stringProp name="host">server-agent</stringProp>
<stringProp name="port">4444</stringProp>
<stringProp name="metric">Memory</stringProp>
<stringProp name="parameters"></stringProp>
</collectionProp>
<collectionProp name="disk-io-metric">
<stringProp name="host">server-agent</stringProp>
<stringProp name="port">4444</stringProp>
<stringProp name="metric">Disks I/O</stringProp>
<stringProp name="parameters"></stringProp>
</collectionProp>

</collectionProp>
</kg.apc.jmeter.perfmon.PerfMonCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>


Do you have any suggestions?

--
You received this message because you are subscribed to the Google Groups "jmeter-plugins" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jmeter-plugin...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/jmeter-plugins/5c82e36d-71b1-4429-9dfd-a7f31adcf7d6n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages