Incident Library
  • Root cause
  • Component
  • Symptom
  • Contributing factor
  • Case

Cases6 cases

3 to 6 of 6

  1. Symptoms

    After Ravelin moved its API layer from VMs into its GKE cluster behind a Kubernetes Ingress, the integration tests occasionally received 502 errors.

    Trigger

    Pods behind the Ingress being removed by their controller, as happens during rolling deploys and scaling.

    Root cause
    Component
    Contributing factor
    Detection

    Integration tests occasionally received 502 errors.

    Lessons

    Removing a pod's endpoint and signalling the pod happen at the same time, and an ingress can take a long time to catch up. A pod behind an ingress should keep serving after SIGTERM until its grace period ends, and the grace period should be longer than the load balancer's reprogramming time.

  2. Symptoms

    On a Sunday, a team using one of Skyscanner's Kubernetes clusters manually paged the out-of-hours Kubernetes support because their service showed elevated latency, worst at the p90 and maximum levels.

    Trigger

    Upgrading the HAProxy ingress controller to v0.6 and going back from Service virtual IPs to individual pod IPs as backends, on the understanding that the sort-backends flag introduced in v0.5, false by default, meant backends were randomly shuffled.

    Root cause
    Component
    Contributing factor
    Detection

    A cluster user manually paged the out-of-hours Kubernetes support about elevated latency; Prometheus queries then showed the uneven request distribution across pods.

    Remediation
    • switched the affected service from pod IPs back to the Service virtual IP through a config change in the internal deployment tool Slingshot
    • patched the controller to keep each backend's servers in an insertion-ordered slice and changed the template to iterate over that slice
    • tested the patch in sandbox clusters and rolled it out to the production clusters
    Action items
    • Run the internal controller patch in production and return to pod IPs as the default backends
    • Raise the problem upstream with a GitHub issue and pull request
    • Test changes to cluster components against the specific behaviour and conditions that matter
    Lessons

    The team already knew that sorted backends cause hot-podding and still did not test the new flag well enough to see that it did not work; changes to cluster components need tests aimed at the exact behaviour that matters, under conditions like production. A keyword can behave differently in different parts of a language: range over a map is unordered in Go code but sorted in text/template. A small cluster component can do outsized harm to every user of the cluster and, in the end, to customers.

  3. Symptoms

    On Friday, January 18, Moonlight's API began returning occasional errors that looked like connection problems with its Redis database. The API checks Redis on every authenticated request to validate the session. At the same time the Kubernetes monitoring reported some nodes and pods as unresponsive.

    Trigger

    The Kubernetes scheduler put several CPU-heavy pods on the same node, including all three replicas of the website and the server-side rendering pods.

    Root cause
    Component
    Contributing factor
    Detection

    API errors that looked like Redis connectivity problems, and Kubernetes monitoring that showed nodes and pods as unresponsive. Google Cloud Support later found the link between sustained 100% CPU and kernel panics.

    Remediation
    • added anti-affinity rules to all major deployments so their pods spread across nodes
    Action items
    • Keep working with Google Cloud support to find and fix the cause of the node kernel panics
    Lessons

    Three replicas give no fault tolerance if the scheduler places them all on one node; anti-affinity rules make the spread explicit. A node failure that comes from the workload itself moves with the workload when the scheduler reschedules it.

  4. Symptoms

    Services that used the team's Redis-dependent library crashed on start-up, and their teams reported it to the ops members. The Spring Boot logs showed the applications failing to resolve the Redis hostname during bean initialisation.

    Root cause
    Component
    Contributing factor
    Detection

    Service teams reported start-up crashes to the ops members of the team; later the resolution failures were seen in Kibana.

    Remediation
    • set ndots to 4 with a dnsConfig block in the Kubernetes deployment manifests
    • corrected the maximum connection pool size in the Redis library, cutting Redis connections by about 70%
    Action items
    • Test Debian glibc-based base images added to the image library to see whether they resolve the DNS problems
    • Update dependencies to newer Netty versions
    Lessons

    With ndots:5, a name outside the cluster that has fewer than five dots is first tried with every cluster search domain. Adding a trailing dot avoids that, but only if every library in the path accepts it, and the Netty version in use did not. The ndots change is a workaround that will not hold forever. The author would like better alerting for this kind of failure but notes that alerts on one-off DNS failures are not wanted.

Check your cluster for this

These 6 cases involve Ingress controller and HAProxy. 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 cause4

Configuration change1 case

Organisation
  1. IL-0177 WebSocket connections to LINE's messaging-hub on Kubernetes dropped all at once whenever another service's change reloaded a shared HAProxy L7 load balancer, and the reconnect wave overloaded a token API and filled nf_conntrack on Nginx
    LINE+2022

DNS or network fault1 case

Component
Organisation
  1. IL-0056 Calls from EKS pods to services behind an NLB in another VPC failed intermittently because the AWS VPC CNI did source NAT on the node

Software defect3 cases

  1. IL-0062 Intermittent 502 errors behind a GKE Ingress because pods shut down before the load balancer stopped sending them traffic
  2. IL-0011 HAProxy ingress piled requests onto the same pods because a Go template visited the backend map in sorted pod IP order
  3. IL-0050 Pods on Kubernetes failed to resolve an external Redis hostname because of ndots:5, the musl resolver in Alpine images, and Netty's DNS resolver

Capacity exhaustion1 case

Organisation
  1. IL-0064 CPU-heavy pods packed onto one Google Cloud node drove it to kernel panics, and the crash moved from node to node until the site was down