IL-0504 Binder Project · 2018-07-08kubernetes
mybinder.org 에서 사용자 파드가 지워지지 않고 쌓여 클러스터가 가득 차고 실행이 약 5시간 동안 실패했다
2018년 7월 8일 토요일 저녁 18:30 CEST 무렵, KubeSpawner 의 pod reflector 가 쿠버네티스 이벤트를 받지 못하게 됐다. 그 뒤로 실행이 중단되면, 실제로 떠 있는 파드가 pod reflector 에 없다는 이유로 이미 지워진 것으로 처리돼 그대로 남았다. 이 두 번째 버그가 겹치면서 파드가 계속 늘어 클러스터를 채웠고 새 실행이 모두 실패했다. 운영팀은 파드가 늘어난 직접 원인은 지워지지 않은 파드이고, 근본 원인은 pod reflector 가 갱신을 멈춘 것이라고 적었다. KubeSpawner 에 새로 들어간 event reflector 가 관련이 있을 수 있지만 아직 분명하지 않다고 봤다.
22:48 에 gitter 에 이상하다는 신고가 올라왔고 23:25 에 조사를 시작했다. 4시간보다 오래된 파드를 지우고, ipython-in-depth 처럼 많이 쓰는 이미지의 파드는 더 공격적으로 정리했다. 파드 600개쯤을 정리하고 Hub 파드를 재시작하자 실행이 정상으로 돌아왔다. 파드가 몰리면서 늘어난 노드 몇 개는 cordon 해서 오토스케일러가 회수하게 했다. 다음 날 08:00 에 최대 수명을 넘긴 파드를 손으로 정리해 남은 노드 일부를 비웠고, 클러스터는 3개 노드로 줄었다. 서비스 전체 중단은 약 5시간이었다.
- 심각도
- –
- 영향 범위
- mybinder.org: all new launches failed; the cluster filled with almost 1k user pods
- 영향 사용자
- all mybinder.org users launching servers
- 지역
- –
- 감지까지
- 4h 18m
- 복구까지
- 5h
증상
mybinder.org 의 실행 성공률이 0으로 떨어졌고 새 실행이 모두 실패했다. 다운타임 알림이 없어서, 문제는 4시간쯤 지나 gitter 에 이상하다는 신고가 올라온 뒤에야 알려졌다.
조사를 시작했을 때 클러스터는 가득 차 8개 노드까지 늘어나 있었다. 사용자 파드가 1k 개 가까이 있었고, 그 가운데 737개는 돌고 있었고 240개는 스케줄을 기다리고 있었다.
hub 로그에는 18:30 CEST 무렵부터 pod reflector 가 이벤트를 받지 못한 흔적이 남았다. TimeoutError: pod/jupyter-ipython-2dipython-2din-2ddepth-... did not disappear in 300 seconds! 와 TimeoutError: pod/jupyter-bokeh-2dbokeh-2dnotebooks-... did not start in 300 seconds! 가 찍혔다.
곧이어 hub 는 실행을 중단한 파드를 지우는 단계를 건너뛰었고, 로그에는 [W 2018-07-07 16:38:35.506 JupyterHub spawner:1476] No pod jupyter-ipython-2dipython-2din-2ddepth-... to delete. Assuming already deleted. 가 남았다. 이 파드들은 실제로 떠 있었지만 pod reflector 에 등록되지 않아서 지워지지 않았다.
원인
원인 소프트웨어 결함
- 촉발
- –
감지
18:30 무렵 시작된 실패를 22:48 에 gitter 에 올라온 신고로 알았다. 다운타임 알림이 아직 없었다.
조치
- 4시간보다 오래된 파드를 지웠다.
- ipython-in-depth 처럼 가장 많이 쓰는 이미지의 파드는 더 공격적으로 정리했다.
- 파드 600개쯤을 정리하고 Hub 파드를 재시작해 실행을 정상으로 돌렸다.
- 파드가 몰려 새로 할당된 노드 몇 개를 cordon 해서 오토스케일러가 회수하게 했다.
- 다음 날 아침 최대 수명을 넘긴 파드를 손으로 정리해 남은 노드 일부를 비웠고, 클러스터는 3개 노드로 줄었다.
후속 조치
- 다운타임 알림을 만든다.
- 스케일 다운을 자동화한다.
- 다운타임 복구 절차를 문서로 남긴다. 이번에는 잘 됐지만 팀원 모두가 그 절차를 아는 것은 아니었다.
- pod reflector 가 실패할 때 파드를 지우지 못하는 버그를 고친다.
- KubeSpawner 가 pod reflector 의 실패에 더 잘 견디게 한다.
- event reflector 를 파드마다 하나씩 두지 않고 하나로 합친다. event reflector 는 KubeSpawner 의 큰 새 기능이었고 이번 실패와 관련이 있을 수 있지만 아직 분명하지 않다.
- Hub 상태가 나빠 보이는 것을 자동으로 찾아 사람 손 없이 Hub 를 재시작하는 방법을 시도한다.
교훈
mybinder.org-deploy 의 scripts/delete-pods.py 스크립트로 오래된 파드를 한꺼번에 지울 수 있었고, 문제를 알고 나서는 몇 분 안에 클러스터가 정상으로 돌아왔다. KubeSpawner 의 pod reflector 가 이벤트를 받지 못해 Hub 를 재시작할 때까지 실행이 모두 실패했다. 여기에 pod reflector 가 시작을 알아채지 못한 파드를 처음부터 만들어지지 않은 것으로 보고 지우지 않는 버그가 겹쳤다. 다운타임 알림이 없어서 5시간 동안 알아채지 못했다. 클러스터 스케일 다운에는 노드를 cordon 하고 drain 하는 손 작업이 여러 단계 남아 있었다.
출처
참고 문서
- 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
관련 사례
–
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0504
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0504/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.