{"id":"IL-0095","org":"2i2c","date":"2025-08-12","docs":["https://github.com/2i2c-org/infrastructure/issues/5560","https://github.com/2i2c-org/jupyterhub-home-nfs/pull/34","https://github.com/2i2c-org/infrastructure/pull/6555"],"title":{"en":"An engineer deleted the prod namespace of the LEAP JupyterHub while believing the kubeconfig pointed at a new cluster, and the hub was down for about an hour","ko":"엔지니어가 kubectl 이 다른 클러스터를 가리키는 줄 모르고 prod 네임스페이스를 지워 LEAP JupyterHub 가 약 1시간 동안 멈췄다."},"domain":["kubernetes"],"impact":{"scope":"The prod namespace of the LEAP JupyterHub deployment; all its pods were terminated and the hub was inaccessible","users":"Users of the LEAP hub","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Provisioning persistent storage outside Kubernetes with Terraform meant a deleted namespace cost an hour of downtime and no data. The engineer had no visible cue for which kubeconfig context was active, which turned a workaround on a new cluster into a deletion on a production one. The engineer knew the fix but did not feel confident redeploying without first confirming with the tech lead, in another time zone, that there was no risk of losing persistent data, and reaching the tech lead took time.","ko":"쿠버네티스 리소스를 잃는 경우에 대비해 영구 스토리지를 Terraform 으로 따로 프로비저닝해 둔 설계 덕분에 사용자 데이터가 살아남았다. 반면 엔지니어에게 지금 어느 클러스터를 다루고 있는지 알려 주는 단서가 없었던 것이 실수로 이어졌다. 복구 방법을 알고 있어도 데이터 손실 위험을 확인해 줄 사람이 다른 시간대에 있어 재배포가 늦어졌다. NFS 배포의 순환 의존과 nfs-share-creator 잡도 복구를 늦췄다."},"related":[],"sources":[{"url":"https://github.com/2i2c-org/incident-reports/blob/main/reports/2025-08-12-leap-outage.md","kind":"github","license":"unknown","archived":null}],"summary":{"en":"2i2c runs JupyterHub deployments, including the LEAP hub. An engineer deploying a new hub hit a bug in 2i2c's NFS deployment process, and while working around it deleted the prod namespace. The kubeconfig context in use was the LEAP cluster, not the new one, and the engineer had no contextual clue that they had switched onto the LEAP hub. Every pod in LEAP's prod namespace was terminated and the hub was down for about an hour. The report names the NFS deployment bug as the root cause.\n\nA second engineer joined early to handle communication with the LEAP community, and the People Operations Lead later took over the communications part of the incident commander role. The engineer knew the fix was to redeploy prod but was not confident it carried no risk of losing persistent data, and reaching the tech lead in another time zone took time; the tech lead confirmed the plan at 5:43 PM. The last remaining user pod in prod was deleted so the namespace deletion completed. The nfs-share-creator job was blocking the deployment and was killed at 5:48 PM, prod was redeployed with the updated NFS server IP address at 5:51 PM, and the prod ingress was deleted at 5:55 PM so that users could not start servers mid-deployment. The hub was declared online at 6:08 PM and the incident resolved at 6:11 PM. Redeployment took longer because of a circular dependency between deploying jupyterhub-home-nfs and putting the NFS server IP into the PVCs.\n\nNo data was lost. 2i2c provisions persistent storage separately with Terraform, so deleting Kubernetes resources did not remove user data.","ko":"2025년 8월 12일, 2i2c 엔지니어가 새 temple 클러스터에 허브를 배포하다 NFS 문제를 만났다. NFS 배포 과정의 버그를 우회하려고 prod 네임스페이스를 지웠는데, 그때 작업 대상은 LEAP 클러스터였다. 엔지니어에게는 LEAP 허브로 넘어갔다는 단서가 보이지 않았다. LEAP 의 prod 네임스페이스에 있던 파드가 모두 종료됐고 허브는 약 1시간 동안 접속할 수 없었다.\n\n엔지니어는 모니터링 알림보다 먼저 상황을 알아채고 17:00 BST 에 장애를 선언했다. 두 번째 엔지니어가 일찍 합류해 LEAP 커뮤니티에 상황을 알리는 일을 맡았다. 첫 엔지니어는 prod 를 다시 배포하면 된다는 것을 알았지만, 영구 데이터를 잃을 위험이 없는지 다른 시간대에 있는 테크 리드에게 확인받고 싶어 했고 연락이 닿기까지 시간이 걸렸다. 17:43 에 테크 리드가 재배포를 승인했다.\n\n남아 있던 사용자 파드 하나를 지워 네임스페이스 삭제를 끝냈다. 재배포는 nfs-share-creator 잡이 막고 있어 그 잡을 종료했다. jupyterhub-home-nfs 배포와 PVC 가 쓰는 NFS 서버 IP 사이에 순환 의존이 있어 재배포에 몇 분이 더 걸렸다. 엔지니어는 바뀐 NFS 서버 IP 로 prod 를 다시 배포하고, 배포 중 사용자가 서버를 띄우지 못하게 인그레스를 지웠다. 18:08 에 허브를 다시 열었고 18:11 에 장애를 종료했다. 영구 스토리지를 Terraform 으로 따로 프로비저닝해 두었기 때문에 데이터는 잃지 않았다."},"trigger":{"en":"While working around a bug in the NFS deployment process for a new hub, an engineer ran a delete of the prod namespace with the kubeconfig context pointing at the LEAP cluster.","ko":"2i2c 엔지니어가 새 허브의 NFS 배포 버그를 우회하던 중, kubectl 이 LEAP 클러스터를 가리키고 있는 줄 모르고 prod 네임스페이스를 지웠다."},"severity":"unknown","symptoms":["resources-deleted","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":"1h 11m"},"date_kind":"incident","detection":{"en":"The engineer who ran the deletion noticed when it hung and the pod list showed users of another cluster, and declared the incident at 5:00 PM BST, before the uptime check on leap.2i2c.cloud failed at 5:32 PM.","ko":"네임스페이스를 지운 엔지니어가 삭제가 멈춘 것을 보고 파드 목록을 확인하다 다른 클러스터의 사용자 파드를 발견했고, 17:00 BST 에 장애를 선언했다. 모니터링 알림보다 먼저였다. 업타임 검사 실패 알림은 17:32 에 왔고 사용자의 404 제보는 17:37 에 들어왔다."},"components":["persistent-volume","job-controller","ingress","terraform"],"root_cause":"human-operation","remediation":[{"text":{"en":"deleted the single remaining user pod in prod so the namespace deletion completed","ko":"prod 네임스페이스에 남아 있던 사용자 파드 하나를 지워 멈춰 있던 네임스페이스 삭제를 끝냈다."}},{"text":{"en":"killed the nfs-share-creator job that was blocking the deployment","ko":"재배포를 막고 있던 nfs-share-creator 잡을 종료했다."}},{"text":{"en":"redeployed the prod hub with the updated NFS server IP address","ko":"바뀐 NFS 서버 IP 주소로 prod 허브를 다시 배포했다."}},{"text":{"en":"deleted the prod ingress during the deployment so that users could not start servers mid-deployment","ko":"배포 도중 사용자가 서버를 띄우지 못하게 prod 인그레스를 지웠다."}}],"action_items":[{"text":{"en":"Write an incident report","ko":"장애 보고서를 쓴다."}},{"text":{"en":"Write a short incident response document for the case \"I deleted prod, what next?\"","ko":"\"I deleted prod, what next?\" 같은 상황에서 바로 볼 수 있는 짧은 장애 대응 문서를 쓴다."}},{"text":{"en":"Upgrade jupyterhub-home-nfs and remove the nfs-share-creator job","ko":"jupyterhub-home-nfs 를 업그레이드하고 nfs-share-creator 잡을 없앤다."}},{"text":{"en":"Add opt-in hardening against nested kubeconfig contexts to the deployer system","ko":"deployer 시스템에 kubeconfig 컨텍스트가 중첩되는 것을 막는 보호 장치를 넣었다. 기본으로는 꺼져 있고 쓰는 사람이 켜야 동작한다."}},{"text":{"en":"Change the engineer's shell configuration to show distinct kubeconfig contexts more clearly","ko":"해당 엔지니어의 셸 설정을 바꿔 kubeconfig 컨텍스트를 더 쉽게 구분하게 했다."}}],"contributing":["no-safeguard-on-destructive-change","no-runbook"],"recurrence_of":null,"symptoms_text":{"en":"At 5:00 PM BST on 12 August 2025 a 2i2c engineer declared an incident. While deploying a new cluster called temple and running into NFS issues, the engineer had deleted the prod namespace; the deletion hung, and listing the pods showed many users from a different cluster. The engineer's first message suggested that the kubeconfig had somehow been mutated or corrupted and that some other cluster was being torn down.\n\nAt 5:02 PM the engineer confirmed the cluster was LEAP, with no pods left in prod. Every pod in the prod namespace had been terminated and the hub was entirely inaccessible. One user pod remained, and it kept the namespace deletion from finishing.\n\nThe uptime check on leap.2i2c.cloud failed at 5:32 PM, half an hour after the engineer had declared the incident. At 5:37 PM the first user reports of 404 errors when opening the hub arrived.\n\nAfter the hub was back, at 8:16 PM, Sarika de Bruyn reported that some users had trouble logging in or saw kernel disconnections. The engineer saw heavy Dask workloads starting, with up to 200 Dask nodes being provisioned, and then saw the load settle.","ko":"새 temple 클러스터에 허브를 배포하던 엔지니어가 NFS 문제를 만나 prod 네임스페이스를 지웠는데 삭제가 멈춘 채 끝나지 않았다. 파드 목록을 보니 다른 클러스터의 사용자 파드가 잔뜩 있었다.\n\n엔지니어는 처음에 kubeconfig 가 바뀌었거나 망가져 다른 클러스터가 지워지고 있다고 생각하고 17:00 BST 에 장애를 선언했다. 2분 뒤 지워진 쪽이 LEAP 클러스터이고 prod 에는 파드가 하나도 남지 않았다는 것을 확인했다.\n\n17:32 BST 에 LEAP 업타임 검사가 실패했다. 알림에는 \"An uptime check on two-eye-two-see Uptime Check URL labels {project_id=two-eye-two-see, host=leap.2i2c.cloud} is failing.\" 이 찍혔다. 17:37 에는 허브에 접속하면 404 오류가 난다는 사용자 제보가 처음 들어왔다.\n\n복구 뒤 20:16 에 일부 사용자가 로그인에 어려움을 겪거나 커널 연결이 끊긴다는 제보가 들어왔다. 엔지니어가 확인해 보니 무거운 Dask 워크로드가 시작되면서 Dask 노드가 최대 200개까지 프로비저닝됐고, 워크로드는 결국 안정됐다."},"updated_at":"2026-11-10T00:00:32.643226Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0095","org_slug":"2i2c"}