k6-operator MaxVUs Parallelism Error
Problem#
A 4000 VU load test failed immediately upon execution:
Status: error (Pod: Succeeded)
Summary not available (test may have ended too quickly)
k6-operator logs:
k6 inspect: {MaxVUs:1 ...}
ERROR: Parallelism argument cannot be larger than maximum VUs in the script
{"maxVUs": 1, "parallelism": 2, "error": "number of instances > number of VUs"}
Root Cause Analysis#
k6-operator execution flow#
1. TestRun CR created
↓
2. Initializer Pod runs
- k6 inspect: analyzes script (determines maxVUs) ← no env vars here!
- k6 archive: compresses script
↓
3. Runner Pod created (VUS, DURATION env vars injected)
↓
4. Test runs
Problematic code#
// k6 script generated by scripts.go
const _vus = __ENV.K6_VUS ? parseInt(__ENV.K6_VUS) : (parseInt(__ENV.VUS) || 1);
// ↑
// default value 1!
| Stage | Env vars | _vus value |
|---|---|---|
| k6 inspect (Initializer) | none | 1 (default) |
| k6 run (Runner) | VUS=4000 | 4000 |
k6 inspect analyzes export const options in the script to determine maxVUs.
Without env vars, the default value of 1 is used, so maxVUs is reported as 1.
Why are env vars missing?#
This is intentional by design:
The k6-operator analyzes the script to determine resource requirements before creating the Runner Pod. The Initializer Pod is analysis-only, so VUS env vars are not injected into it.
Fix#
Change#
In k6-controller/services/scripts.go, set the default value large enough to never be a limiting factor:
// Before
const _vus = __ENV.K6_VUS ? parseInt(__ENV.K6_VUS) : (parseInt(__ENV.VUS) || 1);
// After
const _vus = __ENV.K6_VUS ? parseInt(__ENV.K6_VUS) : (parseInt(__ENV.VUS) || 50000);
How to apply#
cd /path/to/k6-controller
docker build -t ghcr.io/goorm-gongbang/k6-controller:latest .
# 2. Push
docker push ghcr.io/goorm-gongbang/k6-controller:latest
# 3. Restart
kubectl rollout restart deployment/k6-controller -n k6-system
Related Issue#
ConfigMap 1MB limit (15000+ VUs)#
When using large numbers of tokens (15000+), you can hit the ConfigMap 1MB size limit.
Fix: Split tokens into multiple ConfigMaps of 1000 each
createTokenConfigMaps(): splits tokens into chunksBuildScriptWithMultipleTokenFiles(): loads tokens from multiple files
// Load tokens from multiple files
const AUTH_TOKENS = new SharedArray('tokens', function() {
let allTokens = [];
try { allTokens = allTokens.concat(JSON.parse(open('/data/tokens-0/tokens.json'))); } catch(e) {}
try { allTokens = allTokens.concat(JSON.parse(open('/data/tokens-1/tokens.json'))); } catch(e) {}
// ...
return allTokens;
});
References#
- k6-operator GitHub: https://github.com/grafana/k6-operator
- k6 inspect docs: https://k6.io/docs/misc/k6-inspect/