aks-traffic-ingress-competitive-testing
CLAUDE.md0 starsChanged 6 months agoMITView on GitHub
This is a load-testing framework for comparing Kubernetes ingress controllers (nginx via Ingress, Istio via Gateway API). It orchestrates: spin up a Kind cluster → install a traffic controller → deploy a backend server via Helm → run vegeta load tests → collect JSON results → tear down. master.sh handles everything including cleanup (via EXIT trap). Requires kubectl, helm, jq, and curl on the host. scripts/master.sh is the single entry point that orchestrates these steps in order: Each module under…
We split this CLAUDE.md into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 128
All scripts expect to be run from the repository root.
- Very specific: line 129
All shell scripts use set -ex (or set -e for library-style scripts).
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What This Repo Does
This is a load-testing framework for comparing Kubernetes ingress controllers (nginx via Ingress, Istio via Gateway API). It orchestrates: spin up a Kind cluster → install a traffic controller → deploy a backend server via Helm → run vegeta load tests → collect JSON results → tear down.
Key Commands
Run the full test pipeline locally
# Ingress (nginx) + basic RPS
./scripts/master.sh --traffic ingress --scenario basic-rps --rate 50 --duration 30s --output-file ./results/basic_rps.json
# Gateway (istio) + basic RPS
./scripts/master.sh --traffic gateway --scenario basic-rps --rate 50 --duration 30s --output-file ./results/gateway_basic_rps.json
# Restarting backend scenario
./scripts/master.sh --traffic ingress --scenario restarting-backend-rps --rate 50 --duration 90s --output-file ./results/restarting_backend_rps.json
# With scheduling constraints (multi-node Kind cluster)
./scripts/master.sh --traffic ingress --scenario basic-rps --kind-topology scheduling-e2e --node-selector scheduling=enabled --tolerations-file ./charts/server/ci-scheduling-values.yaml --rate 50 --duration 30s --output-file ./results/scheduling_test.jsonmaster.sh handles everything including cleanup (via EXIT trap). Requires kubectl, helm, jq, and curl on the host.
Run individual module tests
./modules/kind/test/test.sh # Creates/destroys a real Kind cluster
./modules/vegeta/test/test.sh # Starts a Docker container, runs a short vegeta attack
./modules/jplot/test/test.sh # Installs jplot (requires Go)Build the server
cd server && go build -o serverValidate Helm chart templates
helm template server ./charts/server
helm template server ./charts/server --set ingress.enabled=true --set ingress.className=nginx
helm template server ./charts/server --set gateway.enabled=true --set gateway.className=istioSyntax-check shell scripts
bash -n scripts/master.sh
bash -n scripts/scenarios/basic_rps.sh
bash -n scripts/scenarios/restarting_backend_rps.shArchitecture
Pipeline flow (master.sh)
scripts/master.sh is the single entry point that orchestrates these steps in order:
- Kind cluster —
modules/kind/install/install.sh+modules/kind/run/run.shcreate a cluster withextraPortMappings(port 8080→80). The--kind-topologyflag controls the cluster shape:default— single control-plane node (current behavior)scheduling-e2e— control-plane + one worker node labeledscheduling=enabledand taintedscheduling=enabled:NoSchedule
- Traffic controller —
scripts/install/nginx.sh(Ingress) orscripts/install/istio.sh(Gateway API) - Server deployment —
scripts/setup/ingress.shorscripts/setup/gateway.shdeployscharts/servervia Helm with full readiness polling - URL + readiness — reads
host_portfrom Kind's statefile, constructs the URL, then curl-probes until HTTP 200 - Scenario —
scripts/scenarios/basic_rps.shorscripts/scenarios/restarting_backend_rps.shinstalls vegeta and runs load tests via the vegeta module
Module pattern
Each module under modules/ follows the same interface:
install/install.sh— installs the toolrun/run.sh— runs the tool (can be sourced as a library or executed directly)output/output.sh— reads from../statefile.jsonand emits resultsmerge/merge.sh— (vegeta only) merges multiple.binfiles into per-second JSON output using timestamp-based bucketingtest/test.sh— integration test
Modules communicate via statefile.json (one per module, gitignored in practice). The kind module writes {"cluster_name": "...", "host_port": "..."}. The vegeta module writes streaming jaggr JSON (one line per second of the test).
Docker entrypoint
The Dockerfile entrypoint routes commands to scripts via prefix matching:
master [args...]→scripts/master.shscenario/<name> [args...]→scripts/scenarios/<name>.shinstall/<name> [args...]→scripts/install/<name>.shsetup/<name> [args...]→scripts/setup/<name>.shmodule/<name>/<action> [args...]→modules/<name>/<action>/<action>.shmerge [args...]→modules/vegeta/merge/merge.shserver→ starts the Go HTTP server
Scenario interface
Scenarios accept CLI arguments (--ingress-url, --rate, --duration, --workers, --output-file, --request-headers). Environment variables with the same names (uppercased, underscored) are read as defaults for backward compatibility. Each scenario installs its own dependencies (vegeta).
Data flow for results
The vegeta run pipeline streams results in real time: vegeta attack | tee statefile.bin | vegeta encode | jaggr | tee statefile.json. This saves raw binary results to .bin alongside the per-second jaggr JSON. The scenario script then copies statefile.json content to the --output-file. The result file contains one JSON line per second of the test. The CI validation sums code.hist["200"] across all lines and fails only if the total is zero (i.e., no connection was ever successfully routed).
For multi-pod tests, modules/vegeta/merge/merge.sh combines multiple .bin files into unified per-second JSON. It uses actual request timestamps for bucketing (not wall-clock time), so it correctly interleaves results from pods that started at slightly different times. The first and last second-buckets may be partial.
Helm chart (charts/server)
Single chart with two mutually exclusive traffic modes controlled by values:
ingress.enabled=true+ingress.className=nginx— creates an Ingress resourcegateway.enabled=true+gateway.className=istio— creates Gateway + HTTPRoute resourcesnodeSelector: {}+tolerations: []— optional pod scheduling values rendered only when non-empty
scripts/setup/ingress.sh and scripts/setup/gateway.sh accept --node-selector <key=value> and --tolerations-file <path> to wire these values into the Helm release. master.sh also accepts these flags and passes them through to the appropriate setup script.
--tolerations-file must point to a Helm values fragment with a top-level tolerations: key, not a bare YAML list, because the chart reads .Values.tolerations.
The server image is ghcr.io/azure/aks-traffic-ingress-competitive-testing (a Go HTTP server returning "hello world!" on /).
CI (validate.yaml)
The validation workflow runs on PRs to main. The test-scenarios job uses a matrix over traffic: [ingress, gateway] × scenario: [basic-rps, restarting-backend-rps] × variant: [default] — each combination gets its own runner and Kind cluster. An additional scheduling-e2e variant tests pod placement with node selectors and tolerations on a multi-node Kind cluster. The test-merge job validates multi-pod merge by running 4 simultaneous vegeta attacks against a Docker server and verifying the merged output (RPS totals, code histograms, JSON structure). The test-dns-resources job runs a matrix over resource: [ingresses, httproutes], deploys the server chart to namespace server, and exercises the setup/cleanup DNS scripts with positive (count=5, asserting created/deleted via kubectl get -n server) and negative paths (missing service, invalid domain, count=0). Because the chart deploys into namespace server, all DNS script invocations must pass --namespace server. Other jobs: module tests (matrix over discovered modules), chart validation (including scheduling render checks), and project structure validation.
DNS test scripts
scripts/setup/dns-ingresses.sh, scripts/setup/dns-httproutes.sh, scripts/cleanup/dns-ingresses.sh, and scripts/cleanup/dns-httproutes.sh bulk-create or delete N Ingress / HTTPRoute resources (each with a unique test-{i}.{domain} hostname, all labeled dns-test=true) for external-DNS reconciliation testing in downstream telescope pipelines (app-routing-nginx, app-routing-istio). They are intentionally not wired into master.sh — that script remains RPS-focused. The httproutes setup script requires an existing parent Gateway; the ingresses setup script requires an existing backend Service. Defaults: namespace default, service/gateway server, port 8080. Setup scripts accept --existing-n <N> to offset the index range (objects are numbered (existing-n+1)..(existing-n+count)) so additional batches can be appended without colliding with existing hostnames. Setup scripts stream the generated multi-document YAML to a unique mktemp file under $TMPDIR (path printed at the top of the run) and apply it with a single kubectl apply --server-side -f. Cleanup uses label selector dns-test=true with --wait=false --ignore-not-found and leaves the parent Gateway intact.
Conventions
- All scripts expect to be run from the repository root.
- All shell scripts use
set -ex(orset -efor library-style scripts). Scripts with data-processing pipelines also useset -o pipefailso that failures in any pipeline stage propagate correctly. - The
--trafficflag acceptsingressorgateway. The--scenarioflag acceptsbasic-rpsorrestarting-backend-rps. - Releases are driven by CHANGELOG.md — the Release workflow reads it to create GitHub releases. Follow Keep a Changelog format.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

