JMeter’s default configuration is a single-threaded juggernaut, but when you need to flood a system with load from many machines, its Kubernetes deployment becomes a distributed ballet.

Let’s see it in action. Imagine you’ve got a JMeter test plan (my_test.jmx) and you want to run it across 10 Kubernetes pods, each acting as a JMeter client. Your Kubernetes cluster is already set up.

First, you need a Docker image that has JMeter installed and can run a test. A simple Dockerfile might look like this:

FROM alpine:latest

RUN apk add --no-cache openjdk11-jdk wget && \
    wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.5.tgz && \
    tar -xzf apache-jmeter-5.5.tgz && \
    mv apache-jmeter-5.5 /opt/jmeter && \
    rm apache-jmeter-5.5.tgz

ENV PATH="/opt/jmeter/bin:$PATH"

Build this image and push it to a registry your Kubernetes cluster can access (e.g., Docker Hub, Google Container Registry). Let’s say your image is your-docker-repo/jmeter-client:latest.

Now, you need a Kubernetes Job to orchestrate this. This Job will launch multiple pods, each running your JMeter client image.

apiVersion: batch/v1
kind: Job
metadata:
  name: jmeter-load-test
spec:
  completions: 10  # This is key: we want 10 successful pods
  parallelism: 10  # And we want them to start concurrently
  template:
    spec:
      restartPolicy: Never # Important: Job pods shouldn't restart on failure
      containers:
      - name: jmeter-client
        image: your-docker-repo/jmeter-client:latest
        command: ["jmeter", "-n", "-t", "/jmeter/my_test.jmx", "-l", "/jmeter/results.jtl", "-R", "192.168.1.100,192.168.1.101"] # Placeholder for RMI hosts
        volumeMounts:
        - name: test-plan
          mountPath: /jmeter
      volumes:
      - name: test-plan
        configMap:
          name: jmeter-test-plan # We'll create this ConfigMap next

The problem here is that JMeter needs to know about its master (the pod orchestrating the test) and its slaves (the other pods running the actual test load). By default, JMeter uses RMI (Remote Method Invocation) for this. The -R flag in the command is where you’d list the IP addresses of your slave JMeter instances. However, in Kubernetes, pod IPs are ephemeral and dynamic. This makes the -R flag with static IPs a non-starter.

This is where the JMeter Master/Slave setup in Kubernetes pattern comes in. You don’t typically use -R directly. Instead, you designate one pod as the master and the others as slaves. The master pod then discovers and communicates with the slaves.

Here’s how you actually set it up in Kubernetes:

  1. The Master Pod: This pod will run JMeter in server mode. It listens for incoming RMI connections from the slaves.
  2. The Slave Pods: These pods will run JMeter in client mode, connecting to the master and executing the test plan.

You’ll need two Kubernetes Deployments (or Jobs if the test is a one-off): one for the master and one for the slaves.

Master Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jmeter-master
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jmeter
      role: master
  template:
    metadata:
      labels:
        app: jmeter
        role: master
    spec:
      containers:
      - name: jmeter-master
        image: your-docker-repo/jmeter-master:latest # Image with jmeter and rmi enabled
        ports:
        - containerPort: 1099 # RMI default port
        - containerPort: 50000 # RMI default high port range
        command: ["sh", "-c", "jmeter -s -j /jmeter/jmeter.log"] # Run in server mode
        env:
        - name: JMETER_MASTER_HOST
          value: "jmeter-master-svc" # Kubernetes Service name for the master

Slave Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jmeter-slaves
spec:
  replicas: 10 # Scale this to your needs
  selector:
    matchLabels:
      app: jmeter
      role: slave
  template:
    metadata:
      labels:
        app: jmeter
        role: slave
    spec:
      containers:
      - name: jmeter-slave
        image: your-docker-repo/jmeter-client:latest # Image with jmeter client
        command: ["sh", "-c", "jmeter -n -t /jmeter/my_test.jmx -l /jmeter/results.jtl -H $JMETER_MASTER_HOST -P 1099"] # Connect to master
        env:
        - name: JMETER_MASTER_HOST
          valueFrom:
            service:
              name: jmeter-master-svc # Reference the master service
              port: 1099
        volumeMounts:
        - name: test-plan
          mountPath: /jmeter
      volumes:
      - name: test-plan
        configMap:
          name: jmeter-test-plan

You’ll also need a Service for the master so the slaves can reliably find it.

Master Service:

apiVersion: v1
kind: Service
metadata:
  name: jmeter-master-svc
spec:
  selector:
    app: jmeter
    role: master
  ports:
  - protocol: TCP
    port: 1099
    targetPort: 1099
  - protocol: TCP
    port: 50000 # RMI dynamic ports
    targetPort: 50000

And a ConfigMap to hold your test plan:

apiVersion: v1
kind: ConfigMap
metadata:
  name: jmeter-test-plan
data:
  my_test.jmx: |
    <?xml version="1.0" encoding="UTF-8"?>
    <jmeterTestPlan version="1.2" ...>
      ... your test plan ...
    </jmeterTestPlan>

The jmeter -s command starts JMeter in server mode. The jmeter -n -t ... -H $JMETER_MASTER_HOST -P 1099 command tells the slave to run a non-GUI test, targeting the specified master host and port. The valueFrom.service in the slave deployment automatically resolves the master’s IP through the Kubernetes DNS.

When you scale the jmeter-slaves deployment to 10 replicas, Kubernetes will launch 10 slave pods. Each slave pod, upon starting, will try to connect to the jmeter-master-svc on port 1099. The JMeter master, running in server mode, will accept these connections. The master then coordinates the test execution, distributing the load across all connected slaves. The results are typically aggregated on the master or collected from individual slaves.

The real magic happens because Kubernetes manages the IP addresses and network routing. The jmeter-master-svc provides a stable DNS name (jmeter-master-svc) that the slaves can use to find the master, regardless of which specific pod IP the master is running on. The RMI communication then flows through Kubernetes networking.

The most surprising thing about this setup is that JMeter’s RMI, which feels ancient, actually works quite well in Kubernetes because the cluster handles the dynamic IP resolution and network proxying for you. You don’t need to pre-configure RMI registry hosts for each slave.

The next hurdle you’ll likely face is managing and collecting the large volumes of result data (.jtl files) generated by many distributed JMeter slaves.

Want structured learning?

Take the full Jmeter course →