{"id":"IL-0035","org":"NU.nl","date":"2019-02-01","docs":["https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#node-oom-behavior","https://elastalert.readthedocs.io/en/latest/ruletypes.html#max-query-size","https://github.com/helm/charts/blob/d3fd3f11578ebf74749b1b2c994c51d5199b8599/stable/elastalert/values.yaml#L33","https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/#create-a-limitrange-and-a-pod","https://www.robustperception.io/federation-what-is-it-good-for","https://github.com/dastergon/postmortem-templates"],"title":{"en":"Worker nodes went NotReady after a new ElastAlert rule without memory limits used up node memory","ko":"ElastAlert 에 새로 넣은 알림 규칙 때문에 ElastAlert 파드가 메모리를 끝없이 써서 노드가 잇달아 NotReady 가 되었고, 같은 클러스터의 도구와 Grafana, Prometheus 가 함께 흔들렸다."},"domain":["kubernetes","aws","observability"],"impact":{"scope":"one Kubernetes cluster; internally used tools and dashboards, including the cluster's own Grafana and Prometheus","users":"internal users of the affected tools","region":"AWS eu-west-1"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Fixing the root cause covers only a small part of what an incident can teach; the chain of smaller failures around it is where most of the learning is. A pod without a memory limit can take whole nodes down, so limits need defaults at the namespace level. Monitoring that runs inside the cluster it watches goes blind with it. Changes installed by hand from the command line leave the rest of the team without a record of what changed.","ko":"근본 원인 하나만 탓하는 포스트모템은 장애로 이어진 문제의 15% 정도만 다룬다. 이번에도 ElastAlert 를 고친 것은 일부일 뿐이었다. 알림이 없었고, 모니터링이 같은 클러스터 안에 있었고, Helm 차트의 자원 한도에 기본값이 없었다. 운영 도구와 고객 워크로드가 자원을 나눠 썼고, 손으로 한 배포를 팀이 몰랐고, 스모크 테스트가 없었고, 운영 지식이 일부에게만 있었다. 이렇게 배우려면 장애 건수나 해결 시간만 보지 않고 비난 없는 포스트모템을 허용하는 문화가 필요하다."},"related":[],"sources":[{"url":"https://www.tibobeijen.nl/2019/02/01/learning-from-kubernetes-cluster-failure/","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"NU.nl ran a few Kubernetes clusters on AWS for logging and build tooling and some small internal customer-facing workloads. The team had been trying ElastAlert, which raises alerts from Elasticsearch logs. A new rule watched the Cloudfront-* indexes for missing documents. Because that Cloudfront distribution handles a few million requests per hour, the rule drove the ElastAlert pods' memory up steeply. The ElastAlert Helm chart has no default resource requests or limits, the team had not set any, and the namespace had no default limits, so the pods kept growing until the nodes' operating system ran out of memory, killed processes and left nodes NotReady. A Grafana dashboard of per-pod memory showed the ElastAlert pods climbing, and the ElastAlert problem was fixed. In hindsight, the team writes, it would have been better to use the ElastAlert options use_count_query and/or max_query_size.\n\nThe write-up then walks through the chain of failures around that cause. No alert fired, because no alerts on resource usage had been defined yet. Grafana and Prometheus ran inside the failing cluster, so the team was nearly blind; the same metrics were also in the EC2-based ELK stack, but not visualised well enough to be noticed. Logging, monitoring and customer workloads shared the same nodes, so one tool could exhaust resources for everything. The rule had passed code review, but it was installed from the command line by one team member, so others did not know it had been deployed and nothing recorded what had changed in the cluster. There was no smoke test after deploying that could have caught the memory growth or restarts. And knowledge of operating Kubernetes sat with part of the team; the on-call engineer had little infrastructure experience and needed help from colleagues.","ko":"NU.nl 개발팀은 몇 달 전부터 작은 쿠버네티스 클러스터 몇 개를 운영했다. 클러스터에는 로그 도구와 빌드 도구 일부, 그리고 작은 내부용 고객 대면 워크로드가 올라가 있었다. 로그는 Elasticsearch 클러스터에 모았고, 로그로 알림을 만들려고 ElastAlert 를 시험하고 있었다.\n\n장애 직전에 Cloudfront-* 인덱스에 일정 시간 새 문서가 들어오지 않으면 울리는 알림이 추가되었다. 이 Cloudfront 배포는 시간당 요청이 수백만 건이어서, 이 규칙 때문에 ElastAlert 파드의 메모리 사용량이 폭증했다. ElastAlert 를 설치한 Helm 차트는 자원 요청값과 한도를 지정할 수 있지만 기본값이 없었고, 팀도 이를 놓쳐 파드에 메모리 한도가 없었다. 메모리가 바닥난 노드에서 시스템 OOM 이 일어나 노드가 NotReady 를 오갔고, 같은 노드를 쓰던 서비스와 클러스터 안의 Grafana, Prometheus 가 함께 흔들렸다.\n\n팀은 NotReady 노드를 하나씩 종료했지만 새 노드도 곧 NotReady 가 되었다. 그 사이 Prometheus 와 Grafana 가 안정적인 노드에 스케줄되었고, 파드 메모리 대시보드에서 끝없이 치솟는 선이 모두 ElastAlert 파드라는 것이 드러났다. 돌아보면 use_count_query 나 max_query_size 를 썼어야 했다.\n\n원문은 근본 원인 하나를 고치는 데서 끝내지 않고, 장애로 이어진 실패를 차례로 하나씩 짚는다. 알림은 하나도 울리지 않았다. 모니터링이 장애가 난 클러스터 안에 있어서 클러스터에 닿지 못하면 아무것도 볼 수 없었다. EC2 에서 도는 ELK 스택에 같은 데이터가 있었지만 제대로 시각화되지 않았거나 눈에 띄지 않았다. 고객 대면 워크로드와 운영 도구가 같은 자원을 나눠 썼다. 새 알림은 코드 리뷰를 거쳤지만 배포 사실을 팀 전체가 알지 못했다. 배포 뒤 스모크 테스트가 없었고, 쿠버네티스 운영 지식은 팀 일부에만 있었다."},"trigger":{"en":"A new ElastAlert rule, deployed shortly before from the command line, that fired if the Cloudfront-* Elasticsearch indexes stopped receiving documents; those indexes take a few million requests per hour, and the rule made ElastAlert's memory grow without bound.","ko":"장애 직전에 팀원 한 명이 ElastAlert 에 새 알림 규칙을 명령줄로 배포했다. Cloudfront-* 인덱스에 일정 시간 새 문서가 들어오지 않으면 울리는 규칙이었다."},"severity":"unknown","symptoms":["node-not-ready","high-error-rate","high-latency","intermittent-timeouts","monitoring-down","oom-killed"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Users reported error pages, slow responses and timeouts from several services; no alert fired.","ko":"여러 서비스에서 오류 페이지, 느린 응답, 타임아웃이 난다는 보고가 팀에 들어왔다. 알림은 하나도 울리지 않았다. 팀은 Prometheus 와 Grafana 가 안정적인 노드로 옮겨 간 뒤 파드 메모리 사용량 대시보드에서 원인을 찾았다."},"components":["memory-limit","kubelet","prometheus"],"root_cause":"config-change","remediation":[{"text":{"en":"terminated the NotReady nodes one by one so the auto-scaling group replaced them (the new nodes also went NotReady, but Prometheus and Grafana landed on a stable node)","ko":"NotReady 노드를 하나씩 종료해 오토스케일링 그룹이 새 노드를 띄우게 했다. 새 노드도 곧 NotReady 가 되어 이것만으로는 풀리지 않았다."}},{"text":{"en":"found the ElastAlert pods' memory growth on the Grafana pod memory dashboard","ko":"Prometheus 와 Grafana 가 안정적인 노드에 다시 스케줄된 뒤 파드 메모리 대시보드를 보고 ElastAlert 파드가 원인임을 찾아냈다."}},{"text":{"en":"fixed the ElastAlert problem that caused the memory growth","ko":"원인인 ElastAlert 문제를 고쳤다."}}],"action_items":[{"text":{"en":"Define alerts on resource usage such as CPU, memory and disk space","ko":"CPU, 메모리, 디스크 공간 같은 자원 사용량에 알림 규칙을 정의한다."}},{"text":{"en":"Keep researching an alerting strategy that combines Elasticsearch, Prometheus Alertmanager and New Relic","ko":"여러 출처를 함께 쓰는 알림 전략을 계속 연구한다. 지금은 Elasticsearch, 아직 설정하지 않은 Prometheus alertmanager, APM 용 New Relic 이 있다."}},{"text":{"en":"Consider exporting metrics out of the cluster and running Grafana outside it, on EC2 or a separate Kubernetes cluster","ko":"지표를 클러스터 밖으로 내보내고 Grafana 도 클러스터 밖으로 옮기는 방안을 검토한다. 옮길 곳은 EC2 나 별도 쿠버네티스 클러스터가 될 수 있다."}},{"text":{"en":"Either visualise the important metrics in the ELK stack or make Prometheus and Grafana more available, and improve the metrics strategy","ko":"중요한 지표를 ELK 에서 시각화하거나 Prometheus 와 Grafana 의 가용성을 높인다. 지표 전략도 개선한다."}},{"text":{"en":"Set resource limits for ElastAlert through the Helm values","ko":"ElastAlert 의 자원 한도를 Helm values 로 지정한다."}},{"text":{"en":"Configure namespaces with default memory requests and limits","ko":"네임스페이스에 메모리 요청값의 기본값과 한도를 설정한다."}},{"text":{"en":"Isolate customer-facing workloads from build, logging and monitoring workloads (a dedicated node group, or later a separate tools cluster)","ko":"고객 대면 워크로드를 빌드, 로깅, 모니터링 워크로드와 떼어 놓는다. 먼저 운영 클러스터 안의 전용 노드 그룹으로 옮기고, 경험에 따라 별도 도구 클러스터도 검토한다."}},{"text":{"en":"Deploy everything through automation such as Jenkins pipelines, and consider a GitOps approach","ko":"모든 배포를 Jenkins 파이프라인 같은 자동화로 하고, 새 버전 배포에 GitOps 방식을 검토한다."}},{"text":{"en":"Deploy through a pipeline that includes a smoke test step","ko":"배포 뒤 스모크 테스트 단계가 있는 파이프라인으로 배포한다."}},{"text":{"en":"Make Kubernetes and cloud infrastructure work part of the team's sprints, picked up by all team members while pairing with more experienced members, and hold workshops that go deep into specific topics","ko":"쿠버네티스와 클라우드 인프라 작업을 팀 스프린트에 넣어 모든 팀원이 경험 많은 팀원과 짝을 지어 맡게 하고, 주제별 심화 워크숍을 연다."}}],"contributing":["no-resource-limits","default-config-unchanged","no-alert-on-limit","monitoring-blind-spot","blast-radius-too-wide"],"recurrence_of":null,"symptoms_text":{"en":"The NU.nl development team got reports that several services on one of its Kubernetes clusters behaved erratically: occasional error pages, slow responses and timeouts. The affected services were mostly internal tools and dashboards.\n\nWhen the team opened Grafana to investigate, Grafana and Prometheus showed the same erratic behaviour, because they ran on the same cluster. kubectl get nodes listed two worker nodes as NotReady (cluster version v1.10.6, nodes in eu-west-1), and several workers were only about two hours old.\n\nkubectl describe node, on healthy and unhealthy nodes alike, showed the kubelet restarting and the node going NotReady and back to Ready, with the warning event \"SystemOOM ... System OOM encountered\" repeated four times. It looked as if the node's operating system was killing processes before the kubelet could reclaim memory itself.\n\nThe team first terminated the NotReady nodes one by one, expecting the auto-scaling group to bring up stable replacements. New nodes joined fine, but existing or new nodes soon went NotReady again. The one useful result was that Prometheus and Grafana landed on a node that stayed up, which gave the team the data to find the cause.","ko":"팀은 여러 서비스가 이상하게 동작한다는 보고를 받았다. 가끔 오류 페이지가 뜨고, 응답이 느리고, 타임아웃이 났다.\n\nGrafana 로 조사하려 했지만 Grafana 와 Prometheus 도 같은 증상을 보였다. 콘솔에서 `kubectl get nodes` 를 실행하자 v1.10.6 노드 가운데 ip-10-150-37-179 와 ip-10-150-39-21 두 대가 NotReady 였다.\n\n정상인 노드까지 여러 노드를 `kubectl describe node` 로 보니, 이벤트에 Starting kubelet, NodeNotReady, NodeReady 가 이어졌다. 그 사이에 `SystemOOM` 경고가 `System OOM encountered` 라는 메시지와 함께 네 번 찍혀 있었다. kubelet 이 메모리를 회수하기 전에 노드의 운영체제가 프로세스를 죽이는 것으로 보였다.\n\n노드는 오토스케일링 그룹에 속해 있었다. 팀은 NotReady 노드를 하나씩 종료해 새 노드가 안정적으로 버티는지 보았다. 새 노드는 정상으로 올라왔지만 기존 노드나 새 노드가 금세 다시 NotReady 가 되었다. 다만 이 과정에서 Prometheus 와 Grafana 가 안정적인 노드로 옮겨 가 데이터를 볼 수 있게 되었다."},"updated_at":"2026-11-10T00:00:38.087241Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0035","org_slug":"nu-nl"}