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

Cases7 cases

4 to 7 of 7

  1. Symptoms

    After a Spring-based JVM microservice was deployed to Kubernetes and part of production traffic was routed to it, request latency there was up to 10 times higher than on EC2. Behind the NGINX ingress controller the backend took 100 to 200 ms to answer, where the EC2 instances took about 20 ms.

    Trigger

    Moving the service onto the Kubernetes platform, where KIAM served IAM credentials to pods with its default 15-minute lifetime.

    Root cause
    Component
    Contributing factor
    Detection

    After part of production traffic was routed to the Kubernetes deployment, its request latency was seen to be up to 10 times higher than on EC2.

    Remediation
    • reconfigured KIAM to request IAM credentials with a longer expiration period
    Lessons

    KIAM's short credential lifetime for pods and the AWS Java SDK's eager refresh of any credential within 15 minutes of expiry are each reasonable, but together they force a metadata round trip on every request. When latency rises after a move to Kubernetes, the cause is often the interaction of components that never ran together before rather than Kubernetes itself. Here a packet capture from inside the container found what latency metrics and DNS tests had not.

  2. Symptoms

    Datadog alerted the DevOps team to a high normalised load average, first on the staging cluster (Day 0, 21:06 EST) and the next day on the development cluster (Day 1, 16:53 EST). The load was first put down to a legitimate service that kept scaling up and down in both environments, and because of the volume of identical alerts they were muted until things settled.

    Trigger

    An automated crawler found the Weave Scope dashboard on a public load balancer that required no authentication. Weave Scope can open a shell in running containers, and its scope-agent containers ran privileged, as root and with the host's root filesystem mounted, so a command started there could run on the host.

    Root cause
    Component
    Contributing factor
    Detection

    Datadog alerts on high normalised load average, muted at first as noise from a legitimate service; on Day 3 an engineer ran top on a node and found the gcc process at 100% CPU.

    Remediation
    • stopped Weave Scope and removed its deployment from all Kubernetes clusters
    • checked access logs for unauthorised access to the instances
    • confirmed the miner's outbound connections went only to a mining pool
    • isolated one affected node for later investigation
    • rotated out every node in each cluster so all affected hosts were destroyed and rebuilt
    Action items
    • Audit all Kubernetes and deployment YAML files and security groups for services that are unintentionally public or misconfigured
    • Replace load-average alerts with more meaningful monitors and tune them to signal a real anomaly
    • Research tools such as falcon or sysdig for behavioural monitoring and anomaly and intrusion detection
    • Evaluate Istio and Linkerd for observing and controlling end-to-end network traffic
    • Analyse the cost of scanning Docker images and containers for known vulnerabilities
    • Set aside time in architecture discussions to revisit older choices such as running containers as root
    • Share knowledge of large projects, such as Kubernetes version migrations, across the whole team
    Lessons

    Load average was not a useful signal for spotting a problem in the clusters, and muted noisy alerts nearly hid the miner. A load balancer that Kubernetes creates belongs to Kubernetes: a manual security group edit in the AWS console is reverted, so internal exposure must be declared in the Kubernetes YAML. A dashboard that can open shells, in front of privileged agents that mount the host's root filesystem, gives whoever reaches it root on every node.

  3. Symptoms

    The NU.nl development team got reports that several services on one of its Kubernetes clusters behaved erratically: occasional error pages, slow responses and timeouts. The affected services were mostly internal tools and dashboards.

    Trigger

    A new ElastAlert rule, deployed shortly before from the command line, that fired if the Cloudfront-* Elasticsearch indexes stopped receiving documents; those indexes take a few million requests per hour, and the rule made ElastAlert's memory grow without bound.

    Root cause
    Component
    Contributing factor
    Detection

    Users reported error pages, slow responses and timeouts from several services; no alert fired.

    Remediation
    • terminated the NotReady nodes one by one so the auto-scaling group replaced them (the new nodes also went NotReady, but Prometheus and Grafana landed on a stable node)
    • found the ElastAlert pods' memory growth on the Grafana pod memory dashboard
    • fixed the ElastAlert problem that caused the memory growth
    Action items
    • Define alerts on resource usage such as CPU, memory and disk space
    • Keep researching an alerting strategy that combines Elasticsearch, Prometheus Alertmanager and New Relic
    • Consider exporting metrics out of the cluster and running Grafana outside it, on EC2 or a separate Kubernetes cluster
    • Either visualise the important metrics in the ELK stack or make Prometheus and Grafana more available, and improve the metrics strategy
    • Set resource limits for ElastAlert through the Helm values
    • Configure namespaces with default memory requests and limits
    • Isolate customer-facing workloads from build, logging and monitoring workloads (a dedicated node group, or later a separate tools cluster)
    • Deploy everything through automation such as Jenkins pipelines, and consider a GitOps approach
    • Deploy through a pipeline that includes a smoke test step
    • Make Kubernetes and cloud infrastructure work part of the team's sprints, picked up by all team members while pairing with more experienced members, and hold workshops that go deep into specific topics
    Lessons

    Fixing the root cause covers only a small part of what an incident can teach; the chain of smaller failures around it is where most of the learning is. A pod without a memory limit can take whole nodes down, so limits need defaults at the namespace level. Monitoring that runs inside the cluster it watches goes blind with it. Changes installed by hand from the command line leave the rest of the team without a record of what changed.

  4. Symptoms

    A Kubernetes cluster went down after one CronJob kept failing and its pods piled up. Because each failed run kept restarting instead of being replaced by the next one, the pods accumulated into the thousands. The speaker's arithmetic for a job scheduled every minute that fails for three days is 4,320 restarting pods.

    Trigger

    A CronJob kept failing.

    Root cause
    Component
    Contributing factor
    Lessons

    When deploying a CronJob, set its concurrency policy to Replace or Forbid; with the default, failing runs accumulate and restart together. An application should handle a missing dependency with probes and bounded back-off rather than crash in a loop, which also keeps it from hammering a dependency that is already struggling.

Check your cluster for this

These 7 cases involve kubelet and Container memory limit. 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 cause1

Configuration change7 cases

  1. IL-0153 DOKS worker nodes went NotReady at 06:00 UTC on three days because a systemd timer in the Debian image ran apt upgrade and killed it mid kernel upgrade, breaking node networking
  2. IL-0109 A retried node rollover on a DFDS EKS cluster set the Auto Scaling group back to 15 instances, and the default termination policy removed every healthy worker node, leaving the cluster with no nodes
    DFDS2023
  3. IL-0191 A label selector accidentally added to the Loki distributor Service matched only the new pods of a rollout, which took all write traffic, were OOM-killed and stalled the rollout
  4. IL-0189 A JVM service moved from EC2 to Kubernetes answered up to ten times slower because KIAM's 15-minute credentials made the AWS Java SDK fetch new credentials on every request
  5. IL-0052 A cryptocurrency miner ran on every node of the development and staging clusters through a public, unauthenticated Weave Scope dashboard
  6. IL-0035 Worker nodes went NotReady after a new ElastAlert rule without memory limits used up node memory
    NU.nl2019
  7. IL-0055 A failing CronJob with the default concurrency policy piled up thousands of restarting pods and took down a cluster
    Target2018