Skip to content
LoadStrike
Portal

Search LoadStrike

GOOD PLACES TO START

ClusterCode 61 / 69

Docker Images And EKS Deployment

Package a runnable harness and use finite coordinator and agent Jobs with explicit identity, transport, licensing and artifact retention.

What this page helps you do

What this page helps you do

Package a runnable harness and use finite coordinator and agent Jobs with explicit identity, transport, licensing and artifact retention.

Who this is for

Teams moving from one machine to coordinator-and-agent execution or tighter workload targeting.

Prerequisites

  • A scenario that already works in a single-node run

By the end

A clearer cluster topology and the fields that must line up across nodes.

Use this page when

Use this page when execution topology, partitioning, or targeting changes how the run should be distributed.

Visual guide

Cluster deployment flow showing image build, registry, EKS pods, NATS, and merged report.
The same image can run as coordinator or agent, with EKS and NATS providing the deployment and coordination path.

Guide

Architecture

Use one coordinator Job and a finite Indexed Agent Job. Each standalone Agent waits for one signed run, executes its assignment and exits. A continuously restarted Deployment can leave idle replacement Agents after completion.

Runnable Images

Package your executable harness and its normal production SDK/runtime dependencies. The C# example uses a .NET 8 console entry point; other SDKs need their own entry points. A unit test assembly is not an application. Use the same verified immutable build on all nodes; keyless Go Agents need the compatible signed runtime preinstalled.

Explicit Configuration

Your application must read the deployment variables and pass them to the runner APIs. The sample names are not automatically bound by the SDK. Match SessionId, ClusterId, AgentGroup and scenario definitions; remote V2 requires unique stable AgentId values and the exact ExpectedAgentIds roster.

One Run Per Job

Set Indexed Job completions and parallelism to the expected Agent count. Ensure every Agent listener is ready before starting the coordinator. Wait for coordinator and agent Job Complete, inspect exit codes and diagnostics, and use new Job names and a new SessionId for each run.

NATS And Security

Use a reachable authenticated NATS broker and configure certificate trust for TLS in each language runtime. Keep broker credentials in a Secret and the entitled runner key in a separate coordinator-only Secret. Restrict namespace access and allow only required broker, licensing, sink and target network paths. Agents validate coordinator-issued authorization; do not give them the runner key.

EKS Access And Storage

Configure node or Fargate permissions to pull your immutable ECR image. AWS API calls from targets or sinks need supported AWS workload credentials, using EKS Pod Identity or IRSA as appropriate; image pull and pod API permissions are separate. Provision a suitable CSI storage class before creating the coordinator report claim.

Reports And Sinks

The coordinator writes the combined HTML, TXT, CSV and Markdown report set. Configured remote-agent sinks receive agent data; coordinator sinks receive the merged result. Final SinkErrors and DisabledSinks include child and coordinator failures. ReportingComplete describes raw observation delivery, not success of every independent final sink callback.

Artifact Retention

Retain coordinator artifacts on its PVC or export them before cleanup. Retrieve a completed Job report through a separate retention Pod or exporter. Use distinct node paths for agent JSONL output, or remote sinks; an RWO coordinator claim is not shared multi-node agent storage.

Reporting Limits

Distributed results retain aggregate and node statistics; temporal history can be unavailable and is disclosed in reports. Do not infer a merged latency time series from final counters. Size in-flight capacity and CPU/memory for offered load, and inspect explicit generator drops separately from application failures.

Validation Scope

Docker and local Kubernetes process tests verify SDK execution, Job completion and artifact delivery for the tested builds. They do not verify your EKS IAM, CNI policies, registry access, multi-node storage, broker TLS trust or external vendor credentials; validate those in the target cluster before a production load run.

Cluster setup samples

The executable harness explicitly maps environment inputs to runner APIs. Give every new run a fresh SESSION_ID and use that same value on the coordinator and all agents. The shared Jobs use stable indexed Agent identities, coordinator-only licensing and retained report storage.

If you run these examples locally, add a valid runner key before execution starts. Set it with WithRunnerKey("...") or the config key LoadStrike:RunnerKey.

Build, Run, and Retain Artifacts

using LoadStrike;

public static class LoadStrikeHarness
{
    public static int Main()
    {
        string Required(string name) => Environment.GetEnvironmentVariable(name)
            ?? throw new InvalidOperationException($"Missing {name}");
        var role = Enum.Parse<LoadStrikeNodeType>(Required("NODE_ROLE"));
        if (role is not (LoadStrikeNodeType.Agent or LoadStrikeNodeType.Coordinator))
            throw new InvalidOperationException("NODE_ROLE must be Agent or Coordinator");
        var scenario = LoadStrikeScenario.Create("checkout", _ =>
            Task.FromResult(LoadStrikeResponse.Ok(statusCode: "200")))
            .WithoutWarmUp()
            .WithLoadSimulations(LoadStrikeSimulation.IterationsForInject(10, TimeSpan.FromSeconds(1), 100));
        var runner = LoadStrikeRunner.RegisterScenarios(scenario)
            .UseLoadEngineV2().WithMaxInFlight(1000)
            .WithSessionId(Required("SESSION_ID")).WithClusterId(Required("CLUSTER_ID"))
            .WithAgentGroup(Required("AGENT_GROUP")).WithNatsServerUrl(Required("NATS_URL"))
            .WithNodeType(role).WithClusterCommandTimeout(TimeSpan.FromSeconds(180))
            .WithReportFolder(Environment.GetEnvironmentVariable("REPORT_DIR") ?? "/reports")
            .WithReportFormats(LoadStrikeReportFormat.Html, LoadStrikeReportFormat.Txt,
                LoadStrikeReportFormat.Csv, LoadStrikeReportFormat.Md);
        if (role == LoadStrikeNodeType.Agent)
            runner = runner.WithAgentId("agent-" + Required("JOB_COMPLETION_INDEX"));
        else
            runner = runner.EnableLocalDevCluster(false).WithAgentsCount(2)
                .WithExpectedAgentIds("agent-0", "agent-1").WithRunnerKey(Required("LOADSTRIKE_RUNNER_KEY"));
        var result = runner.Run();
        Console.WriteLine($"Requests={result.AllRequestCount}; sink errors={result.SinkErrors.Count}");
        return result.AllFailCount == 0 && result.SinkErrors.Count == 0 ? 0 : 1;
    }
}

One-Run Kubernetes Jobs (Map Inputs in Your Application)

ParameterDescription
Indexed agent Job

Starts two finite Agent processes with stable agent-0 and agent-1 identities. Each exits after its signed assignment; both must reach Job Complete.

Coordinator Job

Carries the runner key through SecretKeyRef, assigns work over reachable NATS and writes merged reports. Agent pods have no runner key.

Application inputs

The sample Program.cs explicitly reads these variables. Other SDK harnesses must map equivalent role, session, roster and transport inputs to their own runner APIs.

Retained reports

Only the coordinator mounts the RWO report claim. Use a retention Pod or artifact exporter after Job completion; use separate paths or remote destinations for agent sinks.

# Create the namespace and Secrets securely before applying the Jobs.
# loadstrike-coordinator-key: key = existing entitled runner key (coordinator only).
# loadstrike-nats: url = reachable authenticated NATS URL; configure TLS trust for each runtime.
# Replace the image with your immutable tested harness image. Change SESSION_ID for every run.
apiVersion: v1
kind: ConfigMap
metadata:
  name: loadstrike-runtime
  namespace: loadstrike
data:
  SESSION_ID: orders-run-20261004-001
  CLUSTER_ID: orders-cluster
  AGENT_GROUP: perf-agents
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: loadstrike-reports
  namespace: loadstrike
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 2Gi
# Uses the cluster default storage class. On EKS configure a suitable CSI driver/class first.
# This claim is mounted only by the coordinator, not by agents on different worker nodes.
---
apiVersion: batch/v1
kind: Job
metadata:
  name: loadstrike-agents
  namespace: loadstrike
spec:
  completionMode: Indexed
  completions: 2
  parallelism: 2
  backoffLimit: 0
  activeDeadlineSeconds: 600
  template:
    spec:
      restartPolicy: Never
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
      containers:
      - name: agent
        image: <aws-account-id>.dkr.ecr.<region>.amazonaws.com/loadstrike-harness:<immutable-tag>
        envFrom:
        - configMapRef:
            name: loadstrike-runtime
        env:
        - name: NODE_ROLE
          value: Agent
        - name: JOB_COMPLETION_INDEX
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
        - name: NATS_URL
          valueFrom:
            secretKeyRef:
              name: loadstrike-nats
              key: url
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: [ALL]
        resources:
          requests: {cpu: "500m", memory: "512Mi"}
          limits: {cpu: "2", memory: "2Gi"}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: loadstrike-coordinator
  namespace: loadstrike
spec:
  backoffLimit: 0
  activeDeadlineSeconds: 600
  template:
    spec:
      restartPolicy: Never
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
      containers:
      - name: coordinator
        image: <aws-account-id>.dkr.ecr.<region>.amazonaws.com/loadstrike-harness:<immutable-tag>
        envFrom:
        - configMapRef:
            name: loadstrike-runtime
        env:
        - name: NODE_ROLE
          value: Coordinator
        - name: NATS_URL
          valueFrom:
            secretKeyRef:
              name: loadstrike-nats
              key: url
        - name: LOADSTRIKE_RUNNER_KEY
          valueFrom:
            secretKeyRef:
              name: loadstrike-coordinator-key
              key: key
        - name: REPORT_DIR
          value: /reports
        volumeMounts:
        - name: reports
          mountPath: /reports
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: [ALL]
        resources:
          requests: {cpu: "500m", memory: "512Mi"}
          limits: {cpu: "2", memory: "2Gi"}
      volumes:
      - name: reports
        persistentVolumeClaim:
          claimName: loadstrike-reports

Build And Publish Your Harness

Dockerfile And Build Commands

This .NET image example packages the runnable harness shown above; use the matching runtime and entry point for another SDK language.

# Create a console application LoadStrikeHarness.csproj (OutputType=Exe, net8.0).
# Pin the verified SDK release or use the updated SDK source; fixes are not yet released.
# Program.cs is the executable example on EKS Coordinator And Agents (all language tabs).
# Dockerfile: the build context contains the harness and its SDK reference.
FROM mcr.microsoft.com/dotnet/sdk:8.0.424 AS build
WORKDIR /src
COPY . .
RUN dotnet publish LoadStrikeHarness.csproj -c Release -o /out
FROM mcr.microsoft.com/dotnet/runtime:8.0.30
WORKDIR /app
COPY --from=build /out .
USER 10001:10001
ENTRYPOINT ["dotnet", "LoadStrikeHarness.dll"]

# PowerShell: build/push your runnable harness, not the SDK unit test project.
# Use an existing ECR repository and a commit/release tag; all nodes use the identical image.
$registry = "$env:AWS_ACCOUNT_ID.dkr.ecr.$env:AWS_REGION.amazonaws.com"
$image = "${registry}/loadstrike-harness:$env:IMAGE_TAG"
aws ecr get-login-password --region $env:AWS_REGION |
    docker login --username AWS --password-stdin $registry
docker build -t $image .
docker push $image

# Create the loadstrike namespace and the coordinator/NATS Secrets via your secret manager.
# Apply the shared manifest below; wait for both agent listeners before starting the coordinator.
# For V2 the same SESSION_ID, CLUSTER_ID, AGENT_GROUP, definitions and exact stable roster
# must agree. Each Job is a single run; use new names/session IDs for a subsequent run.
kubectl -n loadstrike wait --for=condition=Complete job/loadstrike-coordinator --timeout=600s
kubectl -n loadstrike wait --for=condition=Complete job/loadstrike-agents --timeout=600s
# After completion, retrieve reports through a separate retention Pod mounting the coordinator
# PVC, or your artifact export mechanism. Completed Job containers cannot service kubectl cp.
# Keep the reports before deleting Jobs or claims. Remote agents can write node-specific
# JSONL artifacts or configured remote sinks; never share one JSONL file across worker pods.