Cases4 cases
1 to 4 of 4
IL-0192A Crossplane composition merge-policy change for a TLS policy update switched Kubernetes Services from LoadBalancer to ClusterIP and deleted the load balancers behind about 25% of Grafana Cloud servicesGrafana Labs2025- Symptoms
At 08:41 UTC on 18 February 2025, several Grafana Labs application teams noticed that some of their endpoints were no longer available. The investigation quickly found that the load balancers for those endpoints were gone.
- Trigger
A change to the merge policy of a Crossplane composition, made so that the default TLS policy of the load balancers could be overridden, was applied to development, staging and production at the same time.
- Root cause
- Component
- Contributing factor
- Detection
Application teams noticed at 08:41 UTC that some of their endpoints were no longer available.
- Remediation
- rolled back the Crossplane composition change
- recreated the affected Kubernetes Services so that Kubernetes recreated their load balancers
- Action items
- Add a CI check that blocks a deployment to more than one wave, so any exception to the dev, staging, canary, production order is caught
- Give higher priority to a more sophisticated wave design that was already under discussion
- Enable deletion protection on the load balancers, one layer below Crossplane
- Enable deletion protection on other critical cloud provider resources
- Develop the Crossplane Managed Resources exporter for change validation and visibility, now in beta testing within Platform engineering; simulated against the change that caused this outage, it shows a clear diff
- Complete and test support for reconfiguring the TLS policy so application teams can deploy it
- Lessons
A change that looked small and low-risk had a side effect that only a full end-to-end test through Crossplane and the cloud provider's load balancer controller would have shown; seeing the intended override work was not that test. Staged rollout and blast-radius limits that exist only as process can be skipped, so Grafana Labs moved to enforce waves in CI and to add deletion protection on the load balancers themselves, beneath the Crossplane layer.
IL-0191A 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 rolloutGrafana Labs2022- Symptoms
During a rollout of the Grafana Loki distributor Deployment in one Grafana Cloud Logs environment, the write path went down for about 25 minutes. The newly started distributor pods were OOM-killed over and over, never passed their health and readiness checks, and the rollout stopped making progress.
- Trigger
Step 1 of moving the Loki ring off a separately deployed key-value store onto embedded memberlist added a new label, gossip_ring_member: 'true', to the distributor pods and Service, together with a small config change, and the change required a rollout of the distributors.
- Root cause
- Component
- Contributing factor
- Detection
The first clue was kubectl get pods and the kube_deployment_status_replicas_updated metric showing that only 16 of 140 distributor pods had been updated.
- Remediation
- scaled the distributor Deployment out horizontally so the load spread over more pods with the new label and the rollout could continue
- reverted the migration step to end the outage
- set service_ignored_labels:: [$._config.gossip_member_label] and passed it to k.util.serviceFor so the label is no longer copied into the Service selector
- Action items
- Update the function that generates a Service from a Deployment or StatefulSet to handle edge cases, including this one
- Lessons
A Service whose selector is built by copying every label of a workload changes which pods it matches as soon as a new label is added. During a rollout it then matches only the new pods, which receive all the traffic before there are enough of them. A few errors during a rollout in other environments hid the problem, because those environments gave distributors more memory and could absorb the shift. Manifest diffs kept for every pull request made it possible to see that the Service selector had changed.
IL-0193A GCP persistent disk degradation took down a Cassandra StatefulSet whose OrderedReady policy blocked recovery, and Metrictank could not reconnect to the recreated pods, causing a 23-hour Graphite query outageGrafana Labs2019- Symptoms
An alert fired because some Metrictank instances could not drain their write queue, which meant they could not write to Cassandra. Google had not yet published an incident, but a team member's Discord server, also hosted on GCP, was down, which pointed at GCP; Google confirmed an SSD persistent disk incident about 90 minutes after it began.
- Trigger
A Google Cloud incident in which SSD persistent disks performed so poorly that many I/O operations failed for an extended period.
- Root cause
- Component
- Contributing factor
- Detection
An alert that Metrictank write instances could not flush their write queue to Cassandra. Error rates alone rose too slowly to notice quickly, because most queries were served from memory.
- Remediation
- Added compute nodes hoping new nodes could attach the disks, which did not work
- Deleted and recreated the Cassandra StatefulSet with an updated pod management policy, so all pods but one started
- Raised Kafka topic retention to avoid data loss during the long downtime
- Temporarily added nodes so Metrictank restarts were not slowed by CPU starvation
- Restarted all Metrictank instances, which took more than 20 hours, so they connected to the new Cassandra IPs
- Action items
- Re-instantiate the gocql connection in Metrictank when it is detected dead, so it reconnects even when all Cassandra IPs change
- Fix the Metrictank write-queue deadlock by updating an internal data structure with atomics instead of a lock
- Lessons
An OrderedReady StatefulSet cannot recover past a single pod whose volume will not attach; when every pod is down, the ordering that protects normal start-up blocks the whole cluster. A client that learns peer addresses only from live peers needs a way to go back to the service name when all peers are replaced at once. A long-tolerated bug with a config workaround can surface at the worst time; this one turned out to have a simple fix. Writing incoming data to a Kafka write-ahead log first meant all data could still be ingested after recovery, and Kafka on non-SSD disks escaped the disk incident. With both Metrictank bugs fixed, the team expects a similar event to take less than 3 hours to recover from instead of 23.
IL-0004A new default pod priority let a new Cortex cluster preempt every production ingester in a cascade, taking Grafana Cloud Hosted Prometheus downGrafana Labs2019- Symptoms
On Friday 19 July 2019 the Grafana Cloud Hosted Prometheus service, which is built on the Cortex project, was down for 26 minutes. New error-budget-based Prometheus alerts paged the on-call engineer within 4 minutes of the start.
- Trigger
An engineer created a new dedicated Cortex cluster for a large customer. Its configuration did not include the new pod priorities, so all its pods got the default medium priority, and the Kubernetes cluster did not have enough free resources to fit it.
- Root cause
- Component
- Contributing factor
- Detection
New error-budget-based Prometheus alerts paged the on-call engineer within 4 minutes of the start of the outage.
- Remediation
- Scaled up the underlying Kubernetes cluster to fit both the new and the production Cortex clusters
- Diagnosed and fixed the out-of-memory errors in the authenticating reverse proxies in front of Cortex
- Action items
- Give the production ingesters the high priority class, which should have been done before medium became the default
- Have more people review changes to configuration that is global to the cluster
- Require a design document for every configuration change outside a namespace
- Automate the sizing of the frontend authenticating reverse proxy so it does not run out of memory under overload
- Investigate the default backoff and scaling behaviour of Prometheus to prevent a stampeding herd of retries
- Lessons
Medium should not have become the default priority until the production ingesters had the high priority, and that designation should have reached production sooner. The change was considered too minor for a design document and was only discussed in a GitHub issue; cluster-global configuration needs more scrutiny. On the positive side, once Cortex had enough capacity it recovered by itself, and Grafana Loki helped confirm that the ingesters exited gracefully.
These 4 cases involve Configuration templating and Cloud load balancer. 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
Configuration change3 cases
- Component
- Contributing factor
- Organisation
IL-0192A Crossplane composition merge-policy change for a TLS policy update switched Kubernetes Services from LoadBalancer to ClusterIP and deleted the load balancers behind about 25% of Grafana Cloud servicesGrafana Labs2025IL-0191A 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 rolloutGrafana Labs2022IL-0004A new default pod priority let a new Cortex cluster preempt every production ingester in a cascade, taking Grafana Cloud Hosted Prometheus downGrafana Labs2019
Cloud provider outage1 case
- Contributing factor
- Organisation