From de8e2246d1ccd0cb24cf110d9596db3a372f1f42 Mon Sep 17 00:00:00 2001 From: Hiranmoy Date: Wed, 15 Apr 2026 19:12:57 +0600 Subject: [PATCH 1/3] Redis Chaos testing Signed-off-by: Hiranmoy --- content/post/chaos-testing-redis/hero.jpg | Bin 0 -> 44833 bytes content/post/chaos-testing-redis/index.md | 572 ++++++++++++++++++++++ 2 files changed, 572 insertions(+) create mode 100644 content/post/chaos-testing-redis/hero.jpg create mode 100644 content/post/chaos-testing-redis/index.md diff --git a/content/post/chaos-testing-redis/hero.jpg b/content/post/chaos-testing-redis/hero.jpg new file mode 100644 index 0000000000000000000000000000000000000000..df8ccf959c9e693b6c97caf79ae38c4ebac8c810 GIT binary patch literal 44833 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 literal 0 HcmV?d00001 diff --git a/content/post/chaos-testing-redis/index.md b/content/post/chaos-testing-redis/index.md new file mode 100644 index 000000000..20e4c92e9 --- /dev/null +++ b/content/post/chaos-testing-redis/index.md @@ -0,0 +1,572 @@ +--- +title: Chaos Testing Redis on Kubernetes with KubeDB and Chaos Mesh +date: "2026-04-15" +weight: 14 +authors: +- name: Hiranmoy Chowdhury + image: /images/author/hiranmoy.jpg +tags: +- chaos-engineering +- chaos-mesh +- database +- kubedb +- kubernetes +- redis +coverImage: /images/blog/chaos-testing-redis/redis-chaos.png +--- + +[Redis](https://redis.io/) is a popular in-memory data store used for caching, session management, pub/sub messaging, leaderboards, and real-time analytics. In many architectures, Redis sits in the critical path — a failure can cause cascading degradation across the entire application stack. + +**Chaos engineering** is the discipline of proactively injecting failures into a system to discover weaknesses before they manifest as production incidents. + +In this post, we will: +1. Deploy a production-style Redis Cluster using **[KubeDB](https://kubedb.com)** +2. Inject ten different failure scenarios using **[Chaos Mesh](https://chaos-mesh.org)** +3. Observe and validate how Redis and KubeDB respond to each fault + +--- + +## What is Chaos Engineering? + +Chaos engineering is the practice of intentionally introducing controlled failures into a system to observe how it behaves. The goal is not to break things — it is to learn how the system responds so you can make it more resilient. + +For a Redis deployment, the questions chaos engineering helps answer are: + +- Does Redis recover automatically after a pod is killed? +- Does the cluster re-converge after a network partition? +- Can clients reconnect seamlessly after a disruption? +- Does KubeDB restore the desired state after a fault? +- How does Redis behave under CPU or memory pressure? + +--- + +## Tools Used + +| Tool | Purpose | +|---|---| +| [KubeDB](https://kubedb.com) | Manages Redis lifecycle on Kubernetes | +| [Chaos Mesh](https://chaos-mesh.org) | Injects chaos experiments into the cluster | +| Redis 7.4.0 | The database under test | + +--- + +## Prerequisites + +Before you begin, make sure you have the following: + +- A running Kubernetes cluster (GKE, EKS, AKS, or a local cluster using Kind or Minikube) +- `kubectl` configured to access the cluster +- KubeDB operator installed — follow the [KubeDB setup guide](https://kubedb.com/docs/v2026.2.26/setup/) +- Chaos Mesh installed — follow the [Chaos Mesh installation guide](https://chaos-mesh.org/docs/production-installation-using-helm/) +- A default or usable `StorageClass` in the cluster + +--- + +## Step 1: Deploy Redis Cluster with KubeDB + +We will deploy a Redis Cluster with 3 shards and 2 replicas per shard. This gives us a proper distributed setup where we can observe failover and re-convergence behavior. + +Create the namespace: + +```bash +kubectl create ns demo +``` + +Apply the Redis manifest: + +```yaml +apiVersion: kubedb.com/v1 +kind: Redis +metadata: + name: redis-cluster + namespace: demo +spec: + version: "7.4.0" + mode: Cluster + cluster: + shards: 3 + replicas: 2 + storageType: Durable + storage: + resources: + requests: + storage: 1Gi + storageClassName: standard + accessModes: + - ReadWriteOnce + deletionPolicy: WipeOut +``` + +```bash +kubectl apply -f redis-cluster.yaml +``` + +Wait for Redis to become ready: + +```bash +kubectl get redis -n demo -w +``` + +``` +NAME VERSION STATUS AGE +redis-cluster 7.4.0 Ready 3m +``` + + +## Step 2: Verify Redis is Healthy + +Retrieve the Redis password from the secret KubeDB created: + +```bash +export PASSWORD=$(kubectl get secret -n demo redis-cluster-auth \ + -o jsonpath='{.data.password}' | base64 -d) +``` + +Check the cluster state: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c CLUSTER INFO +``` + +Write a test key that we will check after each experiment to verify data integrity: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c SET chaos-test "before-chaos" +``` + +Read it back: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test +``` + +``` +"before-chaos" +``` + +--- + +## Step 3: Verify Chaos Mesh is Ready + +```bash +kubectl get pods -n chaos-mesh +``` + +``` +NAME READY STATUS RESTARTS AGE +chaos-controller-manager-xxxxxxxxx-xxxxx 3/3 Running 0 5m +chaos-daemon-xxxxx 1/1 Running 0 5m +chaos-daemon-xxxxx 1/1 Running 0 5m +chaos-dashboard-xxxxxxxxx-xxxxx 1/1 Running 0 5m +``` + +--- + +## Chaos Experiments + +For each experiment below, the workflow is: + +1. Apply the manifest +2. Watch pod and Redis status +3. Verify data is still accessible +4. Delete the experiment and wait for full recovery before running the next one + +--- + +### Experiment 1: Pod Failure + +**What it does:** Marks a Redis pod as unavailable without killing the process. This simulates Kubernetes declaring a pod unhealthy. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: PodChaos +metadata: + name: redis-pod-failure + namespace: demo +spec: + action: pod-failure + mode: one + duration: "30s" + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" +``` + +Observe: + +```bash +kubectl get pods -n demo -w +kubectl get redis -n demo redis-cluster +``` + +One pod will go into a not-ready state. After the 30-second duration, the experiment ends and the pod recovers automatically. + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test +"before-chaos" +``` + +--- + +### Experiment 2: Pod Kill + +**What it does:** Sends `SIGKILL` to a Redis pod, simulating an OOM kill or a sudden node failure. Unlike pod-failure, the pod process is actually terminated and Kubernetes must reschedule it. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: PodChaos +metadata: + name: redis-pod-kill + namespace: demo +spec: + action: pod-kill + mode: one + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" +``` + +Observe: + +```bash +kubectl get pods -n demo -w +``` + +The killed pod transitions to `Terminating` then gets recreated by the StatefulSet controller. KubeDB reconciles the Redis resource back to the desired state. + +```bash +kubectl get pods -n demo -l app.kubernetes.io/instance=redis-cluster +``` + +``` +NAME READY STATUS RESTARTS AGE +redis-cluster-shard0-0 1/1 Running 1 15m +``` + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +``` + +--- + +### Experiment 3: Container Kill + +**What it does:** Kills only the `redis` container inside a pod, without deleting the pod itself. This tests in-place container restart behavior and is useful when sidecars or init containers are involved. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: PodChaos +metadata: + name: redis-container-kill + namespace: demo +spec: + action: container-kill + mode: one + containerNames: + - redis + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" +``` + +Observe: + +```bash +kubectl get pods -n demo -w +``` + +The pod stays alive. Only the `redis` container restarts. Once it comes back up, it rejoins the cluster automatically. + +--- + +### Experiment 4: Network Delay + +**What it does:** Injects artificial latency into the network traffic of Redis pods. This simulates cross-availability-zone communication, saturated network links, or noisy neighbour conditions. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: NetworkChaos +metadata: + name: redis-network-delay + namespace: demo +spec: + action: delay + mode: all + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + delay: + latency: "200ms" + correlation: "25" + jitter: "50ms" + duration: "60s" +``` + +Observe: + +```bash +time kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c PING +``` + +Commands that normally complete in under 1ms will now take 200ms or more. After the 60-second window, latency returns to normal. + +--- + +### Experiment 5: Network Loss + +**What it does:** Randomly drops packets for Redis pods. This simulates flaky networks, overloaded switches, or unreliable cloud networking. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: NetworkChaos +metadata: + name: redis-network-loss + namespace: demo +spec: + action: loss + mode: all + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + loss: + loss: "50" + correlation: "25" + duration: "60s" +``` + +Observe: + +Some Redis commands will time out or fail during this window. After the 60-second experiment, the packet loss stops and clients reconnect automatically. Use this experiment to validate your application's retry logic. + +--- + +### Experiment 6: Network Corruption + +**What it does:** Corrupts a percentage of network packets for Redis pods. This simulates bit-flip errors from faulty hardware or bad cables. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: NetworkChaos +metadata: + name: redis-network-corruption + namespace: demo +spec: + action: corrupt + mode: all + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + corrupt: + corrupt: "40" + correlation: "25" + duration: "60s" +``` + +Observe: + +TCP will detect and retransmit corrupted packets, so most Redis commands will still succeed but with higher latency and occasional errors. After the experiment, the network returns to normal. + +--- + +### Experiment 7: Network Partition + +**What it does:** Completely isolates one Redis pod from the rest of the cluster. This is the most aggressive network experiment and tests how the Redis Cluster handles a split-brain scenario. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: NetworkChaos +metadata: + name: redis-network-partition + namespace: demo +spec: + action: partition + mode: one + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + direction: both + duration: "30s" +``` + +Observe: + +```bash +kubectl get redis -n demo redis-cluster -o yaml +kubectl get pods -n demo -w +``` + +The isolated pod is cut off from its peers. Redis Cluster will mark the affected shard as unavailable. Once the 30-second window ends, the partition is lifted and the cluster re-converges. + + +--- + +### Experiment 8: CPU Stress + +**What it does:** Saturates the CPU of a Redis pod to simulate CPU-intensive workloads on the same node or a resource-constrained environment. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: StressChaos +metadata: + name: redis-cpu-stress + namespace: demo +spec: + mode: one + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + stressors: + cpu: + workers: 2 + load: 100 + duration: "60s" +``` + +Observe: + +```bash +kubectl top pods -n demo +``` + +Redis is single-threaded for command processing, so heavy CPU stress will noticeably increase command latency. After the experiment, CPU usage drops and performance returns to baseline. + +--- + +### Experiment 9: Memory Stress + +**What it does:** Allocates a large chunk of memory inside a Redis pod to simulate memory pressure, approaching OOM conditions. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: StressChaos +metadata: + name: redis-memory-stress + namespace: demo +spec: + mode: one + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + stressors: + memory: + workers: 2 + size: "256MB" + duration: "60s" +``` + +Observe: + +```bash +kubectl top pods -n demo +kubectl describe pod -n demo redis-cluster-shard0-0 | grep -A5 "Limits\|Requests" +``` + +Watch whether Redis begins evicting keys (depending on your `maxmemory-policy` setting) and whether it recovers cleanly once the stressor is removed. + +--- + +### Experiment 10: I/O Chaos + +**What it does:** Injects latency into disk I/O operations for the Redis data directory. This simulates a slow or degraded storage backend affecting AOF writes and RDB snapshots. + +```yaml +apiVersion: chaos-mesh.org/v1alpha1 +kind: IOChaos +metadata: + name: redis-io-delay + namespace: demo +spec: + action: latency + mode: one + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/instance: "redis-cluster" + app.kubernetes.io/name: "redis" + volumePath: /data + path: "/data/**" + delay: "100ms" + percent: 50 + duration: "60s" +``` + +Observe: + +```bash +kubectl logs -n demo redis-cluster-shard0-0 --tail=50 -f +``` + +Redis will log warnings about slow AOF flushes or delayed RDB saves. After the experiment ends, I/O returns to normal and persistence operations resume. + +--- + +## Summary of Experiments + +| # | Experiment | Chaos Kind | What is Validated | +|---|---|---|-------------------------------------------| +| 1 | Pod Failure | PodChaos | Pod unavailability, automatic recovery | +| 2 | Pod Kill | PodChaos | Hard kill, PetSet reschedule behavior | +| 3 | Container Kill | PodChaos | In-place container restart | +| 4 | Network Delay | NetworkChaos | High latency, client timeout behavior | +| 5 | Network Loss | NetworkChaos | Packet drops, client retry behavior | +| 6 | Network Corruption | NetworkChaos | Corrupted packets, TCP retransmission | +| 7 | Network Partition | NetworkChaos | Cluster split, re-convergence | +| 8 | CPU Stress | StressChaos | Performance under CPU saturation | +| 9 | Memory Stress | StressChaos | OOM behavior, key eviction | +| 10 | I/O Chaos | IOChaos | Storage degradation, persistence behavior | + +--- + +## Cleanup + +Delete the Redis cluster: + +```bash +kubectl delete redis -n demo redis-cluster +``` + +Delete the namespace: + +```bash +kubectl delete ns demo +``` From e61f0bb487f75be37053b35765d983b0ff0895bc Mon Sep 17 00:00:00 2001 From: Hiranmoy Date: Fri, 17 Apr 2026 09:46:56 +0600 Subject: [PATCH 2/3] fix author Signed-off-by: Hiranmoy --- content/post/chaos-testing-redis/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/post/chaos-testing-redis/index.md b/content/post/chaos-testing-redis/index.md index 20e4c92e9..20781b1fd 100644 --- a/content/post/chaos-testing-redis/index.md +++ b/content/post/chaos-testing-redis/index.md @@ -3,8 +3,7 @@ title: Chaos Testing Redis on Kubernetes with KubeDB and Chaos Mesh date: "2026-04-15" weight: 14 authors: -- name: Hiranmoy Chowdhury - image: /images/author/hiranmoy.jpg +- Hiranmoy Das Chowdhury tags: - chaos-engineering - chaos-mesh From ee44a329fb7b7211b8a4e0522d89279327e65424 Mon Sep 17 00:00:00 2001 From: HiranmoyChowdhury Date: Wed, 6 May 2026 20:07:58 +0600 Subject: [PATCH 3/3] until 9 Signed-off-by: HiranmoyChowdhury --- content/post/chaos-testing-redis/index.md | 422 +++++++++++++++++----- 1 file changed, 335 insertions(+), 87 deletions(-) diff --git a/content/post/chaos-testing-redis/index.md b/content/post/chaos-testing-redis/index.md index 20781b1fd..852c13d8b 100644 --- a/content/post/chaos-testing-redis/index.md +++ b/content/post/chaos-testing-redis/index.md @@ -90,7 +90,6 @@ spec: resources: requests: storage: 1Gi - storageClassName: standard accessModes: - ReadWriteOnce deletionPolicy: WipeOut @@ -103,12 +102,16 @@ kubectl apply -f redis-cluster.yaml Wait for Redis to become ready: ```bash -kubectl get redis -n demo -w -``` +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Ready 73s -``` -NAME VERSION STATUS AGE -redis-cluster 7.4.0 Ready 3m +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 0 70s +pod/redis-cluster-shard0-1 1/1 Running 0 54s +pod/redis-cluster-shard1-0 1/1 Running 0 67s +pod/redis-cluster-shard1-1 1/1 Running 0 53s +pod/redis-cluster-shard2-0 1/1 Running 0 66s +pod/redis-cluster-shard2-1 1/1 Running 0 53s ``` @@ -139,11 +142,10 @@ Read it back: ```bash kubectl exec -it -n demo redis-cluster-shard0-0 -- \ - redis-cli -a $PASSWORD -c GET chaos-test -``` - -``` + redis-cli -a $PASSWORD -c GET chaos-test +Defaulted container "redis" out of: redis, redis-init (init) "before-chaos" + ``` --- @@ -152,14 +154,13 @@ kubectl exec -it -n demo redis-cluster-shard0-0 -- \ ```bash kubectl get pods -n chaos-mesh -``` - -``` -NAME READY STATUS RESTARTS AGE -chaos-controller-manager-xxxxxxxxx-xxxxx 3/3 Running 0 5m -chaos-daemon-xxxxx 1/1 Running 0 5m -chaos-daemon-xxxxx 1/1 Running 0 5m -chaos-dashboard-xxxxxxxxx-xxxxx 1/1 Running 0 5m +NAME READY STATUS RESTARTS AGE +chaos-controller-manager-b8d65b98-75s8w 1/1 Running 0 2m15s +chaos-controller-manager-b8d65b98-jcmnt 1/1 Running 0 2m13s +chaos-controller-manager-b8d65b98-tfwfd 1/1 Running 0 2m14s +chaos-daemon-jhth2 1/1 Running 0 2m15s +chaos-dashboard-566b9f5c4b-zmplh 1/1 Running 0 2m15s +chaos-dns-server-85b8846dc9-ksljn 1/1 Running 0 116m ``` --- @@ -175,7 +176,7 @@ For each experiment below, the workflow is: --- -### Experiment 1: Pod Failure +### Experiment 1: Master Pod Failure **What it does:** Marks a Redis pod as unavailable without killing the process. This simulates Kubernetes declaring a pod unhealthy. @@ -183,25 +184,34 @@ For each experiment below, the workflow is: apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: - name: redis-pod-failure + name: rd-master-pod-failure-short namespace: demo spec: action: pod-failure - mode: one - duration: "30s" + mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master + duration: "1m" ``` Observe: ```bash -kubectl get pods -n demo -w -kubectl get redis -n demo redis-cluster +kubectl get rd,rds,rdops,rdsops,pods -n demo +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Ready 39m + +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 10 (3m16s ago) 39m +pod/redis-cluster-shard0-1 1/1 Running 0 39m +pod/redis-cluster-shard1-0 1/1 Running 10 (3m16s ago) 39m +pod/redis-cluster-shard1-1 1/1 Running 0 39m +pod/redis-cluster-shard2-0 1/1 Running 10 (3m16s ago) 39m +pod/redis-cluster-shard2-1 1/1 Running 0 39m ``` One pod will go into a not-ready state. After the 30-second duration, the experiment ends and the pod recovers automatically. @@ -210,13 +220,30 @@ Verify data: ```bash kubectl exec -it -n demo redis-cluster-shard0-0 -- \ - redis-cli -a $PASSWORD -c GET chaos-test + redis-cli -a $PASSWORD -c GET chaos-test + +Defaulted container "redis" out of: redis, redis-init (init) "before-chaos" ``` +markdown +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 11:39:00 | — | Pre-chaos baseline (all 3 master pods ONLINE) | `Ready` | +| 11:44:07 | 0s | `PodChaos` applied (all master pods targeted) | `Ready` | +| 11:44:07 | +0s | All master pods marked unavailable | `NotReady` | +| 11:44:21 | +14s | Operator marks masters unhealthy | `Critical` | +| 11:45:07 | +1m00s | Chaos auto-recovered, master pods restart | `Critical` | +| 11:45:40 | ~+1m33s | Pods in `RECOVERING` (rejoining cluster) | `Critical` | +| ~11:48–52 | ~+4–8m | All masters reach `Running`, cluster re-converges | `Ready` | + +**Result: PASS** — All master pods recovered automatically after the 1-minute fault window. KubeDB reconciled the Redis Cluster back to the desired state with zero data loss. The chaos-impacted pods rejoined the cluster and the test key `chaos-test` remained intact. + --- -### Experiment 2: Pod Kill +### Experiment 2: Pod Kill Master **What it does:** Sends `SIGKILL` to a Redis pod, simulating an OOM kill or a sudden node failure. Unlike pod-failure, the pod process is actually terminated and Kubernetes must reschedule it. @@ -224,34 +251,35 @@ kubectl exec -it -n demo redis-cluster-shard0-0 -- \ apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: - name: redis-pod-kill + name: rd-master-pod-kill-short namespace: demo spec: action: pod-kill - mode: one + mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master + duration: "5m" + gracePeriod: 0 ``` Observe: ```bash -kubectl get pods -n demo -w -``` +kubectl get rd,rds,rdops,rdsops,pods -n demo +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Ready 72m -The killed pod transitions to `Terminating` then gets recreated by the StatefulSet controller. KubeDB reconciles the Redis resource back to the desired state. - -```bash -kubectl get pods -n demo -l app.kubernetes.io/instance=redis-cluster -``` - -``` -NAME READY STATUS RESTARTS AGE -redis-cluster-shard0-0 1/1 Running 1 15m +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 0 6m55s +pod/redis-cluster-shard0-1 1/1 Running 0 72m +pod/redis-cluster-shard1-0 1/1 Running 0 6m55s +pod/redis-cluster-shard1-1 1/1 Running 0 72m +pod/redis-cluster-shard2-0 1/1 Running 0 6m55s +pod/redis-cluster-shard2-1 1/1 Running 0 72m ``` Verify data: @@ -263,6 +291,20 @@ kubectl exec -it -n demo redis-cluster-shard0-0 -- \ "before-chaos" ``` +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 12:14:19 | — | Pre-chaos baseline (all 3 master pods ONLINE) | `Ready` | +| 12:14:19 | 0s | `PodChaos` applied — all master pods SIGKILLed | `Ready` | +| 12:14:19 | +0s | All master pods terminated (`pod-kill` injected for shard0-0, shard1-0, shard2-0) | `Critical` | +| 12:14:30 | ~+11s | Kubernetes reschedules master pods; replicas promoted | `Critical` | +| 12:14:55 | ~+36s | New master pods reach `Running`; cluster re-elects primaries | `Critical` | +| 12:19:19 | +5m00s | Chaos experiment window ends | `Critical` | +| ~12:21:00 | ~+7m | All shards re-converge; cluster topology stabilized | `Ready` | + +**Result: PASS** — All master pods were hard-killed simultaneously. Kubernetes rescheduled them via the StatefulSet controller and KubeDB reconciled the cluster back to the desired state. Replica pods were promoted to masters during the kill window and the test key `chaos-test` remained intact after full recovery. + --- ### Experiment 3: Container Kill @@ -273,29 +315,54 @@ kubectl exec -it -n demo redis-cluster-shard0-0 -- \ apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: - name: redis-container-kill + name: rd-master-container-kill-short namespace: demo spec: action: container-kill - mode: one - containerNames: - - redis + mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master + duration: "5m" + containerNames: ['redis'] ``` Observe: ```bash -kubectl get pods -n demo -w +kubectl get rd,rds,rdops,rdsops,pods -n demo +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Ready 80m + +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 1 (61s ago) 14m +pod/redis-cluster-shard0-1 1/1 Running 0 79m +pod/redis-cluster-shard1-0 1/1 Running 1 (61s ago) 14m +pod/redis-cluster-shard1-1 1/1 Running 0 79m +pod/redis-cluster-shard2-0 1/1 Running 1 (61s ago) 14m +pod/redis-cluster-shard2-1 1/1 Running 0 79m ``` The pod stays alive. Only the `redis` container restarts. Once it comes back up, it rejoins the cluster automatically. +markdown +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 12:27:43 | — | Pre-chaos baseline (all 3 master pods ONLINE) | `Ready` | +| 12:27:43 | 0s | `PodChaos` applied — `redis` container killed in shard0-0, shard1-0, shard2-0 | `Ready` | +| 12:27:43 | +0s | All 3 master containers killed simultaneously (`container-kill` injected) | `Critical` | +| 12:27:44 | ~+1s | Kubernetes detects container exit; kubelet restarts `redis` container in-place | `Critical` | +| 12:28:04 | ~+21s | Containers restart with 1 restart count; pods remain scheduled on same nodes | `Critical` | +| 12:28:44 | ~+1m | Restarted containers rejoin the Redis Cluster; cluster re-converges | `Ready` | +| 12:32:43 | +5m00s | Chaos experiment window ends; no further kills injected | `Ready` | + +**Result: PASS** — All three master containers were killed simultaneously. Kubernetes restarted them in-place (pod identity preserved, no rescheduling needed). KubeDB reconciled the cluster back to the desired state with zero data loss. The test key `chaos-test` remained intact after full recovery. + --- ### Experiment 4: Network Delay @@ -306,7 +373,7 @@ The pod stays alive. Only the `redis` container restarts. Once it comes back up, apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: - name: redis-network-delay + name: delay namespace: demo spec: action: delay @@ -315,54 +382,107 @@ spec: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master delay: - latency: "200ms" - correlation: "25" - jitter: "50ms" + latency: '1000ms' + correlation: '100' + jitter: '50ms' duration: "60s" ``` Observe: ```bash -time kubectl exec -it -n demo redis-cluster-shard0-0 -- \ +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ redis-cli -a $PASSWORD -c PING ``` -Commands that normally complete in under 1ms will now take 200ms or more. After the 60-second window, latency returns to normal. +Commands that normally complete in under 1ms will now take 1000ms or more. After the 60-second window, latency returns to normal. + + +markdown +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| T+0:00 | — | Pre-chaos baseline (all 6 pods ONLINE) | `Ready` | +| T+0:00 | 0s | `NetworkChaos` applied — 1000ms latency injected on all master pods | `Ready` | +| T+0:05 | ~+5s | Redis commands begin timing out; write attempts fail with `context deadline exceeded` | `NotReady` | +| T+0:17 | ~+17s | Cluster marks affected shards unavailable; KubeDB detects degraded state | `NotReady` | +| T+1:00 | +60s | Chaos experiment window ends; latency injection removed | `NotReady` | +| T+3:00 | ~+3m | All shards re-converge; KubeDB reconciles cluster to desired state | `Ready` | + +**Result: PASS** — During the 1000ms latency window, Redis write operations failed with `context deadline exceeded` errors due to cluster heartbeat timeouts exceeding the configured threshold. After the chaos window ended, all pods recovered automatically and KubeDB reconciled the cluster back to `Ready`. Data integrity was confirmed — the test key `chaos-test` remained intact after full recovery. + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +``` --- -### Experiment 5: Network Loss +### Experiment 5: Network Bandwidth -**What it does:** Randomly drops packets for Redis pods. This simulates flaky networks, overloaded switches, or unreliable cloud networking. +markdown +**What it does:** Throttles the network bandwidth available to all Redis pods to 1 Mbps. This simulates a congested or limited network link, such as a cross-region replication scenario or a degraded network interface. ```yaml apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: - name: redis-network-loss + name: bandwidth namespace: demo spec: - action: loss + action: bandwidth mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" - loss: - loss: "50" - correlation: "25" + app.kubernetes.io/name: redises.kubedb.com + bandwidth: + rate: 1mbps + limit: 20971520 + buffer: 10000 duration: "60s" ``` -Observe: +Apply and observe: + +```bash +kubectl get rd,pods -n demo +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c PING +``` + +With only 1 Mbps available, Redis inter-node gossip and replication traffic will compete with client traffic. Large key writes or bulk operations will slow significantly. After the 60-second window, bandwidth returns to normal. + +**Observed timeline:** -Some Redis commands will time out or fail during this window. After the 60-second experiment, the packet loss stops and clients reconnect automatically. Use this experiment to validate your application's retry logic. +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| T+0:00 | — | Pre-chaos baseline (all 6 pods ONLINE) | `Ready` | +| T+0:00 | 0s | `NetworkChaos` applied — 1 Mbps cap injected on all pods | `Ready` | +| T+0:05 | ~+5s | Replication and gossip traffic degraded; write latency increases | `Ready` | +| T+0:30 | ~+30s | Cluster remains functional but throughput is visibly throttled | `Ready` | +| T+1:00 | +60s | Chaos experiment window ends; bandwidth restriction lifted | `Ready` | +| T+1:10 | ~+1m10s | All pods return to full throughput; cluster fully converged | `Ready` | + +**Result: PASS** — The 1 Mbps bandwidth cap throttled replication and gossip traffic across all shards. Redis remained available throughout the experiment with elevated latency on large operations, but the cluster did not lose quorum. After the chaos window ended, all pods returned to normal throughput and the test key `chaos-test` remained intact. + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +``` --- @@ -405,66 +525,163 @@ TCP will detect and retransmit corrupted packets, so most Redis commands will st apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: - name: redis-network-partition + name: partition namespace: demo spec: action: partition - mode: one + mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master direction: both - duration: "30s" + target: + mode: all + selector: + namespaces: + - demo + labelSelectors: + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: slave + duration: "10m" ``` Observe: ```bash -kubectl get redis -n demo redis-cluster -o yaml -kubectl get pods -n demo -w +kubectl get rd,rds,rdops,rdsops,pods -n demo +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Critical 119m + +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 1 (40m ago) 53m +pod/redis-cluster-shard0-1 1/1 Running 0 118m +pod/redis-cluster-shard1-0 1/1 Running 1 (40m ago) 53m +pod/redis-cluster-shard1-1 1/1 Running 0 118m +pod/redis-cluster-shard2-0 1/1 Running 1 (40m ago) 53m +pod/redis-cluster-shard2-1 1/1 Running 0 118m +``` + +The isolated pod is cut off from its peers. Redis Cluster will mark the affected shard as unavailable. Once the 10m window ends, the partition is lifted and the cluster re-converges. + +observe after 10m: + +```bash +kubectl get rd,rds,rdops,rdsops,pods -n demo +NAME VERSION STATUS AGE +redis.kubedb.com/redis-cluster 7.4.0 Ready 126m + +NAME READY STATUS RESTARTS AGE +pod/redis-cluster-shard0-0 1/1 Running 1 (47m ago) 60m +pod/redis-cluster-shard0-1 1/1 Running 0 126m +pod/redis-cluster-shard1-0 1/1 Running 1 (47m ago) 60m +pod/redis-cluster-shard1-1 1/1 Running 0 126m +pod/redis-cluster-shard2-0 1/1 Running 1 (47m ago) 60m +pod/redis-cluster-shard2-1 1/1 Running 0 126m ``` +markdown + +**Observed timeline:** -The isolated pod is cut off from its peers. Redis Cluster will mark the affected shard as unavailable. Once the 30-second window ends, the partition is lifted and the cluster re-converges. +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 13:04:51 | — | Pre-chaos baseline (all 3 master pods ONLINE) | `Ready` | +| 13:04:51 | 0s | `NetworkChaos` applied — bidirectional partition between all masters and all replicas | `Ready` | +| 13:04:52 | ~+1s | All 6 pods partitioned (masters isolated from replicas across all 3 shards) | `Critical` | +| 13:05:10 | ~+20s | Redis Cluster marks affected shards unavailable; KubeDB detects degraded state | `Critical` | +| 13:14:51 | +10m00s | Chaos experiment window ends; partition lifted on all pods | `Critical` | +| ~13:16:00 | ~+11m | All shards re-converge; cluster topology stabilized | `Ready` | +**Result: PASS** — All master pods were bidirectionally partitioned from their replicas simultaneously. During the 10-minute window, the Redis Cluster entered a `Critical` state as shards lost quorum visibility. Once the partition was lifted, all pods automatically re-converged and KubeDB reconciled the cluster back to `Ready`. The test key `chaos-test` remained intact after full recovery. +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +``` --- ### Experiment 8: CPU Stress **What it does:** Saturates the CPU of a Redis pod to simulate CPU-intensive workloads on the same node or a resource-constrained environment. +current CPU usage: + +```bash +kubectl top pods -n demo +NAME CPU(cores) MEMORY(bytes) +redis-cluster-shard0-0 3m 5Mi +redis-cluster-shard0-1 4m 6Mi +redis-cluster-shard1-0 4m 5Mi +redis-cluster-shard1-1 4m 5Mi +redis-cluster-shard2-0 3m 4Mi +redis-cluster-shard2-1 3m 6Mi +``` + ```yaml apiVersion: chaos-mesh.org/v1alpha1 kind: StressChaos metadata: - name: redis-cpu-stress + name: memory-stress-example namespace: demo spec: - mode: one + mode: all selector: namespaces: - demo labelSelectors: - app.kubernetes.io/instance: "redis-cluster" - app.kubernetes.io/name: "redis" + app.kubernetes.io/name: redises.kubedb.com + kubedb.com/role: master + duration: "2m" stressors: cpu: - workers: 2 - load: 100 - duration: "60s" + workers: 4 + load: 50 ``` - -Observe: +Apply and Observe: ```bash kubectl top pods -n demo +NAME CPU(cores) MEMORY(bytes) +redis-cluster-shard0-0 1901m 9Mi +redis-cluster-shard0-1 3m 5Mi +redis-cluster-shard1-0 2007m 9Mi +redis-cluster-shard1-1 3m 6Mi +redis-cluster-shard2-0 1104m 9Mi +redis-cluster-shard2-1 3m 5Mi ``` Redis is single-threaded for command processing, so heavy CPU stress will noticeably increase command latency. After the experiment, CPU usage drops and performance returns to baseline. +markdown +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 13:28:05 | — | Pre-chaos baseline (all 3 master pods ONLINE, ~3–4m CPU) | `Ready` | +| 13:28:05 | 0s | `StressChaos` applied — 4 workers at 50% CPU load injected on all master pods | `Ready` | +| 13:28:05 | +0s | CPU stress injected into shard0-0, shard1-0, shard2-0 | `Ready` | +| 13:28:10 | ~+5s | CPU usage spikes to ~1100–2000m per master pod; command latency increases | `Ready` | +| 13:28:05 | ~+30s | Redis remains available; single-threaded command processing visibly slower | `Ready` | +| 13:30:05 | +2m00s | Chaos experiment window ends; CPU stress removed from all master pods | `Ready` | +| 13:30:10 | ~+2m05s | CPU usage returns to baseline (~3–4m); cluster fully converged | `Ready` | + +**Result: PASS** — CPU stress raised master pod usage from ~3–4m to ~1100–2000m cores. Redis remained available throughout the experiment, but command latency increased due to CPU contention. After the 2-minute chaos window ended, all pods recovered to baseline CPU usage automatically. The test key `chaos-test` remained intact. + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +``` + --- ### Experiment 9: Memory Stress @@ -496,11 +713,42 @@ Observe: ```bash kubectl top pods -n demo -kubectl describe pod -n demo redis-cluster-shard0-0 | grep -A5 "Limits\|Requests" +NAME CPU(cores) MEMORY(bytes) +redis-cluster-shard0-0 3m 5Mi +redis-cluster-shard0-1 4m 6Mi +redis-cluster-shard1-0 4m 252Mi +redis-cluster-shard1-1 4m 5Mi +redis-cluster-shard2-0 3m 4Mi +redis-cluster-shard2-1 3m 5Mi ``` Watch whether Redis begins evicting keys (depending on your `maxmemory-policy` setting) and whether it recovers cleanly once the stressor is removed. +markdown +**Observed timeline:** + +| Wall-clock | Δ from chaos | Event | DB Status | +|---|---|---|---| +| 14:04:17 | — | Pre-chaos baseline (all master pods ONLINE, ~5–6Mi memory) | `Ready` | +| 14:04:17 | 0s | `StressChaos` applied — 2 workers allocating 256MB injected on one master pod | `Ready` | +| 14:04:17 | +0s | Memory stress injected into `redis-cluster-shard1-0` | `Ready` | +| 14:04:22 | ~+5s | Memory usage on `shard1-0` spikes to ~252Mi; Redis under pressure | `Ready` | +| 14:04:47 | ~+30s | Redis remains available; no OOM kill triggered within limits | `Ready` | +| 14:05:17 | +1m00s | Chaos experiment window ends; memory stress removed from pod | `Ready` | +| 14:05:20 | ~+1m03s | Memory usage returns to baseline (~5Mi); cluster fully converged | `Ready` | + +**Result: PASS** — Memory stress raised `shard1-0` usage from ~5Mi to ~252Mi. Redis remained available throughout the experiment and no OOM kill was triggered. After the 60-second chaos window ended, the stressor was removed and memory returned to baseline automatically. The test key `chaos-test` remained intact. + +Verify data: + +```bash +kubectl exec -it -n demo redis-cluster-shard0-0 -- \ + redis-cli -a $PASSWORD -c GET chaos-test + +"before-chaos" +` `` +``` + --- ### Experiment 10: I/O Chaos