Skip to content

Repository files navigation

jmeter-docker

Apache JMeter 5.6.3 with the Plugins Manager and common jp@gc plugins baked in.

Two roles:

  • Workers run the jmeter-worker image, which starts jmeter-server on 1099/tcp and 50000/tcp.
  • The main controller runs the jmeter-base image, publishes 60000/tcp, and launches the distributed test against the workers.

Bundled jp@gc plugins

Plugins are installed at build time by the PluginsManagerCMD.sh install … line in the Dockerfile — a comma-separated list of plugin IDs from the jp@gc catalog.

  • Change the bundled set: edit that install list in the Dockerfile (use id=version to pin), then rebuild. jmeter-worker builds on jmeter-base, so both get the same plugins.
  • Find plugin IDs: browse the catalog, or run PluginsManagerCMD.sh status / available in a container (see the CLI docs).
  • Add plugins without rebuilding: in your own Dockerfile, FROM jmeter-docker:jmeter-base-5.6.3, then as root run "${JMETER_HOME}/bin/PluginsManagerCMD.sh" install your-id (and chown -R jmeter:jmeter "${JMETER_HOME}"), then switch back to USER jmeter.

Build

export DOCKER_BUILDKIT=1

docker compose build
# …or with Bake:
docker buildx bake

# …or a single target:
docker build --pull --target jmeter-base -t bsmithio/jmeter-base:latest .
docker build --pull --target jmeter-worker -t bsmithio/jmeter-worker:latest .

Compose and Bake tag the images jmeter-docker:jmeter-base-<version> and jmeter-docker:jmeter-worker-<version> — the tags used in the examples below.

To change the JMeter version, update JMETER_VERSION in each of:

Also refresh the .sha512 file when the archive version changes.

docker build --pull --target jmeter-base \
  --build-arg JMETER_VERSION=5.6.3 \
  -t my/jmeter-base:5.6.3 .

On your laptop

Start the main controller

docker run --detach --name main-controller \
  -p 60000:60000 \
  jmeter-docker:jmeter-base-5.6.3

Start the workers

Set LOCALIP to the address the controller uses to reach each worker.

docker run --detach \
  --name worker1 \
  -e LOCALIP='192.168.1.101' \
  -p 1099:1099 -p 50000:50000 \
  jmeter-docker:jmeter-worker-5.6.3

docker run --detach \
  --name worker2 \
  -e LOCALIP='192.168.1.102' \
  -p 1099:1099 -p 50000:50000 \
  jmeter-docker:jmeter-worker-5.6.3

Check they're running

docker container ls --format '{{.Names}}\t{{.Status}}'

Copy a test plan into the controller

${JMETER_HOME} is /opt/apache-jmeter-5.6.3 inside the image.

docker exec -i main-controller sh -c 'cat > "${JMETER_HOME}/bin/test.jmx"' < test.jmx

Run the distributed test

Set CONTROLLER_IP, CLIENT_PORT, and the worker IPs for your environment.

docker exec -it main-controller "${JMETER_HOME}/bin/jmeter" \
  -n \
  -t "${JMETER_HOME}/bin/test.jmx" \
  -Djava.rmi.server.hostname="${CONTROLLER_IP}" \
  -Dclient.rmi.localport="${CLIENT_PORT}" \
  -R "${WORKER1_IP},${WORKER2_IP}"

Shell into a container

docker exec -it main-controller bash   # controller
docker exec -it worker1 bash           # worker

On AWS (or similar) hosts

Firewall / security groups

⚠️ By default, workers require no authentication and use no encryption. They run any test plan they receive, and test plans can run arbitrary code (JSR223/Groovy, OS Process samplers). Anyone who can reach 1099/tcp or 50000/tcp can run code inside the worker. Never expose these ports to the public internet — never 0.0.0.0/0. Restrict them to the controller and workers, prefer a private network (VPC/VPN), and set LOCALIP to a private address. To require authentication and encryption, enable RMI SSL.

Open only what you need, scoped to trusted sources:

  • 22/tcp — SSH, restricted to your admin IPs
  • 1099/tcp & 50000/tcp — workers, restricted to the controller and workers
  • 60000/tcp — controller (-Dclient.rmi.localport), restricted to the workers

Controller

docker run --detach --name main-controller \
  --restart unless-stopped \
  -p 60000:60000 \
  jmeter-docker:jmeter-base-5.6.3

Workers

The lookup below sets LOCALIP to the host's public IP — use it only if the controller reaches workers over public addresses. On a private network, use the private IP and bind ports to that interface (e.g. -p 10.0.0.5:1099:1099).

docker run --detach \
  --name worker1 \
  --restart unless-stopped \
  -e LOCALIP="$(curl -fs https://checkip.amazonaws.com)" \
  -p 1099:1099 -p 50000:50000 \
  jmeter-docker:jmeter-worker-5.6.3

Copy in test.jmx and run the test the same way as on your laptop.

Enabling RMI SSL

To require authentication and encryption, give the controller and every worker the same keystore — a peer without it can't connect, which closes the code-execution risk noted above.

1. Generate one shared keystore

Generate it once and copy the same file to every host:

docker run --rm -v "$(pwd):/out" -w /out \
  jmeter-docker:jmeter-base-5.6.3 \
  sh -c '"${JMETER_HOME}/bin/create-rmi-keystore.sh" && cp rmi_keystore.jks /out/'

This writes rmi_keystore.jks to the current directory; note the password you set (JMeter's default is changeit). The same file and password must go to the controller and all workers — a per-host keystore won't work.

2. Start workers with the keystore

Mount it read-only and set RMI_KEYSTORE and RMI_KEYSTORE_PASSWORD:

docker run --detach \
  --name worker1 \
  --restart unless-stopped \
  -e LOCALIP='192.168.1.101' \
  -e RMI_KEYSTORE=/rmi_keystore.jks \
  -e RMI_KEYSTORE_PASSWORD='changeit' \
  -v "$(pwd)/rmi_keystore.jks:/rmi_keystore.jks:ro" \
  -p 1099:1099 -p 50000:50000 \
  jmeter-docker:jmeter-worker-5.6.3

Env vars: RMI_KEYSTORE (path in the container; setting it enables SSL), RMI_KEYSTORE_PASSWORD (required), RMI_KEYSTORE_TYPE (optional, default JKS). The password is read from a private file, not the command line, so it never appears in the process list.

3. Run the controller with the same keystore

Mount the keystore and pass the SSL properties on the run:

docker run --detach --name main-controller \
  --restart unless-stopped \
  -p 60000:60000 \
  -v "$(pwd)/rmi_keystore.jks:/rmi_keystore.jks:ro" \
  jmeter-docker:jmeter-base-5.6.3

docker exec -it main-controller "${JMETER_HOME}/bin/jmeter" \
  -n -t "${JMETER_HOME}/bin/test.jmx" \
  -Djava.rmi.server.hostname="${CONTROLLER_IP}" \
  -Dclient.rmi.localport="${CLIENT_PORT}" \
  -Jserver.rmi.ssl.disable=false \
  -Jserver.rmi.ssl.keystore.file=/rmi_keystore.jks \
  -Jserver.rmi.ssl.keystore.password='changeit' \
  -Jserver.rmi.ssl.truststore.file=/rmi_keystore.jks \
  -Jserver.rmi.ssl.truststore.password='changeit' \
  -R "${WORKER1_IP},${WORKER2_IP}"

Keep the firewall rules even with SSL on. To rotate the keystore, regenerate it and redistribute to every host at once.

About

Docker images for JMeter 3.2

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages