diff --git a/docs/syn-flood.md b/docs/syn-flood.md new file mode 100644 index 0000000..3971533 --- /dev/null +++ b/docs/syn-flood.md @@ -0,0 +1,67 @@ +### Syn Flood scenario +This scenario simulates a user-defined surge of TCP SYN requests directed at one or more services deployed within the cluster or an external target reachable by the cluster. +For more details, please refer to the following [documentation](https://github.com/krkn-chaos/krkn/blob/main/docs/syn_flood_scenarios.md). + +#### Run +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. + +``` +$ podman run --name= --net=host --env-host=true -v :/home/krkn/.kube/config:Z +-e TARGET_PORT= \ +-e NAMESPACE= \ +-e TOTAL_CHAOS_DURATION= \ +-e TARGET_SERVICE= \ +-e NUMBER_OF_PODS=10 \ +-e NODE_SELECTORS==;= \ +-d +quay.io/krkn-chaos/krkn-hub:syn-flood + +$ podman logs -f # Streams Kraken logs +$ podman inspect --format "{{.State.ExitCode}}" # Outputs exit code which can considered as pass/fail for the scenario +``` + +``` +$ docker run $(./get_docker_params.sh) --name= --net=host -v :/home/krkn/.kube/config:Z +-e TARGET_PORT= \ +-e NAMESPACE= \ +-e TOTAL_CHAOS_DURATION= \ +-e TARGET_SERVICE= \ +-e NUMBER_OF_PODS=10 \ +-e NODE_SELECTORS==;= \ +-d +quay.io/krkn-chaos/krkn-hub:syn-flood + +$ docker logs -f # Streams Kraken logs +$ docker inspect --format "{{.State.ExitCode}}" # Outputs exit code which can considered as pass/fail for the scenario +``` + +**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: +```kubectl config view --flatten > ~/kubeconfig && chmod 444 ~/kubeconfig && docker run $(./get_docker_params.sh) --name= --net=host -v ~kubeconfig:/home/krkn/.kube/config:Z -d quay.io/krkn-chaos/krkn-hub:``` +#### Supported parameters + +The following environment variables can be set on the host running the container to tweak the scenario/faults being injected: + +ex.) +`export =` + +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 + + +|Parameter | Description | Default | +|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +|PACKET_SIZE| The size in bytes of the SYN packet |120| +|WINDOW_SIZE| The TCP window size between packets in bytes |64| +|TOTAL_CHAOS_DURATION| The number of seconds the chaos will last |120| +|NAMESPACE| The namespace containing the target service and where the attacker pods will be deployed |default| +|TARGET_SERVICE| The service name (or the hostname/IP address in case an external target will be hit) that will be affected by the attack. Must be empty if TARGET_SERVICE_LABEL will be set || +|TARGET_PORT| The TCP port that will be targeted by the attack || +|TARGET_SERVICE_LABEL| The label that will be used to select one or more services. Must be left empty if TARGET_SERVICE variable is set || +|NUMBER_OF_PODS| The number of attacker pods that will be deployed |2| +|IMAGE| The container image that will be used to perform the scenario |quay.io/krkn-chaos/krkn-syn-flood:latest| +|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. || + +**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: +``` +$ podman run --name= --net=host --env-host=true -v :/home/krkn/kraken/config/metrics-aggregated.yaml -v :/home/krkn/kraken/config/alerts -v :/home/krkn/.kube/config:Z -d quay.io/krkn-chaos/krkn-hub:syn-flood +``` + diff --git a/syn-flood/Dockerfile b/syn-flood/Dockerfile new file mode 100644 index 0000000..008930b --- /dev/null +++ b/syn-flood/Dockerfile @@ -0,0 +1,16 @@ +# Dockerfile for kraken + +#FROM quay.io/krkn-chaos/krkn:latest +FROM quay.io/rh_ee_tsebasti/krkn:syn +ENV KUBECONFIG /home/krkn/.kube/config + +# Copy configurations +COPY metrics_config.yaml.template /home/krkn/kraken/config/kube_burner.yaml.template +COPY config.yaml.template /home/krkn/kraken/config/config.yaml.template +COPY syn-flood/env.sh /home/krkn/env.sh +COPY syn-flood/build_config_file.py /home/krkn/build_config_file.py +COPY env.sh /home/krkn/main_env.sh +COPY syn-flood/run.sh /home/krkn/run.sh +COPY common_run.sh /home/krkn/common_run.sh + +ENTRYPOINT /home/krkn/run.sh diff --git a/syn-flood/README.md b/syn-flood/README.md new file mode 100644 index 0000000..75082c2 --- /dev/null +++ b/syn-flood/README.md @@ -0,0 +1,3 @@ +# Node CPU Hog Scenario Docs + +See [doc](https://github.com/redhat-chaos/krkn-hub/blob/main/docs/syn-flood.md) for how to run and all the variables listed diff --git a/syn-flood/build_config_file.py b/syn-flood/build_config_file.py new file mode 100644 index 0000000..e8e488c --- /dev/null +++ b/syn-flood/build_config_file.py @@ -0,0 +1,65 @@ +import logging +import re +import yaml +import os +import argparse + + +def main(): + + parser = argparse.ArgumentParser(description='') + parser.add_argument('--outconfig', type=str, help='Output config path') + args = parser.parse_args() + config = {} + packet_size = os.getenv("PACKET_SIZE") + window_size = os.getenv("WINDOW_SIZE") + duration = os.getenv("TOTAL_CHAOS_DURATION") + namespace = os.getenv("NAMESPACE") + target_service = os.getenv("TARGET_SERVICE") + target_service_label = os.getenv("TARGET_SERVICE_LABEL") + target_port = os.getenv("TARGET_PORT") + number_of_pods = os.getenv("NUMBER_OF_PODS") + image = os.getenv("IMAGE") + node_selectors = os.getenv("NODE_SELECTORS") + + target_service_label_re = re.compile(r"^$|^.+=.*$") + node_selectors_re = re.compile(r"^$|^(.+=.*)(;.+=.*)*$") + + if not target_service_label_re.match(target_service_label): + logging.error(f"{target_service_label} is not a valid service label, " + f"service label must be either empty or in the format " + f"key=value") + exit(1) + + if not node_selectors_re.match(node_selectors): + logging.error(f"{node_selectors} is not a valid list of node selectors, " + f"node selectors must be one or more selectors separated by ;" + f"e.g. key1=value or key1=value1;key1=value2;key2=value3") + exit(1) + + config["packet-size"] = int(packet_size) + config["window-size"] = int(window_size) + config["duration"] = int(duration) + config["namespace"] = namespace + config["target-service"] = target_service + config["target-port"] = int(target_port) + config["target-service-label"] = target_service_label + config["number-of-pods"] = int(number_of_pods) + config["image"] = image + + parsed_node_selectors: dict[str, list[str]] = {} + if node_selectors and node_selectors != '': + for selector in node_selectors.split(";"): + key_value = selector.split("=") + if key_value[0] not in parsed_node_selectors.keys(): + parsed_node_selectors[key_value[0]] = [] + parsed_node_selectors[key_value[0]].append(key_value[1]) + + config["attacker-nodes"] = parsed_node_selectors + + with open(args.outconfig, "w") as out: + yaml.dump(config, out, default_flow_style=False, allow_unicode=True) + + +if __name__ == '__main__': + main() diff --git a/syn-flood/env.sh b/syn-flood/env.sh new file mode 100644 index 0000000..eb6f2af --- /dev/null +++ b/syn-flood/env.sh @@ -0,0 +1,18 @@ +#!/bin/bash +export SCENARIO_CONFIG_FILE="$KRAKEN_FOLDER/scenarios/kube/syn_flood_config.yaml" +export PACKET_SIZE=${PACKET_SIZE:="120"} +export WINDOW_SIZE=${WINDOW_SIZE:="64"} +export TOTAL_CHAOS_DURATION=${TOTAL_CHAOS_DURATION:="120"} +export NAMESPACE=${NAMESPACE:="default"} +export TARGET_SERVICE=${TARGET_SERVICE} +export TARGET_PORT=${TARGET_PORT} +export TARGET_SERVICE_LABEL=${TARGET_SERVICE_LABEL} +export NUMBER_OF_PODS=${NUMBER_OF_PODS:="2"} +export IMAGE=${IMAGE:="quay.io/krkn-chaos/krkn-syn-flood"} +export NODE_SELECTORS=${NODE_SELECTORS:=""} + +python3.9 $ROOT_FOLDER/build_config_file.py --outconfig $SCENARIO_CONFIG_FILE +cat $SCENARIO_CONFIG_FILE + +export SCENARIO_TYPE=${SCENARIO_TYPE:=syn_flood} +export SCENARIO_FILE=${SCENARIO_FILE:=$SCENARIO_CONFIG_FILE} \ No newline at end of file diff --git a/syn-flood/run.sh b/syn-flood/run.sh new file mode 100755 index 0000000..0755b82 --- /dev/null +++ b/syn-flood/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -ex + +ROOT_FOLDER="/home/krkn" +KRAKEN_FOLDER="$ROOT_FOLDER/kraken" +SCENARIO_FOLDER="$KRAKEN_FOLDER/scenarios/syn-flood" + +# Source env.sh to read all the vars +source $ROOT_FOLDER/main_env.sh +source $ROOT_FOLDER/env.sh + +source $ROOT_FOLDER/common_run.sh + +# Substitute config with environment vars defined +envsubst < $KRAKEN_FOLDER/config/config.yaml.template > $KRAKEN_FOLDER/config/syn_flood_config.yaml + +checks +config_setup + +# Run Kraken +cd $KRAKEN_FOLDER +python3.9 run_kraken.py --config=config/syn_flood_config.yaml + +