RabbitMQ pod eviction during CI/CD pipeline load testing
Problem
During nightly CI/CD pipeline runs the RabbitMQ StatefulSet is repeatedly evicted by the kube‑scheduler. The eviction manifests as a FailedScheduling event and the pod enters Terminating followed by a recreation of the pod with a new name. The pipeline then fails with connection timeouts and lost messages.
Typical symptoms observed in the pipeline logs:
[INFO] Starting load test: publishing 10k messages per second
[ERROR] Connection to rabbitmq-0.rabbitmq.default.svc:5672 failed: EOF
[ERROR] pod rabbitmq-0 evicted: memory pressure
Cluster‑wide alerts:
KubePodEvicted{pod="rabbitmq-0"} 1
node_memory_pressure{node="worker-1"} 1
Root Cause
The StatefulSet runs with default resource requests (CPU = 100m, memory = 128Mi) and no limits. During load testing the broker processes a surge of publish‑confirm traffic that spikes resident set size (RSS) well beyond the node’s allocatable memory. The kubelet’s eviction_manager detects node‑level memory pressure and evicts the highest‑priority pod that does not have a PodDisruptionBudget protecting it – in this case the RabbitMQ pod.
Two contributing factors:
- Insufficient resource requests/limits: The scheduler assumes the pod only needs 128 MiB, so it places it on a node already near capacity.
- Absence of QoS guarantees: Without a memory
limit, the container can consume all available memory, triggering node‑level pressure.
Because the pod is stateful, eviction forces a restart, causing the cluster to re‑elect a leader and lose in‑flight messages.
Debug
Step‑by‑step investigation performed during the failure window:
- Inspect the eviction event:
kubectl describe pod rabbitmq-0 | grep -A3 "Events"
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning EvictionThresholdMet 2m kubelet,worker-1 Pod rabbitmq-0 evicted due to memory pressure
- Check node memory pressure metrics:
kubectl top node worker-1
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 1800m 90% 30Gi / 32Gi 93%
- Examine RabbitMQ process memory usage during the test:
kubectl exec -it rabbitmq-0 -- bash -c "ps -o pid,rss,cmd -C beam.smp"
PID RSS CMD
12 12456 /usr/lib/rabbitmq/bin/beam.smp -kernel inet_dist_listen_min 25672 ...
- Review the StatefulSet manifest for resource specifications:
kubectl get statefulset rabbitmq -o yaml | grep -A5 resources
resources:
requests:
cpu: "100m"
memory: "128Mi"
- Capture a short packet trace to confirm message burst size:
kubectl exec -n default rabbitmq-0 -- tcpdump -i any -s 0 -w /tmp/trace.pcap port 5672 &
sleep 30
kill %1
kubectl cp rabbitmq-0:/tmp/trace.pcap ./trace.pcap
Analysis of the pcap showed a sustained 10k msgs/s publish rate during the test.
Solution
Apply proper resource guarantees and protect the StatefulSet with a PodDisruptionBudget. The following changes were made:
Before
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
spec:
serviceName: rabbitmq
replicas: 3
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3.11-management
resources:
requests:
cpu: "100m"
memory: "128Mi"
After – add limits, higher requests, and a PDB
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: rabbitmq-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: rabbitmq
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
spec:
serviceName: rabbitmq
replicas: 3
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3.11-management
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
env:
- name: RABBITMQ_VM_MEMORY_HIGH_WATERMARK
value: "0.75" # 75 % of the container memory
Key changes:
- Requests increased to 500 m CPU and 1 GiB memory, ensuring the scheduler places the pod on a node with sufficient headroom.
- Limits added to cap memory at 2 GiB, preventing a single broker from exhausting node memory.
- VM memory high watermark set to 75 % of the container limit, causing RabbitMQ to apply flow control before the OS OOM killer intervenes.
- PodDisruptionBudget guarantees at least two replicas remain available, so an eviction would be blocked unless the entire StatefulSet is scaled down.
Verify
After applying the updated manifest:
kubectl apply -f rabbitmq-statefulset.yaml
kubectl rollout status statefulset/rabbitmq
Run the same load test and observe:
kubectl logs -f rabbitmq-0 | grep "memory"
2026-06-13 12:34:10.123 [info] Memory usage: 1.4GiB / 2GiB (70%)
Node pressure metrics return to normal:
kubectl top node worker-1
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 1200m 60% 20Gi / 32Gi 62%
No further EvictionThresholdMet events appear in kubectl get events during the test run.
Prevent
- Capacity planning: Benchmark expected message rates and size, then size
requestsaccordingly. - Memory high watermark tuning: Set
RABBITMQ_VM_MEMORY_HIGH_WATERMARKto 0.6‑0.8 of the container limit to trigger broker‑level flow control before node pressure. - Horizontal scaling: Deploy additional RabbitMQ replicas or a separate queue per test suite to distribute load.
- Monitoring & alerts: Add Prometheus alerts on
rabbitmq_mem_usedand nodememory_pressureto catch early spikes. - CI/CD throttling: Limit concurrent load‑test jobs that target the same RabbitMQ cluster, or run them against an isolated test namespace.
FAQ
- Why does the pod get evicted even though I set
requests? The original manifest only set a very low request (128 MiB). The scheduler placed the pod on a node already near capacity, so when the broker’s memory grew, node pressure triggered eviction. - Can I rely on RabbitMQ’s internal flow control without setting a memory limit? No. Flow control only activates relative to the container’s memory limit. Without a limit, the broker cannot calculate a high‑watermark, and the OS may kill the process before RabbitMQ throttles.
- Do I need to adjust the Kubernetes QoS class? By providing both
requestsandlimits, the pod receives aGuaranteedQoS class, which protects it from being the first victim of node‑level eviction. - How can I test the new configuration before committing to CI? Deploy the updated StatefulSet to a staging namespace and run a synthetic load generator (e.g.,
rabbitmq-perf-test) at the expected peak rate, monitoringrabbitmq_mem_usedand node memory. - Is a PodDisruptionBudget enough to stop evictions? A PDB prevents voluntary disruptions (e.g., rolling updates) but does not block forced evictions due to node pressure. Proper resource sizing and limits are still required.
Related Topic Hub: Data Infrastructure Troubleshooting Hub