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:

  1. Resolve the target pod — If pvc_name is provided, the plugin looks up which pod has the PVC mounted. Otherwise, it uses the pod_name directly.
  2. Find the PVC volume mount — The plugin identifies the container and mount path for the PVC-backed volume inside the target pod. If mount_path is specified, only that specific mount is targeted.
  3. Discover the block device — The block device major:minor is extracted from /proc/self/mountinfo inside the target container, which is reliable for CSI-provisioned volumes.
  4. 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 chroot to access host cgroup files.
  5. Detect the cgroup version — The helper pod checks whether the node uses cgroups v1 or v2.
  6. 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.
  7. Apply I/O throttle — IOPS and/or bandwidth limits are written to the appropriate cgroup files.
  8. Hold for duration — The throttle remains active for the configured duration, with progress logged every 30 seconds.
  9. Remove throttle — Limits are reset to their default (unlimited) values.
  10. Cleanup — The privileged helper pod is deleted.

Configuration Parameters

ParameterDescriptionTypeDefault
pvc_nameTarget PVC name. If set, the pod is auto-resolved from the PVC.string""
pod_nameTarget pod name. Ignored if pvc_name is set.string""
namespaceNamespace of the target PVC or pod. Required.string""
mount_pathSpecific mount path to throttle. If empty, the first PVC mount is used.string""
throttle_typeType of throttle to apply: iops, bandwidth, or both.stringbandwidth
read_iopsMaximum read IOPS. Used when throttle_type is iops or both.integer100
write_iopsMaximum write IOPS. Used when throttle_type is iops or both.integer50
read_bpsMaximum read bytes per second. Used when throttle_type is bandwidth or both. Supports unit suffixes.string/integer1Mi (1,048,576)
write_bpsMaximum write bytes per second. Used when throttle_type is bandwidth or both. Supports unit suffixes.string/integer512Ki (524,288)
durationHow long to hold the throttle. Supports duration suffixes.string/integer60 (seconds)
imageContainer image for the privileged helper pod.stringquay.io/krkn-chaos/krkn:tools

Parameter Dependencies

  • pvc_name vs pod_name: At least one is required. If both are set, pvc_name takes precedence and the pod is auto-resolved from the PVC.
  • throttle_type controls which limits apply:
    • iops — only read_iops and write_iops are applied
    • bandwidth — only read_bps and write_bps are applied
    • both — all four limits are applied simultaneously

Supported Unit Suffixes

Byte values (read_bps, write_bps) accept Kubernetes-style unit suffixes:

SuffixTypeMultiplierExample
KiBinary (kibibyte)1,024512Ki = 524,288 bytes/s
MiBinary (mebibyte)1,048,5761Mi = 1,048,576 bytes/s
GiBinary (gibibyte)1,073,741,8241Gi = 1,073,741,824 bytes/s
KDecimal (kilobyte)1,000500K = 500,000 bytes/s
MDecimal (megabyte)1,000,0005M = 5,000,000 bytes/s
GDecimal (gigabyte)1,000,000,0001G = 1,000,000,000 bytes/s
(none)Raw bytes11048576 = 1,048,576 bytes/s

Duration values (duration) accept time suffixes:

SuffixUnitExample
sSeconds30s = 30 seconds
mMinutes2m = 120 seconds
hHours1h = 3,600 seconds
(none)Seconds120 = 120 seconds

Prerequisites

  • The target PVC must be in Bound state and mounted to a running pod.
  • The privileged helper image (quay.io/krkn-chaos/krkn:tools by 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:

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

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}}"
$ 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

ParameterDescriptionTypeDefault
PVC_NAMETarget PVC name. If set, POD_NAME is auto-resolved from PVCstring
POD_NAMETarget pod name. Ignored if PVC_NAME is setstring
NAMESPACENamespace of the target pod/PVCstringdefault
MOUNT_PATHSpecific mount path to throttle (absolute path, example: /data)string
THROTTLE_TYPEThrottle mode to apply (bandwidth, iops, both)stringbandwidth
READ_IOPSMaximum read IOPS (used for iops/both)number100
WRITE_IOPSMaximum write IOPS (used for iops/both)number50
READ_BPSMaximum read bytes/sec (example: 1Mi, 512Ki, 1000000)string1Mi
WRITE_BPSMaximum write bytes/sec (example: 512Ki, 1Mi, 500000)string512Ki
DURATIONDuration to hold throttling (example: 30s, 1m, 120)string1m
IMAGEImage used for privileged helper pod that writes cgroup valuesstringquay.io/krkn-chaos/krkn:tools

Parameter dependencies

  • At least one of PVC_NAME or POD_NAME should be set.
  • If both are set, PVC_NAME takes precedence and POD_NAME is 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:

ParameterDescriptionTypeRequiredDefault
--pvc-nameTarget PVC name. If set, --pod-name is auto-resolved from PVCstringNo
--pod-nameTarget pod name. Ignored if --pvc-name is setstringNo
--namespaceNamespace of the target pod/PVCstringYesdefault
--mount-pathSpecific mount path to throttle (absolute path, example: /data)stringNo
--throttle-typeThrottle mode to apply (bandwidth, iops, both)enumNobandwidth
--read-iopsMaximum read IOPS (used for iops/both)numberNo100
--write-iopsMaximum write IOPS (used for iops/both)numberNo50
--read-bpsMaximum read bytes/sec (example: 1Mi, 512Ki, 1000000)stringNo1Mi
--write-bpsMaximum write bytes/sec (example: 512Ki, 1Mi, 500000)stringNo512Ki
--durationDuration to hold throttling (example: 30s, 1m, 120)stringNo1m
--imageImage used for privileged helper pod that writes cgroup valuesstringNoquay.io/krkn-chaos/krkn:tools

Parameter dependencies

  • At least one of --pvc-name or --pod-name should be set.
  • If both are set, --pvc-name takes precedence and --pod-name is ignored.

To see all available scenario options

krknctl run storage-throttle --help