IL-0504 Binder Project · 2018-07-08kubernetes
mybinder.org launches failed for about five hours as undeleted user pods piled up and filled the cluster
At about 18:30 CEST the pod reflector in KubeSpawner stopped receiving events, and every launch failed until the Hub was restarted. A second bug made it worse: pods that had started but were not registered in the reflector had their launch aborted, and KubeSpawner then skipped deleting them because it assumed those pods had never been created. The write-up names this skipped deletion as the direct cause of the runaway pod growth and the reflector that stopped updating as the root cause. Why the reflector stopped is not established; the new event reflectors, a major new KubeSpawner feature, may have been relevant.
There were no downtime notifications, so the outage ran for hours before anyone noticed. From 23:25 the team deleted pods older than four hours with the delete-pods.py script, culled pods of the most-used images such as ipython-in-depth more aggressively, and restarted the Hub pod; after about 600 pods were culled, launches were back to normal within a few minutes. Some of the nodes added for the flood of pods were cordoned so the autoscaler could reclaim them, and the next morning manual max-age culling let the cluster scale back down to 3 nodes. The total service disruption lasted about five hours.
- Severity
- –
- Scope
- mybinder.org: all new launches failed; the cluster filled with almost 1k user pods
- Users affected
- all mybinder.org users launching servers
- Region
- –
- Time to detect
- 4h 18m
- Time to resolve
- 5h
Symptoms
On Saturday 8 July 2018 at about 18:30 CEST, the launch success rate of mybinder.org dropped to zero and stayed there. Suspicious behaviour was first reported on Gitter at 22:48, more than four hours later.
When the investigation began at 23:25, the cluster was full and had scaled up to 8 nodes. It held almost 1k user pods: 737 running and 240 waiting to be scheduled.
The hub logs, read the next morning, showed that the pod reflector had stopped receiving events at about 18:30, with errors such as "TimeoutError: pod/jupyter-ipython-2dipython-2din-2ddepth-... did not disappear in 300 seconds!" and "TimeoutError: pod/jupyter-bokeh-2dbokeh-2dnotebooks-... did not start in 300 seconds!". Shortly after came warnings that deletion was skipped for pods that had launched: "No pod jupyter-ipython-2dipython-2din-2ddepth-... to delete. Assuming already deleted."
Symptom Service outagePods PendingStale or inconsistent API reads
Root cause
Root cause Software defect
- Trigger
- –
Detection
Suspicious behaviour was reported on Gitter at 22:48 CEST, more than four hours after launch success dropped to zero; no downtime notifications were set up.
Remediation
- Deleted user pods older than four hours with the delete-pods.py script
- Culled pods of the most-used images, such as ipython-in-depth, more aggressively
- Restarted the Hub pod after about 600 pods were culled, which brought launches back to normal
- Cordoned some of the extra nodes so the autoscaler could reclaim them
- Ran manual max-age culling the next morning, letting the cluster scale back to 3 nodes
Action items
- Set up notifications of downtime
- Automate cluster scale-down
- Document the downtime recovery steps so every team member knows them
- Fix KubeSpawner's failure to delete pods when the pod reflector fails
- Make KubeSpawner more robust to pod reflector failure
- Refactor the event reflector into a single reflector instead of one per pod
- Detect a likely unhealthy Hub state and restart the Hub without manual intervention
Lessons
The delete-pods.py script was useful for bulk-deleting old pods, and once the problem was noticed the cluster was healthy within a few minutes. The outage lasted five hours before anyone noticed because no downtime notifications existed, and scaling the cluster back down still took several manual steps of cordoning and draining nodes.
Sources
References
- https://github.com/jupyterhub/mybinder.org-deploy/issues/611
- https://github.com/jupyterhub/mybinder.org-deploy/issues/646
- https://github.com/jupyterhub/mybinder.org-deploy/issues/655
- https://github.com/jupyterhub/kubespawner/pull/208
- https://github.com/jupyterhub/kubespawner/issues/209
- https://github.com/jupyterhub/kubespawner/issues/210
- https://github.com/jupyterhub/jupyterhub/issues/2028
Related cases
–
Check your cluster for this
For AI tools and scripts
- This case as JSON: https://incidentlib-api.benchlet.com/cases/IL-0504
- The cases most like it: https://incidentlib-api.benchlet.com/cases/IL-0504/similar
- MCP server (Streamable HTTP, no key):
https://incidentlib-mcp.benchlet.com/mcp
The API needs no key. The MCP server offers search_incidents, get_incident, list_incidents, get_topic and vocab; both are explained on API and MCP.