Problem
The AI inference microservice runs on GPU‑enabled nodes in a Kubernetes cluster and consumes a high‑throughput Kafka topic. After a blue‑green deployment rollout, the new (green) pods start reporting CUDA out of memory (OOM) errors, causing request failures and a cascade of consumer lag in Kafka.
Typical log excerpt from a failing pod:
2026-09-28 14:32:07,123 ERROR inference_worker.py:210 - CUDA error: out of memory
Traceback (most recent call last):
File "/app/inference_worker.py", line 198, in run_batch
outputs = model(input_tensor)
File "/usr/local/lib/python3.9/site-packages/torch/cuda/__init__.py", line 123, in _lazy_init
torch._C._cuda_init()
RuntimeError: CUDA out of memory. Tried to allocate 2.00 GiB (GPU 0; 11.00 GiB total capacity; 9.45 GiB already allocated; 0.12 GiB free; 9.55 GiB reserved in total)
Impact includes:
- Increased end‑to‑end latency (seconds per inference).
- Kafka consumer group lag spikes (>10 minutes).
- Pod restarts due to OOM kill (observed in
kubectl get pods).
Root Cause
The blue‑green rollout introduced two changes that together exhausted GPU memory:
- Increased consumer concurrency. The green deployment raised
max.poll.recordsfrom 500 to 2000 and added three additional consumer threads per pod to improve throughput. Each thread creates its own model instance (due to a lazy‑load pattern), resulting in three copies of the model residing simultaneously on the same GPU. - Missing GPU memory limits. The original (blue) deployment relied on the default
limits: { nvidia.com/gpu: 1 }without an explicitmemorylimit. The green pod spec added arequestsentry but omittedlimits.memory, allowing the container runtime to allocate beyond the GPU’s physical memory.
Combined, the three model copies (≈3 GiB each) plus the input batch buffers exceeded the 11 GiB GPU capacity, triggering the OOM error.
Debug
1. Verify pod status and OOM events
kubectl get pods -n ai-inference -l app=inference -o wide
kubectl describe pod inference-green-5d9f8c9c9-7kz9l -n ai-inference
# Look for OOMKilled in the Events section
2. Inspect GPU utilization inside the pod
kubectl exec -it inference-green-5d9f8c9c9-7kz9l -n ai-inference -- nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.85.12 Driver Version: 525.85.12 CUDA Version: 12.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | |
| 0 Tesla V100 Off | 00000000:3B:00.0 Off | 0 |
| N/A 32C P0 45W / 250W | 9500MiB / 16160MiB | 78% Default |
+-----------------------------------------------------------------------------+
3. Correlate consumer configuration with model loading
# Inference service config (ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: inference-config
data:
KAFKA_MAX_POLL_RECORDS: "2000"
CONSUMER_THREADS: "3"
MODEL_PATH: "/models/resnet50.pt"
4. Check for multiple model instances
# Grep for torch.load calls in the container image
kubectl exec -it inference-green-5d9f8c9c9-7kz9l -n ai-inference -- grep -R "torch.load" -n /app
5. Review deployment manifest differences (blue vs. green)
| Field | Blue (working) | Green (failing) |
|---|---|---|
| resources.requests.cpu | 2 | 2 |
| resources.limits.cpu | 4 | 4 |
| resources.requests.memory | 8Gi | 8Gi |
| resources.limits.memory | 12Gi | 8Gi (missing) |
| resources.limits.nvidia.com/gpu | 1 | 1 |
| env.KAFKA_MAX_POLL_RECORDS | 500 | 2000 |
| env.CONSUMER_THREADS | 1 | 3 |
Solution
1. Enforce a single model instance per GPU
Refactor the inference worker to load the model once at process start and share it across consumer threads.
Before (lazy load per thread):
def worker_thread():
# Each thread loads the model independently
model = torch.load(os.getenv("MODEL_PATH")).to('cuda')
while True:
batch = consumer.poll()
run_inference(model, batch)
After (singleton model):
# Global model loaded once
model = torch.load(os.getenv("MODEL_PATH")).to('cuda')
model.eval()
model_lock = threading.Lock()
def worker_thread():
while True:
batch = consumer.poll()
with model_lock:
outputs = model(batch)
# post‑process outputs
2. Add explicit memory limits and enable nvidia.com/gpu memory enforcement
Update the deployment spec to set limits.memory and use the NVIDIA device plugin’s memory resource (if supported) or a initContainer that pre‑allocates a memory pool.
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-green
spec:
replicas: 3
selector:
matchLabels:
app: inference
template:
metadata:
labels:
app: inference
spec:
containers:
- name: inference
image: registry.example.com/inference:green
resources:
requests:
cpu: "2"
memory: "8Gi"
nvidia.com/gpu: "1"
limits:
cpu: "4"
memory: "12Gi"
nvidia.com/gpu: "1"
env:
- name: KAFKA_MAX_POLL_RECORDS
value: "500"
- name: CONSUMER_THREADS
value: "1"
3. Reduce Kafka poll size and throttle consumer throughput
Revert KAFKA_MAX_POLL_RECORDS to a value that keeps the per‑batch GPU memory footprint below 4 GiB, and configure max.poll.interval.ms to allow back‑pressure.
4. Implement graceful shutdown during blue‑green switch
Ensure the green pods finish processing in‑flight batches before termination:
# Add preStop hook
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "python /app/drain.py && sleep 30"]
Verify
- Confirm single model instance:
kubectl exec -it inference-green-5d9f8c9c9-7kz9l -n ai-inference -- ps -ef | grep python # Should show only one python process per pod - Check GPU memory after rollout:
kubectl exec -it inference-green-5d9f8c9c9-7kz9l -n ai-inference -- nvidia-smi # Expect Memory-Usage around 4–5GiB, free > 6GiB - Validate Kafka lag:
kubectl exec -it kafka-0 -n kafka -- kafka-consumer-groups.sh \ --bootstrap-server localhost:9092 \ --describe --group inference-consumer # Lag should be near zero after a few minutes - Monitor pod restarts:
kubectl get pods -n ai-inference -l app=inference -w # No OOMKilled events for at least 30 minutes
Prevent
- Resource Guardrails: Enforce
limits.memoryandlimits.nvidia.com/gpuvia aLimitRangepolicy. - GPU Memory Metrics: Export
nvidia_smi_memory_used_bytesto Prometheus and set alerts when usage exceeds 80 % of capacity. - Canary with Throttled Throughput: Deploy green pods with a reduced
KAFKA_MAX_POLL_RECORDSand a lowerCONSUMER_THREADScount; only increase after confirming stable memory usage. - Model Loading Guard: Use a singleton pattern or a dedicated model server (e.g., Triton Inference Server) to avoid accidental multiple copies.
- Blue‑Green Coordination: Add a post‑deployment hook that checks GPU memory before scaling green pods to full replica count.
Related Topic Hub: Data Infrastructure Troubleshooting Hub
FAQ
- Why did the OOM only appear after the blue‑green rollout? The green version increased both the number of consumer threads and the Kafka poll size, causing multiple model instances to be loaded concurrently. The original blue deployment used a single thread and a smaller poll size, keeping GPU usage within limits.
- Can I keep multiple consumer threads without OOM? Yes, but you must share a single model instance across threads (e.g., using a global model with a lock) or allocate additional GPUs per pod. Avoid loading the model per thread.
- Is there a way to limit GPU memory per container? The NVIDIA device plugin supports
nvidia.com/gpucount limits. For finer‑grained memory control, useCUDA_VISIBLE_DEVICESwithgpu_memory_limitin recent driver versions or run a memory‑capped container runtime such asrunscwith--memoryflags. - How should I size
KAFKA_MAX_POLL_RECORDSfor GPU inference? Estimate the per‑record GPU memory (model size + input tensor). Multiply by the desired batch size and keep the total below ~70 % of the GPU capacity to leave headroom for framework overhead. - What monitoring alerts are most useful for early detection? Alert on:
- GPU memory usage > 80 % (Prometheus query on
nvidia_smi_memory_used_bytes). - Pod OOMKilled events.
- Kafka consumer lag > 5 minutes.
- Sudden increase in
process_resident_memory_bytesfor the inference container.
- GPU memory usage > 80 % (Prometheus query on