Making GPU Failure Invisible: A Zero-Cost Fallback for LLM Inference on Kubernetes
How three things Kubernetes already does for free replaced a failover system I didn't want to write

Search for a command to run...
How three things Kubernetes already does for free replaced a failover system I didn't want to write

No comments yet. Be the first to comment.
In this series, I will help you uncover tips and tricks within AWS. The tips shared will be beneficial for you to start your cloud or hacking journey!
Read more (Technical): https://blog.raeveen.dev/serverless-mcp-on-aws-lambda-using-go The Model Context Protocol (MCP) is rapidly becoming the "USB-C for AI," allowing LLMs to seamlessly interface with local and remote tools. However, most MCP implem...
The way developers write code has vastly changed. AI coding agents with tools like Claude Code, Gemini CLI, GitHub Copilot CLI, and Codex are no longer novelties. They're production tools. Over a quar
Read more (Technical): https://blog.raeveen.dev/serverless-mcp-on-aws-lambda-using-go The Model Context Protocol (MCP) is rapidly becoming the "USB-C for AI," allowing LLMs to seamlessly interface with local and remote tools. However, most MCP implem...

Growth vs. Savings. Gain++

In life, there are few bodily processes that happen automatically: Your hair grows, or you breathing, without having a thought crossing your mind. But pretty much everything else you do in daily life requires thinking. However, you often rely on thou...

I will be giving a formal talk on this implementation soon. More to come in a highly deep dive technical blog followed with the talk
Most GPU-aware routing demos assume you have GPUs. I wanted to know what happens when you don't, on purpose, and whether a cluster could degrade from "canary 90% GPU, 10% CPU" down to "100% CPU" without a single line of failover code.
It can. The trick is refusing to write any failover code at all, and letting three things Kubernetes already does for free do the work instead.
The routing layer is the Kubernetes Gateway API Inference Extension running behind NGINX Gateway Fabric: a Gateway, an HTTPRoute with a weighted canary split, and two InferencePools, one GPU-backed, one CPU-backed, each fronting an Ollama deployment serving Llama 3.
Nothing unusual yet. The interesting part is what happens when the GPU side has nowhere to run.
The GPU deployment requests nvidia.com/gpu: 1. On a cluster with no GPU nodes and no device plugin, that pod never leaves Pending. No crash loop, no error to catch, nothing to alert on. It just never becomes Ready, which means the GPU InferencePool has zero healthy endpoints. The Gateway routes 90% of traffic at a pool with nothing behind it, and every one of those requests would 502.
That's the gap layer two closes.
A small CronJob polls allocatable nvidia.com/gpu across all nodes every two minutes and patches the HTTPRoute accordingly:
GPU_CAPACITY=$(kubectl get nodes -o json \
| jq '[.items[].status.allocatable["nvidia.com/gpu"] // "0" | tonumber] | add')
if [ "${GPU_CAPACITY}" -gt 0 ]; then
WEIGHTS='{"gpu": 90, "cpu": 10}'
else
WEIGHTS='{"gpu": 0, "cpu": 100}'
fi
# ...JSON patch the HTTPRoute backendRefs with $WEIGHTS
No GPU capacity, no traffic sent to it. The RBAC for this is deliberately narrow: read-only on nodes, patch on exactly one HTTPRoute. If a GPU node group shows up later (say, a Karpenter NodePool reacting to those Pending pods), the same CronJob notices on its next run and shifts weight back without anyone touching a manifest.
Two primitives, scheduling and a five-line reconciliation loop, and the failure mode disappears entirely.
Building the demo took an afternoon. Making it survive contact with a real, deliberately locked-down VPC took considerably longer, and that's where most of the actual engineering happened.
The target EKS cluster runs an IPv6-only Service CIDR with no NAT gateway for IPv4 egress by default, just an egress-only path. That single constraint cascades:
Image pulls needed rethinking, since neither ghcr.io nor Docker Hub are reachable from a node with no IPv4 route out and no AAAA record on the registry. Private ECR, reached over existing VPC endpoints, became the only viable pull path.
The Gateway's own load balancer turned out to be IPv6-only by inheritance from the cluster's Service CIDR, which AWS's load balancer controller doesn't handle with its defaults; an NLB provisioned the normal way simply refuses to attach. Getting a healthy, publicly reachable target group meant working through target-registration mode, address-type, and scheme, in that order, each one surfacing a different opaque error only after the previous one was fixed.
A few of AWS's own background health checks (unrelated to the actual routing path) turned out to be silently blocking every reconcile for minutes at a time in an environment where their upstream APIs weren't reachable. No error, no timeout message, just nothing happening.
None of that shows up in a diagram. All of it is the difference between "works in a demo cluster" and "works."
The interesting engineering here isn't the YAML, it's the constraint-driven debugging such as reading past a generic AWS error string to the actual API call failing underneath it, working out which of several plausible IPv6/dual-stack interactions was the real blocker, and doing it without shell access being the bottleneck, since most of this had to be diagnosed from controller logs and AWS console state alone.
I'm holding the full repo and the complete list of fixes for a talk I'm giving on this soon, diagrams, root causes, and all. If distributed inference routing or debugging Kubernetes on non-standard networking is something you deal with, I'd love to compare notes in the meantime.