{"id":"IL-0231","org":"Stytch","date":"2023-02-23","docs":[],"title":{"en":"Deleting EKS managed node groups also deleted Karpenter's instance profile, and every Stytch worker node shut down","ko":"EKS 클러스터의 워커 노드가 모두 꺼지고 새 노드가 뜨지 않아 Stytch API 가 14분 동안 완전히 멈췄다"},"domain":["kubernetes","aws"],"impact":{"scope":"Full outage of the Live Stytch API for 14 minutes and of the Stytch Frontend SDKs and Dashboard for 17 minutes; the production Test API was down from 21:10 to 21:36 UTC","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"The IAM role for Karpenter's self-managed nodes should never have been the same as the one for the managed node groups; sharing it is what let the node group deletion break Karpenter, and the roles had been left combined for speed and simplicity when Karpenter was first set up. The hours-long delay between the configuration change and its effect made the incident hard to assess, and AWS confirmed that this delay is expected, so Stytch will allow for it in future IAM instance profile changes.","ko":"Karpenter 의 자체 관리 노드에 노드 그룹과 같은 IAM 역할을 쓰지 말았어야 했다. 한 IAM 역할을 여러 용도로 겹쳐 쓰면 이번 같은 문제가 그대로 난다. 팀은 Karpenter 를 처음 설정할 때 빠르고 단순하게 하려다 역할을 나누지 않았다. 원인과 결과 사이가 몇 시간 벌어져 있어 장애 전체를 판단하기 어려웠다. AWS 는 이 지연이 예상된 동작이라고 확인했고, 팀은 앞으로 IAM instance profile 을 바꿀 때 이를 고려한다."},"related":[],"sources":[{"url":"https://stytch.com/blog/stytch-postmortem-2023-02-23/","kind":"blog","archived":"https://web.archive.org/web/20260514101439/https://stytch.com/blog/stytch-postmortem-2023-02-23/"}],"summary":{"en":"Stytch runs on AWS EKS and creates its clusters, managed node groups and other cloud resources with Crossplane from a management cluster. After moving from Fargate to EC2 nodes, first on statically sized managed node groups and then on nodes provisioned by Karpenter, Stytch decided to remove the managed node groups for its data plane. Two engineers deleted the node groups in the AWS console, staging first. The console warned that deleting a managed node group removes the worker role from the aws-auth ConfigMap; the engineers expected Crossplane, which manages that ConfigMap, to put the role back at once, saw no impact in staging and repeated the change in production. In the background the AWS API ran cleanup calls after the node group deletion, and CloudTrail later showed that the instance profile was deleted, at 14:24 UTC in staging and 14:50 UTC in production. Karpenter's nodes used the same IAM role and instance profile as the node groups.\n\nCrossplane recreated the role, but its Cluster Composition gave the new instance profile a randomly generated name instead of the old name that Karpenter's configuration referred to, so Karpenter had no permission to launch nodes. Nothing broke until the deleted instance profile was needed again hours later. From 16:06 UTC low-urgency warnings about the missing instance profile came from staging and then production; staging nodes began shutting down at 19:54 UTC, and from 20:13 UTC production nodes went Ready:Unknown, were tainted, had their pods evicted and were not replaced. Stytch started its incident process and froze deployments at 20:26 UTC.\n\nAt 21:00 UTC the team identified the node group change and reverted the deletion, which recreated the node groups, but the new nodes came up too slowly and the API went down at 21:10 UTC. The team then manually scaled up one of the Live API node groups with more and much larger nodes, removed its taints, and removed node selectors from the applications one after another so they could be scheduled. The Live API returned at 21:24 UTC, the Dashboard and Frontend SDKs at 21:34 UTC and the Test API at 21:36 UTC. AWS later confirmed the undocumented cascade of the DeleteNodegroup API: worker nodes are drained, pods on them are evicted, the service role deletes resources associated with the node group such as roles, policies and security groups, and the worker instances are terminated.","ko":"Stytch 는 AWS EKS 위에서 서비스를 돌리고, 클러스터와 관리형 노드 그룹 같은 클라우드 리소스를 Crossplane 으로 코드로 관리한다. 2022년 상반기부터 Fargate 에서 EC2 노드로 옮겨, 크기가 고정된 관리형 노드 그룹과 Karpenter 가 그때그때 띄우는 자체 관리 노드를 함께 썼다. 1년쯤 지나 팀은 Karpenter 를 충분히 믿게 되자 데이터 플레인 애플리케이션용 관리형 노드 그룹을 없애기로 했다.\n\n팀은 2023-02-23 14:21 UTC 에 쓰지 않는 관리형 노드 그룹을 지우는 변경을 반영했다. Crossplane 리소스는 orphan 삭제 정책이라 AWS 콘솔에서 직접 지워야 했다. 두 엔지니어가 스테이징부터 작업했다. 이때 콘솔은 워커 역할이 aws-auth ConfigMap 에서 빠져 자체 관리 노드 그룹에 영향이 갈 수 있다고 경고했다. 팀은 이 ConfigMap 도 Crossplane 이 곧바로 되살릴 것이라 보았고, 스테이징에서 문제가 없자 운영에도 같은 변경을 했다. 그런데 AWS API 는 AWS 문서에 없는 정리 호출을 뒤에서 했다. CloudTrail 로그를 보니 노드 그룹 삭제가 instance profile 삭제로 이어져 있었다. 스테이징은 14:24 UTC, 운영은 14:50 UTC 에 instance profile 이 지워졌다. Crossplane 이 역할은 다시 만들었지만, Cluster Composition 이 이전 이름 대신 무작위 이름을 정했다. Karpenter 설정은 이전 이름에 기대고 있었다. 그래서 Karpenter 는 새 노드를 띄울 권한이 없었다. Karpenter 노드와 노드 그룹이 같은 IAM 역할을 쓰고 있어서 이런 문제가 났다.\n\n문제는 지워진 instance profile 을 다시 쓰려 할 때 드러났기 때문에 몇 시간 뒤에야 나타났다. 16:06 UTC 와 17:48 UTC 에 스테이징과 운영에서 낮은 긴급도 경고가 왔지만 급하지 않은 문제로 분류되었다. 19:54 UTC 에 스테이징 노드가 꺼지기 시작했고, 20:13 UTC 부터 운영 노드가 Ready:Unknown 이 되며 하나씩 종료되고 파드가 쫓겨났다. Karpenter 는 새 노드를 띄우지 못했다. 팀은 20:26 UTC 에 장애 대응을 시작했고, 21:00 UTC 에 노드 그룹 삭제를 되돌렸다. 하지만 새 노드가 뜨는 동안 파드가 빨리 스케줄되지 않아 21:10 UTC 에 API 가 내려갔다. 팀은 Live API 노드 그룹 하나를 훨씬 큰 노드로 늘리고 taint 와 애플리케이션의 node selector 를 차례로 지워 파드를 스케줄했다. 21:24 UTC 에 Live API, 21:34 UTC 에 Dashboard 와 Frontend SDK, 21:36 UTC 에 Test API 가 돌아왔다. Live API 는 14분, Frontend SDK 와 Dashboard 는 17분 동안 완전히 멈췄다.\n\n사고 뒤 AWS 는 DeleteNodegroup 이 문서에 없는 연쇄 동작을 일으킨다고 확인했다. DeleteNodegroup 은 워커 노드를 drain 하고 파드를 쫓아낸다. 이어 서비스 역할이 노드 그룹과 엮인 역할, 정책, 보안 그룹 같은 리소스를 지우고, 마지막으로 워커 노드 인스턴스를 종료한다. AWS 는 원인과 결과 사이의 지연도 예상된 동작이라고 확인했다."},"trigger":{"en":"At 14:21 UTC Stytch landed a change to remove unused EKS managed node groups, because it had decided to run its data plane applications fully on nodes provisioned by Karpenter. Because Crossplane resources use an orphan deletion policy, engineers deleted the node groups in the AWS console, first in staging and then in production.","ko":"14:21 UTC 에 팀은 쓰지 않는 관리형 노드 그룹을 지우는 변경을 반영했고, 두 엔지니어가 AWS 콘솔에서 스테이징과 운영의 노드 그룹을 지웠다. 노드 그룹을 지우자 AWS 가 뒤에서 그 노드 그룹과 엮인 리소스를 정리하면서 Karpenter 노드도 쓰던 instance profile 을 지웠다."},"severity":"unknown","symptoms":["node-not-ready","autoscaler-scale-up-failed","pods-evicted","pod-pending","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"Low-urgency alerts that Karpenter could not find the instance profile fired in staging at 16:06 UTC and in production at 17:48 UTC but were not treated as an incident. The incident process started at 20:26 UTC when node launch failures spiked the Karpenter error rate, and Kubernetes monitoring alerts in production followed at 20:44 UTC. The support team flagged the outage at 21:14 UTC.","ko":"운영에서는 20:48 UTC 에 Deployment Replicas Unavailable 알림이 울렸고, 21:14 UTC 에 지원팀이 장애를 알렸다. 그보다 앞서 16:06 UTC 에 스테이징, 17:48 UTC 에 운영에서 Karpenter 가 instance profile 을 찾지 못한다는 낮은 긴급도 경고가 왔지만 급하지 않은 문제로 분류되었다. 20:26 UTC 에 Karpenter 오류율이 치솟자 팀은 장애 대응을 시작했다."},"components":["karpenter","node-pool","config-templating"],"root_cause":"config-change","remediation":[{"text":{"en":"Reverted the node group deletion, which recreated the managed node groups","ko":"노드 그룹 삭제를 되돌려 노드 그룹을 다시 만들었다. 새 파드는 새 노드가 뜨기를 기다려야 해서 충분히 빨리 스케줄되지 않았다."}},{"text":{"en":"Manually scaled up one of the Live API node groups with more and much larger nodes and removed its taints","ko":"Live API 노드 그룹 하나를 컴퓨팅 크기가 훨씬 큰 노드로 손으로 늘리고, 그 노드 그룹에서 taint 를 지웠다."}},{"text":{"en":"Removed node selectors from the applications one after another so their pods could be scheduled","ko":"애플리케이션의 node selector 를 차례로 지워 파드가 그 노드에 스케줄되게 했다. 몇 분 안에 애플리케이션마다 필요한 수의 노드에 파드가 스케줄되었다."}}],"action_items":[{"text":{"en":"Treat Karpenter errors as high-urgency alerts that page the on-call engineer","ko":"Karpenter 오류의 심각도를 올려 당직 호출기로 바로 가는 높은 긴급도 알림으로 다룬다."}},{"text":{"en":"Tune cluster alerting thresholds to send a high-urgency page earlier","ko":"클러스터 알림의 임계값을 조정해 높은 긴급도 호출이 더 일찍 가게 한다."}},{"text":{"en":"Add alerts for nodes that stay NotReady or Unknown for long periods","ko":"노드가 오랫동안 NotReady 나 Unknown 으로 표시되면 알림을 보내 기존 노드의 상태를 더 잘 지켜본다."}},{"text":{"en":"Raise the thresholds of other deployment alerts, such as unschedulable pods and replicas not available","ko":"스케줄할 수 없는 파드, 사용할 수 없는 레플리카를 비롯한 다른 배포 관련 알림의 임계값을 올린다."}},{"text":{"en":"Give Karpenter its own IAM instance profile, separate from the one used by the node groups","ko":"Karpenter 가 쓰는 IAM instance profile 을 노드 그룹이 쓰는 IAM profile 과 다른 것으로 바꾼다."}},{"text":{"en":"Separate other AWS resources that were shared between uses, specifically IAM and security groups","ko":"여러 용도로 겹쳐 쓰던 다른 AWS 리소스, 특히 IAM 과 보안 그룹을 나눈다."}},{"text":{"en":"Rework the EKS and Karpenter configuration so system instance profiles are unique, static per cluster and generated by Karpenter","ko":"EKS 와 Karpenter 설정을 크게 고쳐 복잡도를 줄인다. 새 시스템 instance profile 은 Karpenter 가 클러스터마다 고유하고 고정된 이름으로 만들게 한다."}},{"text":{"en":"Introduce a cloud visualization tool for engineers making infrastructure changes","ko":"개발팀이 인프라를 바꿀 때 쓸 수 있도록 클라우드 시각화 도구를 들인다."}},{"text":{"en":"Add live system and product metrics to the status page","ko":"상태 페이지에 실시간 시스템 지표와 제품 지표를 넣어 Stytch API 가 지금 어떻게 동작하는지 보여 준다."}},{"text":{"en":"Ask AWS to confirm the undocumented actions and side effects of deleting a node group","ko":"문서에 없는 동작과 부작용을 AWS 에 문의해 확인한다."}},{"text":{"en":"Correct the shared use of IAM roles and security groups across the infrastructure","ko":"인프라에서 IAM 역할과 보안 그룹을 여러 용도로 겹쳐 쓰던 것을 바로잡는다."}}],"contributing":["assumption-unverified","shared-credential","delayed-effect-change","early-signal-ignored"],"recurrence_of":null,"symptoms_text":{"en":"On 23 February 2023 Stytch's EKS worker nodes went Ready:Unknown one after another, Karpenter failed to launch replacement nodes, and with fewer and fewer pods scheduled the Live Stytch API went down for 14 minutes, returning 5XX on every call.\n\nThe first signs came hours earlier as low-urgency warnings that Karpenter could not find the specified instance profile and could not launch a new node: at 16:06 UTC in staging and at 17:48 UTC in production. The on-call engineer judged both low urgency because existing deployments were healthy and new pods were still being scheduled. At 19:54 UTC existing staging nodes began shutting down and kube-proxy health notifications arrived from staging, while production still looked healthy.\n\nAt 20:13 UTC the first production node was marked Ready:Unknown by the Kubernetes control plane. Nodes running with the deleted instance profile terminated slowly as they tried to use it; after five minutes in Unknown the taint controller added an unreachable taint and all pods on the node were evicted. Karpenter kept trying to launch new nodes with the deleted instance profile, and at 20:26 UTC its error rate spiked. Kubernetes monitoring alerts fired in production at 20:44 UTC and Deployment Replicas Unavailable alerts at 20:48 UTC; the Live and Test API went down at 21:10 UTC and the Dashboard and Frontend SDKs at 21:17 UTC.\n\nReverting the node group deletion at about 21:00 UTC recreated the managed node groups, but new pods could not be scheduled quickly enough because they had to wait for the new nodes to start.","ko":"2023-02-23 20:48 UTC 에 운영 환경에서 Deployment Replicas Unavailable 알림이 울리기 시작했다. 21:10 UTC 에 운영 Live API 와 Test API 가 내려가 Stytch API 호출이 모두 5XX 를 냈고, 21:17 UTC 에는 Dashboard 와 SDK 도 내려갔다.\n\n첫 경고는 몇 시간 앞서 왔다. 16:06 UTC 에 스테이징에서 Karpenter 가 지정된 instance profile 을 찾지 못해 새 노드를 띄울 수 없다는 낮은 긴급도 경고가 왔고, 17:48 UTC 에 운영에서도 비슷한 경고가 왔다. 기존 배포가 정상이고 새 파드도 스케줄되고 있어서 당직 엔지니어는 그날 안에 고치면 되는 문제로 판단했다.\n\n19:54 UTC 에 스테이징의 기존 노드가 꺼지기 시작했고 kube-proxy 상태 알림이 왔다. 이때 운영은 아직 정상으로 보여 팀은 스테이징 문제와 원인에 집중했다.\n\n20:13 UTC 에 운영의 첫 노드가 쿠버네티스 컨트롤 플레인에서 Ready:Unknown 으로 표시되었다. 지워진 instance profile 을 쓰던 노드들이 그 profile 을 쓰려 할 때마다 하나씩 종료되었다. Unknown 상태로 5분이 지나면 taint controller 가 unreachable taint 를 걸어 그 노드의 파드를 모두 쫓아냈다. Karpenter 는 지워진 instance profile 로 새 노드를 띄우려다 실패했고, 스케줄되는 파드가 점점 줄었다.\n\n20:26 UTC 에 노드 생성 실패로 Karpenter 오류율이 치솟아 팀은 장애 대응 절차를 시작하고 배포를 멈췄다. 이때까지 Stytch API 와 SDK 는 아직 동작했다. 20:44 UTC 에 운영의 쿠버네티스 모니터링 알림이 울렸다.\n\n21:00 UTC 에 팀은 노드 그룹 변경을 원인으로 보고 노드 그룹 삭제를 되돌렸다. 노드 그룹은 금방 다시 만들어졌지만, 새 파드는 새 노드가 뜨기를 기다려야 해서 충분히 빨리 스케줄되지 않았다."},"updated_at":"2026-11-14T05:53:44.921106Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0231","org_slug":"stytch"}