Lasttests in GitHub Actions: ein praktischer Leitfaden
6 Min. Lesezeit
Für Lasttests in GitHub Actions genügt eine Workflow-Datei: Tool installieren, einen kurzen Test gegen eine Staging-URL starten und den Job über den Exit-Code des Tools scheitern lassen, wenn ein Schwellenwert verfehlt wird. Laden Sie die Ergebnisse als Artefakt hoch, damit jeder Lauf eine Spur hinterlässt. Halten Sie den Test klein, denn ein Standard-Runner von GitHub ist eine einzelne virtuelle Maschine mit wenigen CPU-Kernen.
Die Angaben zu den Tools und zu GitHub stammen aus der Dokumentation der jeweiligen Anbieter, geprüft am 9. Oktober 2026, und die Workflows folgen dieser dokumentierten Verwendung.
| Tool | Installiert über | Was den Job scheitern lässt | Ergebnisse zum Aufbewahren |
|---|---|---|---|
| k6 | Grafanas setup-k6-action | Ein verfehlter Schwellenwert | JSON-Ausgabe |
| Artillery | artilleryio/action-cli | Eine fehlgeschlagene ensure-Prüfung | report.json |
| Locust | pip install locust | Jede fehlgeschlagene Anfrage oder eine eigene Regel | CSV- und HTML-Dateien |
| JMeter | Das Release-Archiv | Nichts eingebaut: einen Prüfschritt ergänzen | .jtl-Datei und HTML-Bericht |
Was braucht ein Lasttest-Workflow?
Einen Trigger, einen Runner, einen Schritt, der das Tool installiert, einen Schritt, der den Test mit Schwellenwerten ausführt, und einen Schritt, der die Ergebnisse hochlädt. Alles andere ist optional.
Legen Sie das Testskript ins Repository, etwa unter tests/load/. Lesen Sie die Staging-URL aus einer Repository-Variablen über den vars-Kontext und setzen Sie timeout-minutes für den Job: Der Standardwert liegt bei 360 Minuten (geprüft am 9. Oktober 2026).
Welche Ereignisse sollten einen Lasttest starten?
Lassen Sie bei Pull Requests einen kurzen Test laufen und einen längeren nach Zeitplan oder von Hand.
Nutzen Sie pull_request, um eine Regression vor dem Merge zu erkennen, workflow_dispatch für manuelle Läufe und schedule für Cron-Läufe (in UTC, sofern Sie keine Zeitzone angeben). GitHubs Referenz der Ereignisse nennt einige Grenzen (geprüft am 9. Oktober 2026): Zeitpläne laufen nur auf dem Standard-Branch, höchstens alle fünf Minuten, und können sich zur vollen Stunde verzögern. Pull Requests aus Forks erhalten außer GITHUB_TOKEN keine Secrets.
Wie führt man k6 in GitHub Actions aus?
Nutzen Sie die zwei offiziellen Actions von Grafana, eine zum Installieren von k6 und eine zum Ausführen des Skripts. Der Schritt schlägt fehl, sobald k6 mit einem Exit-Code ungleich null endet, und genau das löst ein verfehlter Schwellenwert aus.
Sowohl setup-k6-action als auch run-k6-action stehen auf v1 (geprüft am 9. Oktober 2026):
name: load-test
on:
pull_request:
workflow_dispatch:
jobs:
k6:
runs-on: ubuntu-latest
timeout-minutes: 15
env:
BASE_URL: ${{ vars.STAGING_URL }}
steps:
- uses: actions/checkout@v7
- uses: grafana/setup-k6-action@v1
- uses: grafana/run-k6-action@v1
with:
path: tests/load/smoke.js
flags: --out json=results.json
- uses: actions/upload-artifact@v7
if: always()
with:
name: k6-results
path: results.json
retention-days: 14
Die Schwellenwerte stehen im Skript, das BASE_URL aus der Umgebung liest:
import http from "k6/http";
export const options = {
vus: 10,
duration: "1m",
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<500"],
},
};
export default function () {
http.get(`${__ENV.BASE_URL}/health`);
}
Laut k6-Dokumentation beeinflussen Checks den Exit-Status nicht, daher muss die Entscheidung über Bestehen oder Scheitern von den Schwellenwerten kommen.
Wie führt man stattdessen Artillery, Locust oder JMeter aus?
Tauschen Sie die Installations- und Ausführungsschritte aus, behalten Sie den Rest des Workflows und nutzen Sie den Mechanismus des jeweiligen Tools, um bei einem verfehlten Ziel zu scheitern.
Artillery hat eine offizielle GitHub Action, deren README festhält, dass @v2 dem neuesten 2.x-Release folgt (geprüft am 9. Oktober 2026). Der Schritt lautet uses: artilleryio/action-cli@v2 mit command: run --target ${{ vars.STAGING_URL }} tests/load/api.yml --output report.json, und das ensure-Plugin beendet Artillery mit einem Exit-Code ungleich null, wenn ein Ziel verfehlt wird. Der Abschnitt config des Skripts:
config:
target: "https://staging.example.com"
phases:
- duration: 60
arrivalRate: 5
plugins:
ensure:
thresholds:
- "http.response_time.p95": 500
Locust endet mit Exit-Code 1, sobald eine Anfrage fehlgeschlagen ist, und für Latenzregeln setzen Sie environment.process_exit_code in einem Listener für das quitting-Ereignis, wie dieselbe Seite zeigt (geprüft am 9. Oktober 2026). Nach actions/setup-python@v7 mit python-version: "3.13":
- run: pip install locust
- run: >
locust -f tests/load/locustfile.py --headless
-u 20 -r 5 -t 2m -H "$BASE_URL"
--csv results --html report.html
JMeter läuft mit -n ohne Oberfläche, und die Kommandozeilenoptionen im Benutzerhandbuch enthalten keinen Pass/Fail-Schwellenwert (geprüft am 9. Oktober 2026). Ein kurzer Schritt kann die Ergebnisdatei lesen, standardmäßig CSV mit einer Spalte success, und den Job selbst scheitern lassen:
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "21"
- name: Install JMeter 5.6.3
env:
JM_BASE: https://archive.apache.org/dist/jmeter/binaries
run: curl -sSL "$JM_BASE/apache-jmeter-5.6.3.tgz" | tar -xzf -
- name: Run the test plan
run: >
apache-jmeter-5.6.3/bin/jmeter -n -t tests/load/plan.jmx
-l results.jtl -e -o report
- name: Fail above 1% errors
run: |
python3 - <<'EOF'
import csv, sys
rows = list(csv.DictReader(open("results.jtl")))
bad = sum(r["success"] != "true" for r in rows)
print(f"{bad} of {len(rows)} samples failed")
sys.exit(1 if not rows or bad > 0.01 * len(rows) else 0)
EOF
Wie lässt man den Job scheitern, wenn der Test ein Ziel verfehlt?
Schreiben Sie die Ziele als Schwellenwerte in den Test und überlassen Sie den Rest dem Exit-Code. GitHub markiert einen Schritt als fehlgeschlagen, wenn sein Befehl mit einem Exit-Code ungleich null endet, und der Job scheitert mit ihm.
Beginnen Sie mit einer strengen Fehlerquote und einem großzügigen Latenz-Perzentil, und verschärfen Sie das Latenzlimit, sobald Sie wissen, wie stark Ihre Zahlen von Lauf zu Lauf schwanken. Wie Sie diese Ziele festlegen, zeigt die Schritt-für-Schritt-Anleitung zum Lasttest.
Wohin gehören die Ergebnisse jedes Laufs?
Laden Sie sie als Workflow-Artefakt hoch, mit if: always(), damit auch ein fehlgeschlagener Test seinen Bericht behält. Artefakte werden standardmäßig 90 Tage aufbewahrt, und retention-days verkürzt das.
Das README von upload-artifact dokumentiert die 90 Tage als Standard (geprüft am 9. Oktober 2026). Setzen Sie if-no-files-found: error, damit der Schritt bei einem fehlenden Bericht sichtbar fehlschlägt.
Wie viel Last kann ein von GitHub gehosteter Runner erzeugen?
Genug für einen Smoke-Test oder eine kleine Regressionsprüfung. Ein Standard-Linux-Runner hat in öffentlichen Repositories 4 CPUs und 16 GB RAM, in privaten 2 CPUs und 8 GB, und ein Job endet nach 6 Stunden.
Diese Zahlen stammen aus GitHubs Runner-Referenz und den Actions-Limits (geprüft am 9. Oktober 2026), die keine Angabe zur Netzwerkbandbreite machen.
Ein Job läuft auf einer Maschine und ist damit ein einziger Lastgenerator: Ist seine CPU ausgelastet, enthalten die Antwortzeiten auch die Wartezeit auf dem Runner. Jede Anfrage verlässt zudem dieselbe Maschine, sodass Rate Limiter, die pro Client-Adresse zählen, einen einzigen sehr aktiven Client sehen. GitHub rät davon ab, die Azure-IP-Bereiche seiner Ubuntu-Runner in Allowlists aufzunehmen; größere Runner in den Plänen Team und Enterprise Cloud bieten mehr CPU, mehr Speicher und statische IP-Adressen.
Wann sollte die Last von woanders kommen?
Wenn Sie mehr Last brauchen, als eine Maschine erzeugt, Traffic aus mehreren Regionen oder Läufe, die länger dauern als das Job-Limit.
Bei k6 führt run-k6-action den Test in Grafana Cloud k6 aus, wenn Sie cloud-run-locally: false setzen und K6_CLOUD_TOKEN, K6_CLOUD_PROJECT_ID sowie mit k6 v2 K6_CLOUD_STACK_ID übergeben. Ein Cloud-Lauf ignoriert standardmäßig die Umgebung des Runners, ergänzen Sie flags also um -e BASE_URL=${{ vars.STAGING_URL }} (geprüft am 10. Oktober 2026). Die Action von Artillery kann run-fargate oder run-lambda aufrufen und die Last aus Ihrem eigenen AWS-Konto erzeugen (beides geprüft am 9. Oktober 2026). Der Vergleich k6 vs Artillery behandelt beide.
Für JMeter dokumentiert LoadFocus eine Integration mit GitHub Actions auf Basis seines npm-Clients. Sie speichern den Test in Ihrem LoadFocus-Konto, hinterlegen API-Schlüssel und Team-ID als Secrets und rufen run-test auf. Die Flags unten folgen dem Hilfetext des Clients:
- run: npm install -g @loadfocus/loadfocus-api-client
- env:
LF_KEY: ${{ secrets.LOADFOCUS_API_KEY }}
LF_TEAM: ${{ secrets.LOADFOCUS_TEAM_ID }}
run: |
test -n "$LF_KEY"
test -n "$LF_TEAM"
loadfocus-api config set \
--apikey "$LF_KEY" --teamid "$LF_TEAM"
loadfocus-api jmeter run-test --name checkout-api \
--thresholds "avgresponse<=200,errors==0,p95<=250"
Der Client wartet standardmäßig bis zu 1.800 Sekunden (--wait-timeout ändert das), setzen Sie timeout-minutes für den Job also auf mehr als 30, etwa 40. Er endet mit Exit-Code 1, wenn ein Schwellenwert verfehlt wird, und mit 0, wenn API-Schlüssel oder Team-ID fehlen, deshalb lassen die test -n-Zeilen den Schritt vorher scheitern, sobald ein Secret leer ist. Die Last kommt von LoadFocus, das JMeter-Skripte aus 25 Cloud-Regionen ausführt.
FAQ
Wie lange sollte ein Lasttest in einem Pull Request laufen? Ein paar Minuten: lang genug, um den Ramp-up hinter sich zu lassen und ein stabiles Perzentil zu erhalten, und kurz genug, dass niemand darauf warten muss.
Darf man Lasttests von GitHub-gehosteten Runnern aus fahren? GitHubs Bedingungen für Actions verbieten, damit Dienste zu stören, und beschränken gehostete Runner auf Arbeit am eigenen Projekt des Repositorys (geprüft am 9. Oktober 2026). Ein Lasttest gegen die eigene Staging-Umgebung dieses Projekts, der GitHubs Server nicht übermäßig belastet, ist die Art von Test, die diese Bedingungen beschreiben.
Kann ich stattdessen einen selbst gehosteten Runner nutzen? Ja. Er läuft auf Ihrer eigenen Hardware und in Ihrem Netzwerk, und laut GitHubs Limits-Seite darf ein Job dort bis zu 5 Tage laufen. Er bleibt ein einziger Lastgenerator.
Weiterlesen
- Pass/Fail-Kriterien für Lasttests: so legen Sie sie fest
- Lasttest durchführen: eine Schritt-für-Schritt-Anleitung
- k6 vs Artillery: welches Lasttest-Tool passt zu Ihrem Team?
Die technischen Aussagen in diesem Artikel wurden nicht unabhängig geprüft.