Storage I/O Throttle
This scenario throttles storage I/O on a PVC-backed volume used by a target pod. It limits read/write IOPS and bandwidth (bytes per second) using Linux cgroup controllers, allowing you to observe how your application behaves under degraded disk performance.
- cgroups v2: writes to
io.max - cgroups v1: writes to
blkio.throttle.*_device
The plugin automatically detects the cgroup version, container runtime (CRI-O or containerd), and the block device backing the volume. No manual host configuration is required.
Why Storage Throttle Scenarios Are Important
Many production incidents stem from slow or saturated storage, not complete outages. Storage I/O throttling lets you simulate realistic degraded-disk conditions and answer critical questions before they happen in production:
- Database slowdowns: Will your database handle reduced disk throughput gracefully, or will queries time out and cascade into application-level failures?
- Write-ahead log (WAL) backpressure: If writes to a WAL-backed volume slow down, does your system queue correctly or lose data?
- Logging and monitoring pressure: When log volumes become slow, do your applications block on writes or degrade silently?
- Storage class validation: Verify that your provisioned IOPS and throughput limits match application requirements under real workload conditions.
- Pod eviction thresholds: Test whether Kubernetes eviction policies trigger correctly when I/O-bound pods consume disproportionate resources.
How It Works
The storage throttle scenario follows these steps:
- Resolve the target pod — If
pvc_nameis provided, the plugin looks up which pod has the PVC mounted. Otherwise, it uses thepod_namedirectly. - Find the PVC volume mount — The plugin identifies the container and mount path for the PVC-backed volume inside the target pod. If
mount_pathis specified, only that specific mount is targeted. - Discover the block device — The block device
major:minoris extracted from/proc/self/mountinfoinside the target container, which is reliable for CSI-provisioned volumes. - Deploy a privileged helper pod — A short-lived privileged pod is deployed on the same node as the target workload. It mounts the host root filesystem and uses
chrootto access host cgroup files. - Detect the cgroup version — The helper pod checks whether the node uses cgroups v1 or v2.
- Discover the cgroup path — The plugin finds the real host cgroup path for the target container by its container ID, excluding CRI-O conmon (container monitor) paths.
- Apply I/O throttle — IOPS and/or bandwidth limits are written to the appropriate cgroup files.
- Hold for duration — The throttle remains active for the configured duration, with progress logged every 30 seconds.
- Remove throttle — Limits are reset to their default (unlimited) values.
- Cleanup — The privileged helper pod is deleted.
Note
Rollback safety: A rollback handler is registered before limits are applied. If the scenario fails or is interrupted, the throttle is automatically removed and the helper pod is cleaned up.Configuration Parameters
| Parameter | Description | Type | Default |
|---|---|---|---|
pvc_name | Target PVC name. If set, the pod is auto-resolved from the PVC. | string | "" |
pod_name | Target pod name. Ignored if pvc_name is set. | string | "" |
namespace | Namespace of the target PVC or pod. Required. | string | "" |
mount_path | Specific mount path to throttle. If empty, the first PVC mount is used. | string | "" |
throttle_type | Type of throttle to apply: iops, bandwidth, or both. | string | bandwidth |
read_iops | Maximum read IOPS. Used when throttle_type is iops or both. | integer | 100 |
write_iops | Maximum write IOPS. Used when throttle_type is iops or both. | integer | 50 |
read_bps | Maximum read bytes per second. Used when throttle_type is bandwidth or both. Supports unit suffixes. | string/integer | 1Mi (1,048,576) |
write_bps | Maximum write bytes per second. Used when throttle_type is bandwidth or both. Supports unit suffixes. | string/integer | 512Ki (524,288) |
duration | How long to hold the throttle. Supports duration suffixes. | string/integer | 60 (seconds) |
image | Container image for the privileged helper pod. | string | quay.io/krkn-chaos/krkn:tools |
Parameter Dependencies
pvc_namevspod_name: At least one is required. If both are set,pvc_nametakes precedence and the pod is auto-resolved from the PVC.throttle_typecontrols which limits apply:iops— onlyread_iopsandwrite_iopsare appliedbandwidth— onlyread_bpsandwrite_bpsare appliedboth— all four limits are applied simultaneously
Supported Unit Suffixes
Byte values (read_bps, write_bps) accept Kubernetes-style unit suffixes:
| Suffix | Type | Multiplier | Example |
|---|---|---|---|
Ki | Binary (kibibyte) | 1,024 | 512Ki = 524,288 bytes/s |
Mi | Binary (mebibyte) | 1,048,576 | 1Mi = 1,048,576 bytes/s |
Gi | Binary (gibibyte) | 1,073,741,824 | 1Gi = 1,073,741,824 bytes/s |
K | Decimal (kilobyte) | 1,000 | 500K = 500,000 bytes/s |
M | Decimal (megabyte) | 1,000,000 | 5M = 5,000,000 bytes/s |
G | Decimal (gigabyte) | 1,000,000,000 | 1G = 1,000,000,000 bytes/s |
| (none) | Raw bytes | 1 | 1048576 = 1,048,576 bytes/s |
Duration values (duration) accept time suffixes:
| Suffix | Unit | Example |
|---|---|---|
s | Seconds | 30s = 30 seconds |
m | Minutes | 2m = 120 seconds |
h | Hours | 1h = 3,600 seconds |
| (none) | Seconds | 120 = 120 seconds |
Prerequisites
- The target PVC must be in
Boundstate and mounted to a running pod. - The privileged helper image (
quay.io/krkn-chaos/krkn:toolsby default) must be pullable on worker nodes. - The cluster must allow privileged pods (required for writing to host cgroup files).
- Supported container runtimes: CRI-O and containerd.
- Supported cgroup versions: v1 and v2.
How to Run Storage I/O Throttle Scenarios
Choose your preferred method to run storage I/O throttle scenarios:
Example scenario files:
- storage_throttle.yaml (Kubernetes)
- storage_throttle.yaml (OpenShift)
Sample scenario config
storage_throttle_scenario:
pvc_name: "" # Target PVC name. If set, pod_name is auto-resolved from PVC.
pod_name: my-app-pod # Target pod name. Ignored if pvc_name is set.
namespace: default # Namespace of the target PVC/pod (required)
mount_path: "" # Specific mount path to throttle. If empty, first PVC mount is used.
throttle_type: bandwidth # "iops", "bandwidth", or "both"
read_iops: 100 # Max read IOPS (used when throttle_type is "iops" or "both")
write_iops: 50 # Max write IOPS (used when throttle_type is "iops" or "both")
read_bps: 1Mi # Max read bytes/sec (used when throttle_type is "bandwidth" or "both")
write_bps: 512Ki # Max write bytes/sec (used when throttle_type is "bandwidth" or "both")
duration: 1m # How long to hold the throttle (supports suffixes: 30s, 2m, 1h)
# image: quay.io/krkn-chaos/krkn:tools # (optional) override helper pod image
Throttle type examples
Bandwidth only (default) — cap read/write throughput:
storage_throttle_scenario:
pvc_name: my-data-pvc
namespace: production
throttle_type: bandwidth
read_bps: 1Mi # 1 MiB/s read limit
write_bps: 512Ki # 512 KiB/s write limit
duration: 2m
IOPS only — cap read/write operations per second:
storage_throttle_scenario:
pod_name: postgres-0
namespace: database
throttle_type: iops
read_iops: 50
write_iops: 25
duration: 90s
Both — apply IOPS and bandwidth limits simultaneously:
storage_throttle_scenario:
pvc_name: wal-pvc
namespace: database
throttle_type: both
read_iops: 100
write_iops: 50
read_bps: 5Mi
write_bps: 2Mi
duration: 3m
How to Use Plugin Name
Add the plugin name to the chaos_scenarios section in config/config.yaml:
kraken:
kubeconfig_path: ~/.kube/config
..
chaos_scenarios:
- storage_throttle_scenarios:
- scenarios/kube/storage_throttle.yaml
Note
You can specify multiple scenario files of the same type by adding additional paths to the list:
kraken:
chaos_scenarios:
- storage_throttle_scenarios:
- scenarios/kube/storage_throttle_bandwidth.yaml
- scenarios/kube/storage_throttle_iops.yaml
You can also combine multiple different scenario types in the same config.yaml file. Scenario types can be specified in any order, and you can include the same scenario type multiple times:
kraken:
chaos_scenarios:
- storage_throttle_scenarios:
- scenarios/kube/storage_throttle.yaml
- pvc_scenarios:
- scenarios/kube/pvc_scenario.yaml
- pod_disruption_scenarios:
- scenarios/kube/pod-kill.yaml
- storage_throttle_scenarios: # Same type can appear multiple times
- scenarios/kube/storage_throttle_iops.yaml
Run
python run_kraken.py --config config/config.yaml
Storage Throttle scenario
This scenario applies storage I/O limits (IOPS and/or bandwidth) on a PVC-backed workload by using Linux cgroup controls through a privileged helper pod on the target node.
Run
If enabling Cerberus to monitor the cluster and pass/fail the scenario post chaos, refer docs. 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=<container_name> \
--net=host \
--pull=always \
--env-host=true \
-v <path-to-kube-config>:/home/krkn/.kube/config:Z \
-e PVC_NAME=<target_pvc_name> \
-e NAMESPACE=<target_namespace> \
-e THROTTLE_TYPE=bandwidth \
-e READ_BPS=1Mi \
-e WRITE_BPS=512Ki \
-e DURATION=1m \
-d containers.krkn-chaos.dev/krkn-chaos/krkn-hub:storage-throttle
$ podman logs -f <container_name or container_id>
$ podman inspect <container-name or container-id> \
--format "{{.State.ExitCode}}"
Note
–env-host: This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines. Without the –env-host option you’ll have to set each environment variable on the podman command line like-e <VARIABLE>=<value>$ docker run $(./get_docker_params.sh) \
--name=<container_name> \
--net=host \
--pull=always \
-v <path-to-kube-config>:/home/krkn/.kube/config:Z \
-e PVC_NAME=<target_pvc_name> \
-e NAMESPACE=<target_namespace> \
-e THROTTLE_TYPE=bandwidth \
-e READ_BPS=1Mi \
-e WRITE_BPS=512Ki \
-e DURATION=1m \
-d containers.krkn-chaos.dev/krkn-chaos/krkn-hub:storage-throttle
$ docker logs -f <container_name or container_id>
$ docker inspect <container-name or container-id> \
--format "{{.State.ExitCode}}"
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=<container_name> \
--net=host \
--pull=always \
-v ~/kubeconfig:/home/krkn/.kube/config:Z \
-d containers.krkn-chaos.dev/krkn-chaos/krkn-hub:storage-throttle
Supported parameters
The following environment variables can be set on the host running the container to tweak the scenario/faults being injected:
Example if –env-host is used:
export <parameter_name>=<value>
OR on the command line like example:
-e <VARIABLE>=<value>
See list of variables that apply to all scenarios here that can be used/set in addition to these scenario specific variables
| Parameter | Description | Type | Default |
|---|---|---|---|
PVC_NAME | Target PVC name. If set, POD_NAME is auto-resolved from PVC | string | |
POD_NAME | Target pod name. Ignored if PVC_NAME is set | string | |
NAMESPACE | Namespace of the target pod/PVC | string | default |
MOUNT_PATH | Specific mount path to throttle (absolute path, example: /data) | string | |
THROTTLE_TYPE | Throttle mode to apply (bandwidth, iops, both) | string | bandwidth |
READ_IOPS | Maximum read IOPS (used for iops/both) | number | 100 |
WRITE_IOPS | Maximum write IOPS (used for iops/both) | number | 50 |
READ_BPS | Maximum read bytes/sec (example: 1Mi, 512Ki, 1000000) | string | 1Mi |
WRITE_BPS | Maximum write bytes/sec (example: 512Ki, 1Mi, 500000) | string | 512Ki |
DURATION | Duration to hold throttling (example: 30s, 1m, 120) | string | 1m |
IMAGE | Image used for privileged helper pod that writes cgroup values | string | quay.io/krkn-chaos/krkn:tools |
Parameter dependencies
- At least one of
PVC_NAMEorPOD_NAMEshould be set. - If both are set,
PVC_NAMEtakes precedence andPOD_NAMEis ignored.
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=<container_name> \
--net=host \
--pull=always \
--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 containers.krkn-chaos.dev/krkn-chaos/krkn-hub:storage-throttle
krknctl run storage-throttle [--<parameter> <value>]
Can also set any global variable listed here
Scenario specific parameters:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
--pvc-name | Target PVC name. If set, --pod-name is auto-resolved from PVC | string | No | |
--pod-name | Target pod name. Ignored if --pvc-name is set | string | No | |
--namespace | Namespace of the target pod/PVC | string | Yes | default |
--mount-path | Specific mount path to throttle (absolute path, example: /data) | string | No | |
--throttle-type | Throttle mode to apply (bandwidth, iops, both) | enum | No | bandwidth |
--read-iops | Maximum read IOPS (used for iops/both) | number | No | 100 |
--write-iops | Maximum write IOPS (used for iops/both) | number | No | 50 |
--read-bps | Maximum read bytes/sec (example: 1Mi, 512Ki, 1000000) | string | No | 1Mi |
--write-bps | Maximum write bytes/sec (example: 512Ki, 1Mi, 500000) | string | No | 512Ki |
--duration | Duration to hold throttling (example: 30s, 1m, 120) | string | No | 1m |
--image | Image used for privileged helper pod that writes cgroup values | string | No | quay.io/krkn-chaos/krkn:tools |
Parameter dependencies
- At least one of
--pvc-nameor--pod-nameshould be set. - If both are set,
--pvc-nametakes precedence and--pod-nameis ignored.
To see all available scenario options
krknctl run storage-throttle --help