← Back to Projects

Unblocking Batch Jobs: An Admission Webhook Bottleneck

Cutting cluster-wide Job start latency from minutes to seconds by finding a throttled sidecar-injection webhook

Platform: AKS (250+ nodes)
Workload: KEDA-driven batch Jobs
Type: Performance incident
Role: Investigator & fix owner

Overview

Teams across the cluster reported the same thing: batch Jobs (crawlers, renderers, scheduled tasks) were taking several minutes to even start, when they normally started in seconds. Working with cloud provider support, we traced it to a mutating admission webhook running as a single, rate-limited replica. I delivered the fix through every environment and found the extra step it needed to actually take effect.

The Symptoms

How Admission Works

Every pod create passes through mutating admission webhooks before the scheduler ever sees it. If one webhook is slow, every controller that creates pods queues behind it, and the Job controller has only a handful of workers by default.

KEDAScaledJobJob controllerfew workersAPI serveradmission chainSidecar injector1 replica, rate-limitedPod created→ scheduler
The single injector replica capped admissions for the whole cluster

Investigation

  1. Measure precisely. I computed Job → pod and Succeeded → Complete deltas per namespace from Kubernetes telemetry. The lag was the same for every team, which points at the platform rather than any one workload.
  2. Separate the noise. One node pool was also at its autoscaler maximum. That was real, but it couldn't explain delays for pods that hadn't been created yet.
  3. Escalate with evidence. A support case with the timeline and churn numbers led the provider to the injector. Its API client was rate-limited to a few requests per second, so each pod create took around two seconds of admission time.
  4. Correct the timeline. The delay had also appeared on earlier days. Low weekend load had hidden it.

The Fix

  1. Scaled the sidecar injector from one replica to two through a reviewed change to the cluster core-services repo.
  2. Verified in dev and test, including that traffic was split across both replicas.
  3. Shipped to production under an emergency change.
  4. The extra step: after the production rollout, the delay only halved. The API servers kept their long-lived connections pinned to the original pod. A rolling restart of the injector forced them to reconnect and spread the load.
Force rebalancing
# After scaling ANY admission webhook, make the API servers rebalance
kubectl -n <injector-namespace> rollout restart deploy/<sidecar-injector>
kubectl -n <injector-namespace> rollout status  deploy/<sidecar-injector>
Follow-up

Add an objectSelector to the webhook so only pods that actually need a sidecar are sent to it.

Key Outcomes

minutes → seconds
Job start latency (p50)
~35%
drop in the live Job backlog within minutes
< 1 day
from investigation to verified fix

Lessons Learned

Conclusion

One replica of one webhook was quietly throttling an entire cluster. Measuring precisely, escalating with evidence and checking the result in production turned a vague "everything is slow" into a seconds-level fix.

Technologies Used

AKSKubernetes Job ControllerMutating Admission WebhooksSidecar InjectionKEDAHelmAPM / Kubernetes TelemetryAzure DevOps

About this case study: this write-up is drawn from my hands-on platform engineering experience. Organisation names, system names, figures and timelines have been changed or generalised, and all code, configuration and queries are illustrative examples written for this page.

← Debugging a Managed CoreDNS RegressionDesigning for Node Failure on AKS →
← Back to Projects Get in Touch