{"id":"IL-0075","org":"Civis Analytics","date":"2019-01-16","docs":["https://kubernetes.io/docs/setup/cluster-large/#size-of-master-and-master-components"],"title":{"en":"Kubernetes API servers pegged on CPU when the cluster grew to about 290 nodes, traced to Datadog agents polling the API","ko":"클러스터가 EC2 인스턴스 약 290대로 커지자 노드마다 도는 Datadog 에이전트가 쿠버네티스 API 를 폴링하면서 API 서버가 CPU 와 메모리를 다 써 쿠버네티스 컨트롤 플레인을 쓸 수 없게 됐다."},"domain":["kubernetes","aws","observability"],"impact":{"scope":"internal release of the script platform; Kubernetes control plane unusable under load; notebooks and services intermittently failed to start","users":"internal users (the release was internal)","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Kubernetes can reach thousands of nodes only after tuning, and third-party DaemonSets are a common cause of control plane load: an agent on every node that polls or watches the API server multiplies with the cluster size. Datadog, kube2iam and the fluentd metadata filter all showed this. A small configuration change often fixes it, but finding it took load testing and the API server access logs.","ko":"가장 큰 실수는 쿠버네티스가 모든 것을 알아서 해 준다고 가정한 것이었다. 쿠버네티스는 노드 5,000대와 파드 100,000개를 지원한다고 하지만, 그 규모에 이르려면 손봐야 할 설정이 많다. 클러스터에 넣는 서드파티 도구, 특히 DaemonSet 을 조심해야 한다. 많은 도구가 보통 규모에 맞춰 만들어졌고 큰 규모에서는 시험되지 않았다. 클러스터 상태를 watch 하는 서비스가 데이터를 지나치게 많이 가져가도록 설계되면 규모 문제가 생긴다. kube2iam 은 모든 파드의 메타데이터를 메모리에 들고 있다가 메모리가 바닥났고, Fluentd Kubernetes Metadata Filter 플러그인도 같은 문제가 있다. 대개 설정 하나로 고칠 수 있지만, 부하 테스트를 해 보기 전에는 잘 드러나지 않는다."},"related":[],"sources":[{"url":"https://medium.com/@CivisAnalytics/https-medium-com-civis-analytics-breaking-kubernetes-how-we-broke-and-fixed-our-k8s-cluster-adfa6fbade61","kind":"blog","license":"unknown","archived":"https://web.archive.org/web/20250613144922/https://medium.com/@CivisAnalytics/https-medium-com-civis-analytics-breaking-kubernetes-how-we-broke-and-fixed-our-k8s-cluster-adfa6fbade61"}],"summary":{"en":"Civis Analytics runs its Data Science Platform on Kubernetes: Jupyter notebooks, client services and, since autumn 2018, client scripts. When script workloads moved to Kubernetes, the cluster grew to about 290 EC2 instances and the three API servers, running on m4.large masters with no CPU limit, saturated CPU and memory and made the control plane unusable. Moving internal users back to the old infrastructure stabilised the cluster. The author names the main mistake as assuming that Kubernetes handles scale for free.\n\nThe team set a goal of 500 nodes, with 1,000 as a stretch, and ran load tests on a staging cluster by raising the size of the AWS autoscaling group. They added Prometheus through the CoreOS Prometheus operator for API metrics. Larger masters (three m4.4xlarge, 16 CPU and 64 GiB each, against the Kubernetes recommendation of c4.4xlarge for 251 to 500 nodes) helped, but the cluster still struggled before 500 nodes.\n\nLoad tests without the Datadog and Sumologic (fluentd-based) DaemonSets reached about 800 nodes before latency and 5xx errors rose; adding Sumologic back still reached more than 700, which pointed at Datadog. With DD_KUBERNETES_COLLECT_METADATA_TAGS, every Datadog agent, one per node, polled the API server for the names of all services so it could tag pod metrics, and those tags were not even useful to Civis. Datadog offers two fixes: turning that setting off, or running the Datadog Cluster Agent so only one agent talks to the API server. With the Datadog changes the cluster reached more than 700 EC2 instances easily, and Civis concluded it could support 700 nodes.\n\nTwo other changes stayed in place without making a large difference: max-requests-inflight for non-mutating requests went from 400 to 1200, which reduced 429 responses, and the etcd EBS volumes grew from 20 to 100 GB, raising IOPS from 100 to 300. The team also found kube2iam running out of memory because it kept every pod's metadata in memory, and notes the same pattern in the fluentd Kubernetes metadata filter. For the later DNS timeouts, scripts were switched from the ClusterFirst DNS policy to Default, which skips cluster DNS.","ko":"Civis Analytics 는 Civis Data Science Platform 에서 Python, R, 컨테이너 스크립트를 돌리는 기반을 자체 인프라 Bocce 에서 쿠버네티스로 옮기고, 이 이전을 Croquet 라고 불렀다. 2018년 9월 사내에 먼저 공개한 뒤 클러스터가 불안정해졌다. 스크립트가 대량으로 몰리자 클러스터는 EC2 인스턴스 약 290대까지 커졌고, 예전에는 80대를 넘은 적이 없었다. CPU 제한이 없던 API 서버 컨테이너 세 개가 마스터 노드의 CPU 와 메모리를 다 써 버려 컨트롤 플레인을 쓸 수 없게 됐다. 사내 사용자들을 예전 인프라로 되돌리자 클러스터가 안정됐다.\n\n팀은 500 노드, 가능하면 1,000 노드를 목표로 정하고 스테이징 클러스터에서 오토스케일링 그룹 크기를 조금씩 늘리며 부하 테스트를 했다. API 요청 처리량, 5xx 오류율, 요청 지연 같은 지표가 없어서 Prometheus operator 로 Prometheus 를 배포했다. 마스터 노드를 m4.large 세 대에서 m4.4xlarge 세 대로 키웠지만 500 노드를 무리 없이 넘기지는 못했다. API 서버의 max-requests-inflight 를 400 에서 1200 으로 올리자 429 응답은 줄었지만 CPU 와 메모리 사용 양상은 크게 바뀌지 않았다.\n\n다음으로 DaemonSet 을 의심했다. Sumologic 수집기와 Datadog DaemonSet 을 빼고 테스트하니 약 800 노드까지 버텼다. Sumologic 만 다시 넣어도 700 노드 넘게 무리 없이 커졌으므로 Datadog 이 원인이었다. Datadog 에이전트는 DD_KUBERNETES_COLLECT_METADATA_TAGS 설정 때문에 노드마다 일정 간격으로 쿠버네티스 API 에서 서비스 목록을 가져갔다. 이렇게 모은 태그는 팀의 모니터링에 쓸모도 없었다. Datadog 설정을 고친 뒤 클러스터는 EC2 인스턴스 700대 넘게 무리 없이 커졌다. 이 밖에 etcd 의 EBS 볼륨을 20 GB 에서 100 GB 로 늘려 IOPS 를 100 에서 300 으로 올렸지만 효과는 크지 않았다.\n\n다시 사내에 공개하자 DNS 타임아웃으로 스크립트가 실패한다는 제보가 들어왔다. 팀은 문제를 안정적으로 재현하지 못했지만 클러스터 규모와 관련이 있다고 보고, 스크립트의 DNS 정책을 ClusterFirst 에서 Default 로 바꿨다. 그 뒤로 클러스터 문제는 없었고, 팀은 이 클러스터가 700 노드를 감당한다고 결론지었다."},"trigger":{"en":"Migrating clients' Python, R and container scripts from the in-house Bocce system to Kubernetes (the project called Croquet) pushed the cluster to about 290 EC2 instances under a large script load.","ko":"자체 인프라 Bocce 에서 쿠버네티스로 옮긴 스크립트 실행 기반 Croquet 를 2018년 9월 사내에 공개했다. 대량의 스크립트가 몰리면서 클러스터가 EC2 인스턴스 약 290대로 커졌고, 노드마다 도는 Datadog 에이전트가 쿠버네티스 API 를 폴링하는 부하도 함께 늘었다."},"severity":"unknown","symptoms":["control-plane-slow","control-plane-unresponsive","high-latency","high-error-rate","dns-resolution-failures"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Developer reports of slow kubectl commands and user reports of notebooks and services failing to start, then high CPU on the master nodes.","ko":"개발자들이 kubectl 명령이 느리다고 했고, 사용자들은 노트북과 서비스가 가끔 시작되지 않는다고 알렸다. 마스터 노드의 CPU 사용률이 높았고, 조사해 보니 API 서버 컨테이너 세 개가 CPU 를 꽉 채워 쓰고 있었다. 당시 팀이 볼 수 있던 지표는 마스터 노드 CPU 의 CloudWatch 지표와 Datadog 의 컨테이너별 CPU, 메모리 지표였다. API 요청 처리량과 5xx 오류율과 요청 지연은 볼 수 없었다."},"components":["kube-apiserver","telemetry-agent","etcd"],"root_cause":"capacity","remediation":[{"text":{"en":"moved internal users back to the old Bocce infrastructure","ko":"사내 사용자들을 예전 인프라로 되돌려 클러스터를 안정시켰다."}},{"text":{"en":"resized the three master nodes from m4.large to m4.4xlarge","ko":"마스터 노드를 m4.large 세 대에서 m4.4xlarge 세 대로 키웠다."}},{"text":{"en":"changed the Datadog setup so per-node agents no longer polled the API server for service metadata","ko":"노드마다 도는 Datadog 에이전트가 서비스 메타데이터를 얻으려고 API 서버를 폴링하지 않도록 Datadog 설정을 바꿨다."}},{"text":{"en":"raised max-requests-inflight for non-mutating requests from 400 to 1200","ko":"상태를 바꾸지 않는 요청의 max-requests-inflight 한도를 400 에서 1200 으로 올렸다."}},{"text":{"en":"grew the etcd EBS volumes from 20 to 100 GB (100 to 300 IOPS)","ko":"etcd 의 EBS 볼륨을 20 GB 에서 100 GB 로 늘려 IOPS 를 100 에서 300 으로 올렸다."}},{"text":{"en":"switched client scripts from the ClusterFirst DNS policy to Default","ko":"스크립트의 DNS 정책을 ClusterFirst 에서 Default 로 바꿔 DNS 타임아웃을 줄였다."}}],"action_items":[{"text":{"en":"Upgrade etcd from 2.2.1 to 3.x once Kops supports a safe migration","ko":"Kops 가 안전한 이전을 지원하면 etcd 를 2.2.1 에서 3.x 로 올린다."}},{"text":{"en":"Solve the in-cluster DNS timeouts so client scripts can talk to other workloads in the cluster","ko":"스크립트가 클러스터 안의 다른 워크로드와 통신할 수 있도록 DNS 타임아웃 문제를 근본적으로 해결한다."}}],"contributing":["limit-unknown","assumption-unverified","monitoring-blind-spot","no-capacity-planning"],"recurrence_of":null,"symptoms_text":{"en":"After Civis released its new script infrastructure on Kubernetes internally in September 2018, developers reported that kubectl commands were slow, and users intermittently could not start notebooks and services.\n\nThe serious failures came when the cluster was loaded with a large number of scripts and grew to about 290 EC2 instances; it had never run more than 80 before. CPU on the master nodes went high, and the three Kubernetes API server containers were pegged on CPU. The API servers had no CPU limit, so they starved the other control plane components on the same nodes and the cluster became unusable. The API servers also used up all the memory on their node before restarting.\n\nThe team could see master-node CPU in CloudWatch and per-container CPU and memory through Datadog agents, but not API request throughput, 5xx error rates or request latency. Once they deployed Prometheus, their load tests showed API response latency and 5xx error rates spiking at peak load, and the API server access logs contained several 429 responses.\n\nAfter the infrastructure was released again, users reported scripts failing with DNS timeout errors. The team could not reproduce them reliably and only connected them to the larger cluster size.","ko":"스크립트 실행 기반을 쿠버네티스로 옮긴 Croquet 를 사내에 공개한 뒤, 개발자들은 kubectl 명령이 느리다고 했다. 사용자들은 노트북과 서비스가 가끔 시작되지 않는다고 알렸다.\n\n스크립트가 대량으로 몰리자 클러스터는 EC2 인스턴스 약 290대까지 커졌다. 예전에는 80대를 넘은 적이 없었다. 이때 마스터 노드의 CPU 사용률이 높아졌고, 들여다보니 쿠버네티스 API 서버 컨테이너 세 개가 모두 CPU 를 꽉 채워 쓰고 있었다.\n\nAPI 서버에는 CPU 제한이 없어서 노드 CPU 를 다 써 버렸다. 다른 컨트롤 플레인 구성 요소가 자원을 얻지 못해 클러스터를 쓸 수 없게 됐다. API 서버는 노드 메모리도 다 쓴 뒤에야 재시작됐다.\n\n팀은 처음에 마스터 노드가 작다고 보고 m4.large 를 m4.4xlarge 로 키웠다. 감당할 수 있는 규모는 늘었지만 500 노드를 무리 없이 넘기지는 못했다. 부하 테스트 중에는 API 접근 로그에 429 응답이 여러 번 찍혔고, 부하가 정점에 가까워지면 응답 지연과 5xx 오류율이 치솟았다.\n\n다시 사내에 공개한 뒤에는 사용자들이 DNS 타임아웃 오류로 스크립트가 실패한다고 알렸다."},"updated_at":"2026-11-10T00:00:39.699018Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0075","org_slug":"civis-analytics"}