Cases4 cases
1 to 4 of 4
IL-0029Slow user-facing services from CPU throttling on containers that used far less CPU than their limitBuffer2020- Symptoms
Services on Buffer's kops-managed cluster on AWS (about 60 nodes and 1500 containers) answered more slowly than they should have. The container throttling metric showed that many containers were being throttled whatever their CPU usage was, including containers that never came close to their CPU limit.
- Trigger
CPU limits on user-facing services, enforced through CFS quota by a Linux kernel with a bug that throttled containers even when they were well under their limit.
- Root cause
- Component
- Contributing factor
- Detection
While reviewing the container throttling metric, the team noticed many containers being throttled even though their CPU usage stayed far below their limits.
- Remediation
- removed CPU limits from all services directly or indirectly on the users' critical path
- moved those services to dedicated tainted nodes and gave them matching tolerations
- set each service's CPU request to its peak usage over a few months plus a margin of more than 20%, and set memory requests the same way
- Action items
- Move nodes to a kernel that contains the CFS throttling fix (4.19 or later), which for Buffer means a kops version whose host image is Ubuntu 20.04
- After the kernel upgrade, check whether throttling is fully gone and update the post
- Lessons
A CPU limit can slow a service down even when the service uses a small part of it, because of how the kernel enforces CFS quota on older kernels; the throttling metric shows it, the CPU usage graph does not. Upgrading the kernel is the safer fix. Removing limits works for latency, but only together with honest CPU and memory requests, isolation of the unbounded services, and monitoring of node usage, since unbounded containers can starve the kubelet.
IL-0012CPU limits and an unpatched kernel CFS quota bug throttled multi-threaded services into stalls, failed health checks and timeoutsOmio2020- Symptoms
For about six months, applications on Omio's Google Kubernetes Engine clusters stalled at random: they got stuck, stopped answering health check requests or lost network connections, and nothing the team looked at explained why.
- Trigger
Multi-threaded workloads running with CPU limits on nodes whose kernel did not yet have the fix for a known CFS quota bug that causes unnecessary throttling.
- Root cause
- Component
- Contributing factor
- Detection
Months of random stalls and failed health checks led the team to read the cgroup cpu.stat throttling counters inside the pods, which showed far more throttling than the limits should allow.
- Remediation
- removed CPU limits until the kernel with the CFS bug fix was deployed across all clusters
- Action items
- Deploy the kernel with the CFS quota bug fix across all clusters
- Monitor utilisation, saturation and error metrics, API latency and error rates to confirm the change behaves as expected
- Lessons
A CPU limit is a time quota per 100 ms period, not a share of cores, so a multi-threaded container can be stopped for most of each period long before it uses the cores it was given, and a kernel bug can make that worse. Throttling is invisible in standard Kubernetes metrics and has to be read from the cgroup counters. Removing limits trades away protection against a container that takes a whole node, which is only acceptable when runtimes are tuned.
IL-0057Higher p99 latency after moving a reverse proxy to EKS, from outgoing connections delayed by a SNAT conntrack racePrezi2019- Symptoms
Prometheus alerts fired on tail latency: from 7/24, the p99 response time of a few services went up after Prezi moved its Nginx-based reverse proxy, smartgateway, from dedicated Elastic Beanstalk machines onto shared Kubernetes worker nodes. The bump affected only the tail, so the earlier monitoring of average response times would most certainly have missed it.
- Trigger
Moving the reverse proxy onto shared EKS worker nodes, where its outgoing connections went through source NAT on the node and some SYN packets were dropped by a race in Netfilter's conntrack.
- Root cause
- Component
- Contributing factor
- Detection
Prometheus alerts on p99 latency fired after the reverse proxy migration.
- Remediation
- added AWS X-Ray tracing and an $upstream_connect_time trace attribute to locate the delay
- ran more pods of the service on more nodes, which reduced but did not end the delays
- set AWS_VPC_K8S_CNI_EXTERNALSNAT=true on the AWS VPC CNI to turn off SNAT on the nodes
- Lessons
Alerting on tail latency instead of averages caught a problem that would otherwise have gone unnoticed, and tracing turned a vague slowdown into a measured connect delay. Delays of exactly 1 or 3 seconds on new outgoing connections point to dropped SYN packets, and on EKS the SNAT done by the VPC CNI is where to look.
IL-0047Pod-to-pod requests delayed by 5, 10 or 15 seconds because of DNS lookups lost to a conntrack raceMindTickle2019- Symptoms
Pod-to-pod calls were intermittently slow and the web application felt slow overall. A request that pod A sent at time x reached pod B at x + 5s, x + 10s or x + 15s, always a multiple of five seconds.
- Trigger
The C library resolver sent the A and AAAA queries for each lookup on the same port; where DNAT or SNAT translation takes place, a race condition in conntrack sometimes left one of the queries unanswered, and the resolver waited 5 seconds before retrying.
- Root cause
- Component
- Contributing factor
- Detection
The team noticed intermittent delays in pod-to-pod calls and slowness in the web application, and that the delays were always multiples of five seconds.
- Remediation
- added the single-request-reopen option to dnsConfig in the deployment specs
- set GRPC_DNS_RESOLVER=native in the Dockerfiles of gRPC applications so they used the libc resolver
- checked with tcpdump port 53 that A and AAAA queries left from different ports
- Lessons
Delays that are exact multiples of five seconds point to DNS lookups being retried after the resolver's 5-second timeout. A resolver option set in the pod spec only helps processes that use the libc resolver; runtimes with their own resolver, such as gRPC with c-ares, ignore it.
These 4 cases involve Linux kernel and conntrack table. kubegap audits a Kubernetes cluster for gaps like these, read-only, from Claude Code or Codex; its checks cite public reports like the ones in this library.
By root cause2
Software defect2 cases
DNS or network fault2 cases
- Component
- Contributing factor
- Organisation