Skip to content

Commit 518fd02

Browse files
committed
Adding http_load scenario
1 parent 55f0a1b commit 518fd02

File tree

9 files changed

+248
-2
lines changed

9 files changed

+248
-2
lines changed

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SCENARIOS=( application-outages container-scenarios network-chaos node-cpu-hog node-io-hog \
22
node-memory-hog node-scenarios node-scenarios-bm pod-network-chaos pod-scenarios power-outages pvc-scenario \
3-
service-disruption-scenarios service-hijacking syn-flood time-scenarios zone-outages node-network-filter pod-network-filter kubevirt-outage)
3+
service-disruption-scenarios service-hijacking syn-flood time-scenarios zone-outages node-network-filter pod-network-filter kubevirt-outage http-load)
44
for i in "${SCENARIOS[@]}"; do
55
export KRKNCTL_INPUT=$(cat $i/krknctl-input.json|tr -d "\n")
66
envsubst < $i/Dockerfile.template > $i/Dockerfile

docker-compose.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ services:
113113
build:
114114
context: ./
115115
dockerfile: ./kubevirt-outage/Dockerfile
116-
image: quay.io/krkn-chaos/krkn-hub:kubevirt-outage
116+
image: quay.io/krkn-chaos/krkn-hub:kubevirt-outage
117+
http-load:
118+
build:
119+
context: ./
120+
dockerfile: ./http-load/Dockerfile
121+
image: quay.io/krkn-chaos/krkn-hub:http-load

docs/http-load.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
### HTTP Load scenario
2+
This scenario generates distributed HTTP load against one or more target endpoints using Vegeta load testing pods deployed inside the cluster.
3+
For more details, please refer to the [Kraken documentation](https://github.com/krkn-chaos/krkn/blob/main/docs/http_load_scenarios.md).
4+
5+
#### Run
6+
If enabling [Cerberus](https://github.com/krkn-chaos/krkn#kraken-scenario-passfail-criteria-and-report) to monitor the cluster and pass/fail the scenario post chaos, refer [docs](https://github.com/redhat-chaos/krkn-hub/tree/main/docs/cerberus.md). Make sure to start it before injecting the chaos and set `CERBERUS_ENABLED` environment variable for the chaos injection container to autoconnect.
7+
8+
```
9+
$ podman run --name=<container_name> --net=host --env-host=true -v <path-to-kube-config>:/home/krkn/.kube/config:Z
10+
-e TARGET_ENDPOINTS="GET https://myapp.example.com/health" \
11+
-e NAMESPACE=<target_namespace> \
12+
-e TOTAL_CHAOS_DURATION=30s \
13+
-e NUMBER_OF_PODS=2 \
14+
-e NODE_SELECTORS=<key>=<value>;<key>=<othervalue> \
15+
-d
16+
quay.io/krkn-chaos/krkn-hub:http-load
17+
18+
$ podman logs -f <container_name or container_id> # Streams Kraken logs
19+
$ podman inspect <container-name or container-id> --format "{{.State.ExitCode}}" # Outputs exit code which can considered as pass/fail for the scenario
20+
```
21+
22+
```
23+
$ docker run $(./get_docker_params.sh) --name=<container_name> --net=host -v <path-to-kube-config>:/home/krkn/.kube/config:Z
24+
-e TARGET_ENDPOINTS="GET https://myapp.example.com/health" \
25+
-e NAMESPACE=<target_namespace> \
26+
-e TOTAL_CHAOS_DURATION=30s \
27+
-e NUMBER_OF_PODS=2 \
28+
-e NODE_SELECTORS=<key>=<value>;<key>=<othervalue> \
29+
-d
30+
quay.io/krkn-chaos/krkn-hub:http-load
31+
32+
$ docker logs -f <container_name or container_id> # Streams Kraken logs
33+
$ docker inspect <container-name or container-id> --format "{{.State.ExitCode}}" # Outputs exit code which can considered as pass/fail for the scenario
34+
```
35+
36+
**TIP**: Because the container runs with a non-root user, ensure the kube config is globally readable before mounting it in the container. You can achieve this with the following commands:
37+
```kubectl config view --flatten > ~/kubeconfig && chmod 444 ~/kubeconfig && docker run $(./get_docker_params.sh) --name=<container_name> --net=host -v ~kubeconfig:/home/krkn/.kube/config:Z -d quay.io/krkn-chaos/krkn-hub:http-load```
38+
#### Supported parameters
39+
40+
The following environment variables can be set on the host running the container to tweak the scenario/faults being injected:
41+
42+
ex.)
43+
`export <parameter_name>=<value>`
44+
45+
See list of variables that apply to all scenarios [here](all_scenarios_env.md) that can be used/set in addition to these scenario specific variables
46+
47+
48+
|Parameter | Description | Default |
49+
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
50+
|TARGET_ENDPOINTS| Semicolon-separated list of target endpoints. Format: METHOD URL;METHOD URL HEADER1:VAL1,HEADER2:VAL2 BODY. Example: GET https://myapp.example.com/health;POST https://myapp.example.com/api Content-Type:application/json {\"key\":\"value\"} | **Required** |
51+
|RATE| Request rate per pod (e.g. 50/1s, 1000/1m, 0 for max throughput) |50/1s|
52+
|TOTAL_CHAOS_DURATION| Duration of the load test (e.g. 30s, 5m, 1h) |30s|
53+
|NAMESPACE| The namespace where the attacker pods will be deployed |default|
54+
|NUMBER_OF_PODS| The number of attacker pods that will be deployed |2|
55+
|WORKERS| Initial number of concurrent workers per pod |10|
56+
|MAX_WORKERS| Maximum number of concurrent workers per pod (auto-scales) |100|
57+
|CONNECTIONS| Maximum number of idle open connections per host |100|
58+
|TIMEOUT| Per-request timeout (e.g. 10s, 30s) |10s|
59+
|IMAGE| The container image that will be used to perform the scenario |quay.io/krkn-chaos/krkn-http-load:latest|
60+
|INSECURE| Skip TLS certificate verification (for self-signed certs) |false|
61+
|NODE_SELECTORS| The node selectors are used to guide the cluster on where to deploy attacker pods. You can specify one or more labels in the format key=value;key=value2 (even using the same key) to choose one or more node categories. If left empty, the pods will be scheduled on any available node, depending on the cluster's capacity. ||
62+
63+
**NOTE** In case of using custom metrics profile or alerts profile when `CAPTURE_METRICS` or `ENABLE_ALERTS` is enabled, mount the metrics profile from the host on which the container is run using podman/docker under `/home/krkn/kraken/config/metrics-aggregated.yaml` and `/home/krkn/kraken/config/alerts`. For example:
64+
```
65+
$ podman run --name=<container_name> --net=host --env-host=true -v <path-to-custom-metrics-profile>:/home/krkn/kraken/config/metrics-aggregated.yaml -v <path-to-custom-alerts-profile>:/home/krkn/kraken/config/alerts -v <path-to-kube-config>:/home/krkn/.kube/config:Z -d quay.io/krkn-chaos/krkn-hub:http-load
66+
```

http-load/Dockerfile.template

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Dockerfile for kraken
2+
3+
FROM quay.io/krkn-chaos/krkn:latest
4+
5+
ENV KUBECONFIG /home/krkn/.kube/config
6+
7+
# Copy configurations
8+
9+
COPY config.yaml.template /home/krkn/kraken/config/config.yaml.template
10+
COPY http-load/env.sh /home/krkn/env.sh
11+
COPY http-load/build_config_file.py /home/krkn/build_config_file.py
12+
COPY env.sh /home/krkn/main_env.sh
13+
COPY http-load/run.sh /home/krkn/run.sh
14+
COPY common_run.sh /home/krkn/common_run.sh
15+
16+
LABEL krknctl.kubeconfig_path="/home/krkn/.kube/config"
17+
LABEL krknctl.title="HTTP Load"
18+
LABEL krknctl.description="This scenario generates distributed HTTP load against one or more target endpoints \
19+
using Vegeta load testing pods deployed inside the cluster. For more details, please refer to \
20+
the following documentation (https://github.com/krkn-chaos/krkn-hub/blob/main/docs/http-load.md)."
21+
22+
23+
LABEL krknctl.input_fields='$KRKNCTL_INPUT'
24+
25+
ENTRYPOINT /home/krkn/kraken/containers/setup-ssh.sh && /home/krkn/run.sh

http-load/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# HTTP Load Scenario Docs
2+
3+
See [doc](../docs/http-load.md) for how to run and all the variables listed

http-load/build_config_file.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import logging
2+
import re
3+
import yaml
4+
import os
5+
import argparse
6+
7+
8+
def main():
9+
10+
parser = argparse.ArgumentParser(description='')
11+
parser.add_argument('--outconfig', type=str, help='Output config path')
12+
args = parser.parse_args()
13+
14+
runs = os.getenv("RUNS", "1")
15+
number_of_pods = os.getenv("NUMBER_OF_PODS", "2")
16+
namespace = os.getenv("NAMESPACE", "default")
17+
image = os.getenv("IMAGE", "quay.io/krkn-chaos/krkn-http-load:latest")
18+
node_selectors = os.getenv("NODE_SELECTORS", "")
19+
target_endpoints = os.getenv("TARGET_ENDPOINTS", "")
20+
rate = os.getenv("RATE", "50/1s")
21+
duration = os.getenv("TOTAL_CHAOS_DURATION", "30s")
22+
workers = os.getenv("WORKERS", "10")
23+
max_workers = os.getenv("MAX_WORKERS", "100")
24+
connections = os.getenv("CONNECTIONS", "100")
25+
timeout = os.getenv("TIMEOUT", "10s")
26+
keepalive = os.getenv("KEEPALIVE", "true")
27+
http2 = os.getenv("HTTP2", "true")
28+
insecure = os.getenv("INSECURE", "false")
29+
30+
if not target_endpoints:
31+
logging.error("TARGET_ENDPOINTS must be set. Format: "
32+
"METHOD URL;METHOD URL or "
33+
"METHOD URL HEADER1:VAL1,HEADER2:VAL2 BODY;...")
34+
exit(1)
35+
36+
node_selectors_re = re.compile(r"^$|^(.+=.*)(;.+=.*)*$")
37+
if not node_selectors_re.match(node_selectors):
38+
logging.error(f"{node_selectors} is not a valid list of node selectors, "
39+
f"node selectors must be one or more selectors separated by ;"
40+
f"e.g. key1=value or key1=value1;key1=value2;key2=value3")
41+
exit(1)
42+
43+
# Parse endpoints: "GET https://url1;POST https://url2 Content-Type:application/json {\"key\":\"val\"}"
44+
endpoints = []
45+
for entry in target_endpoints.split(";"):
46+
parts = entry.strip().split(" ", 3)
47+
if len(parts) < 2:
48+
logging.error(f"Invalid endpoint format: {entry}. "
49+
f"Expected: METHOD URL [HEADERS] [BODY]")
50+
exit(1)
51+
endpoint = {"method": parts[0], "url": parts[1]}
52+
if len(parts) >= 3 and parts[2]:
53+
headers = {}
54+
for header in parts[2].split(","):
55+
if ":" in header:
56+
k, v = header.split(":", 1)
57+
headers[k.strip()] = v.strip()
58+
if headers:
59+
endpoint["headers"] = headers
60+
if len(parts) >= 4 and parts[3]:
61+
endpoint["body"] = parts[3]
62+
endpoints.append(endpoint)
63+
64+
# Parse node selectors
65+
parsed_node_selectors = {}
66+
if node_selectors and node_selectors != '':
67+
for selector in node_selectors.split(";"):
68+
key_value = selector.split("=")
69+
if key_value[0] not in parsed_node_selectors.keys():
70+
parsed_node_selectors[key_value[0]] = []
71+
parsed_node_selectors[key_value[0]].append(key_value[1])
72+
73+
config = [{
74+
"http_load_scenario": {
75+
"runs": int(runs),
76+
"number-of-pods": int(number_of_pods),
77+
"namespace": namespace,
78+
"image": image,
79+
"attacker-nodes": parsed_node_selectors if parsed_node_selectors else {},
80+
"targets": {
81+
"endpoints": endpoints
82+
},
83+
"rate": rate,
84+
"duration": duration,
85+
"workers": int(workers),
86+
"max_workers": int(max_workers),
87+
"connections": int(connections),
88+
"timeout": timeout,
89+
"keepalive": keepalive.lower() == "true",
90+
"http2": http2.lower() == "true",
91+
"insecure": insecure.lower() == "true",
92+
}
93+
}]
94+
95+
with open(args.outconfig, "w") as out:
96+
yaml.dump(config, out, default_flow_style=False, allow_unicode=True)
97+
98+
99+
if __name__ == '__main__':
100+
main()

http-load/env.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
export RUNS=${RUNS:="1"}
3+
export NUMBER_OF_PODS=${NUMBER_OF_PODS:="2"}
4+
export NAMESPACE=${NAMESPACE:="default"}
5+
export IMAGE=${IMAGE:="quay.io/krkn-chaos/krkn-http-load:latest"}
6+
export NODE_SELECTORS=${NODE_SELECTORS:=""}
7+
export TARGET_ENDPOINTS=${TARGET_ENDPOINTS:=""}
8+
export RATE=${RATE:="50/1s"}
9+
export TOTAL_CHAOS_DURATION=${TOTAL_CHAOS_DURATION:="30s"}
10+
export WORKERS=${WORKERS:="10"}
11+
export MAX_WORKERS=${MAX_WORKERS:="100"}
12+
export CONNECTIONS=${CONNECTIONS:="100"}
13+
export TIMEOUT=${TIMEOUT:="10s"}
14+
export KEEPALIVE=${KEEPALIVE:="true"}
15+
export HTTP2=${HTTP2:="true"}
16+
export INSECURE=${INSECURE:="false"}
17+
18+
export SCENARIO_TYPE=${SCENARIO_TYPE:=http_load_scenarios}
19+
export SCENARIO_FILE=${SCENARIO_FILE:="$KRAKEN_FOLDER/scenarios/http-load.yaml"}

http-load/krknctl-input.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"name":"target-endpoints","short_description":"Target endpoints","description":"Semicolon-separated list of target endpoints. Format: METHOD URL;METHOD URL HEADER1:VAL1,HEADER2:VAL2 BODY. Example: GET https://myapp.example.com/health;POST https://myapp.example.com/api Content-Type:application/json {\"key\":\"value\"}","variable":"TARGET_ENDPOINTS","type":"string","default":"","required":"true"},{"name":"rate","short_description":"Request rate","description":"Request rate per pod (e.g. 50/1s, 1000/1m, 0 for max throughput)","variable":"RATE","type":"string","default":"50/1s","required":"false"},{"name":"chaos-duration","short_description":"Chaos duration","description":"Duration of the load test (e.g. 30s, 5m, 1h)","variable":"TOTAL_CHAOS_DURATION","type":"string","default":"30s","required":"false"},{"name":"namespace","short_description":"Namespace","description":"The namespace where the attacker pods will be deployed","variable":"NAMESPACE","type":"string","default":"default","required":"false"},{"name":"number-of-pods","short_description":"Number of pods","description":"The number of attacker pods that will be deployed","variable":"NUMBER_OF_PODS","type":"number","default":"2","required":"false"},{"name":"workers","short_description":"Workers","description":"Initial number of concurrent workers per pod","variable":"WORKERS","type":"number","default":"10","required":"false"},{"name":"max-workers","short_description":"Max workers","description":"Maximum number of concurrent workers per pod (auto-scales)","variable":"MAX_WORKERS","type":"number","default":"100","required":"false"},{"name":"connections","short_description":"Connections","description":"Maximum number of idle open connections per host","variable":"CONNECTIONS","type":"number","default":"100","required":"false"},{"name":"timeout","short_description":"Timeout","description":"Per-request timeout (e.g. 10s, 30s)","variable":"TIMEOUT","type":"string","default":"10s","required":"false"},{"name":"image","short_description":"Workload image","description":"The container image that will be used to perform the scenario","variable":"IMAGE","type":"string","default":"quay.io/krkn-chaos/krkn-http-load:latest","required":"false"},{"name":"insecure","short_description":"Insecure TLS","description":"Skip TLS certificate verification (for self-signed certs)","variable":"INSECURE","type":"string","default":"false","required":"false"},{"name":"node-selectors","short_description":"Node selectors","description":"The node selectors are used to guide the cluster on where to deploy attacker pods. You can specify one or more labels in the format key=value;key=value2 (even using the same key) to choose one or more node categories. If left empty, the pods will be scheduled on any available node, depending on the cluster s capacity.","variable":"NODE_SELECTORS","type":"string","validator":"^$|^(([a-zA-Z0-9._-]+\\=[a-zA-Z0-9._-]+)(;)?)+[^;]$","validation_message":"node selector must be in the format key=value or a list of semicolon-separated selectors key=value;key2=value2;key3=value3","default":"","required":"false"}]

http-load/run.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
ROOT_FOLDER="/home/krkn"
3+
KRAKEN_FOLDER="$ROOT_FOLDER/kraken"
4+
SCENARIO_FOLDER="$KRAKEN_FOLDER/scenarios/http-load"
5+
6+
# Source env.sh to read all the vars
7+
source $ROOT_FOLDER/main_env.sh
8+
source $ROOT_FOLDER/env.sh
9+
source $ROOT_FOLDER/common_run.sh
10+
extra_var=""
11+
if [[ $KRKN_DEBUG == "True" ]];then
12+
set -ex
13+
extra_var="--debug True"
14+
fi
15+
16+
# Build scenario config from environment variables
17+
python3.11 $ROOT_FOLDER/build_config_file.py --outconfig $KRAKEN_FOLDER/scenarios/http-load.yaml
18+
envsubst < $KRAKEN_FOLDER/config/config.yaml.template > $KRAKEN_FOLDER/config/http_load_config.yaml
19+
20+
cat $KRAKEN_FOLDER/config/http_load_config.yaml
21+
cat $KRAKEN_FOLDER/scenarios/http-load.yaml
22+
23+
checks
24+
25+
# Run Kraken
26+
cd $KRAKEN_FOLDER
27+
python3.11 run_kraken.py --config=config/http_load_config.yaml $extra_var

0 commit comments

Comments
 (0)