Kubernetes Operator

The Locust Operator for Kubernetes is an operator that manages the lifecyle of Distributed load generation inside a Kubernetes cluster.

It is a Custom Resource Definition (CRD) and a Controller that run on your Kubernetes cluster and allow you to create and manage your Locust tests as Kubernetes resources. Automatically creates master/worker jobs, mounts locustfiles, exposes the web UI, collects metrics, and handles restarts when the spec changes.

Installation

Helm Charts

Helm is a package manager for Kubernetes that installs and manages Kubernetes applications.

  1. Add the Helm repository

$ helm repo add locust-operator https://locustio.github.io/k8s-operator
"locust-operator" has been added to your repositories

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "locust-operator" chart repository
Update Complete. ⎈Happy Helming!⎈
  1. Install Locust Operator for Kubernetes

$ helm install locust-operator locust-operator/locust-operator \
  --namespace locust-operator --create-namespace
  1. Check that the operator is running and the CRD is installed

$ kubectl get pods -A -l app.kubernetes.io/name=locust-operator
NAMESPACE         NAME                              READY   STATUS         RESTARTS   AGE
locust-operator   locust-operator-xxxxxxxxx-xxxxx   1/1     Running        0          18s

$ kubectl get crd                                                                                                                                                                        [none 🚀]
NAME                       CREATED AT
locusttests.locust.io      ...

Manifest Files

Locust Operator for Kubernetes can be installed using raw manifest files with kubectl. To generate the raw resources, you can use the Helm chart and output the manifests without installing them.

$ helm repo add locust-operator https://locustio.github.io/k8s-operator
$ helm repo update
$ helm template locust-operator locust-operator/locust-operator \
  --namespace locust-operator > locust-operator.yaml

Then apply the generated manifest file:

$ kubectl apply -f locust-operator.yaml

Quickstart

  1. Create a YAML file defining a LocustTest resource.

apiVersion: locust.io/v1
kind: LocustTest
metadata:
  name: load-test
spec:
  workers: 2
  locustfile:
    content: |
      from locust import HttpUser, task
      class TestUser(HttpUser):
          @task
          def index(self):
              self.client.get("/")
  1. Apply it using kubectl apply -f <file>.yaml.

  2. You can verify the created resource using kubectl get locusttests and kubectl get pods to see the master and worker pods.

$ kubectl apply -f locust-test.yaml
locusttest.locust.io/load-test created

$ kubectl get locusttests
NAME                 STATE   WORKERS   FAIL_RATIO   RPS   USERS   AGE
load-test            READY   2/2       0%           0     0       30s

$ kubectl get pods -l locust.io/test-run=load-test
NAME                       READY   STATUS    RESTARTS   AGE
load-test-master-xxxxx     1/1     Running   0          35s
load-test-worker-xxxxx     1/1     Running   0          35s
load-test-worker-xxxxx     1/1     Running   0          35s
  1. Access the Locust web UI by port-forwarding the service to your local machine. Then open your browser and navigate to http://localhost:8089.

$ kubectl port-forward svc/load-test-webui 8089:8089
Forwarding from 127.0.0.1:8089 -> 8089
Forwarding from [::1]:8089 -> 8089
  1. You can see the master logs by running:

# Tailing the master pod logs directly
$ kubectl logs -f pod/load-test-master-xxxxx

# Using a selector to follow the master pod by labels
$ kubectl logs -f -l locust.io/test-run=load-test,locust.io/component=master
  1. Cleanup by deleting the LocustTest resource (this will also delete all managed resources):

$ kubectl delete loadtest load-test

LocustTest CRD Configuration

General

spec.image (string, required, default: locustio/locust:latest)

Container image for master and workers pods.

spec.workers (integer, required, default: 1)

Number of worker pods to run.

spec.args (string, optional)

Additional CLI flags, e.g. --run-time=5m --users=200 --spawn-rate=20.

spec.env (array, optional)

List of environment variables to set in the container.

Locustfile source

spec.locustfile (object, optional; choose one)

  • content (string): inline locustfile.py

  • configMap: reference an existing ConfigMap

    • name (string, required)

    • key (string, default: locustfile.py)

  • Built into image

    The image contains a locustfile. If the filename isn’t locustfile.py (default locustfile name), pass -f <custom/path/locustfile> via spec.args.

Metadata

spec.labels / spec.annotations (object, optional)

User-provided labels/annotations merged onto all managed resources.

Per-role overrides

This allows customizing master and worker pods separately.

spec.master / spec.worker (object, optional)

  • labels / annotations (object, optional)

  • resources (object, optional).

Example:
master:
  labels:
    my.custom.label/is-locust-master: "true"
  resources:
    requests:
      cpu: "500m"
      memory: "256Mi"
    limits:
      cpu: "1"
      memory: "512Mi"

Extended

spec.imagePullPolicy (string, optional)

The image pull policy for master/worker pods.

spec.imagePullSecrets (array, optional)

The image pull secrets for master/worker pods. e.g. [{ name: my-regcred }] for private registries.

Examples

Inline locustfile

apiVersion: locust.io/v1
kind: LocustTest
metadata:
  name: load-test-v1
spec:
  image: locustio/locust:latest
  workers: 5
  args:
    --host http://my.site.com/api/v1
    --run-time=10m
    --users=500
    --spawn-rate=50
  env:
  - name: LOCUST_LOGLEVEL
    value: INFO
  locustfile:
    content: |
      from locust import HttpUser, task
      class TestUser(HttpUser):
          @task
          def index(self):
              self.client.get("/")

External ConfigMap locustfile

apiVersion: v1
kind: ConfigMap
metadata:
  name: v2-locustfile
data:
  mytest.py: |
    from locust import HttpUser, task
    class PingUser(HttpUser):
        @task
        def ping(self):
            self.client.get("/ping")
---
apiVersion: locust.io/v1
kind: LocustTest
metadata:
  name: load-test-v2
spec:
  workers: 5
  args:
    -f mytest.py
    --host http://my.site.com/api/v2
    --run-time=10m
    --users=500
    --spawn-rate=50
  locustfile:
    configMap:
      name: v2-locustfile

Custom Master/Worker pod configuration

apiVersion: locust.io/v1
kind: LocustTest
metadata:
  name: locust-test
spec:
  labels:
    # Merged into all managed resources labels
    my.custom.label/group: group1
  image: my-private-registry/custom-image:1.0.0
  imagePullSecrets:
    - name: regcred
  workers: 5
  master:
    annotations:
      # Merged into master pod annotations
      my.custom.annotations/version: "1.0.0"
    labels:
      # Merged into master pod labels
      my.custom.label/is-locust-master: "true"
    resources:
      requests:
        cpu: "500m"
        memory: "512Mi"
      limits:
        cpu: "1"
        memory: "1Gi"
  worker:
    annotations:
      # Merged into worker pod annotations
      my.custom.annotations/version: "1.0.0"
    labels:
      # Merged into worker pod labels
      my.custom.label/is-locust-master: "false"
    resources:
      requests:
        cpu: "500m"
        memory: "512Mi"
      limits:
        cpu: "1"
        memory: "1Gi"

Headless run

apiVersion: locust.io/v1
kind: LocustTest
metadata:
  name: headless-test
spec:
  workers: 2
  args:
    --host http://locust.io/
    --headless
    --run-time=5m
    --users=300
    --spawn-rate=30
  locustfile:
    content: |
      from locust import HttpUser, task
      class TestUser(HttpUser):
          @task
          def index(self):
              self.client.get("/")

Upgrade

Helm

$ helm repo update
$ helm upgrade locust-operator locust-operator/locust-operator \
  --namespace locust-operator \
  --reuse-values

Helm does not support updating or deleting CRDs. You may need to update the CRD manually when upgrading the operator.

$ kubectl apply -f https://raw.githubusercontent.com/locustio/k8s-operator/refs/tags/helm-chart-<version>/charts/locust-operator/crds/locusttest.yaml

Uninstall

  1. Delete all LocustTest resources (optional but recommended)

$ kubectl get locusttests --all-namespaces
$ kubectl delete locusttests --all --all-namespaces

Helm

# Uninstall the Helm release
$ helm uninstall locust-operator --namespace locust-operator
# Remove the LocustTest CRD
$ kubectl delete crd locusttests.locust.io

Manifest Files

$ kubectl delete -f locust-operator.yaml