{"id":"IL-0433","org":"MonotaRO","date":"2023-08-24","docs":[],"title":{"en":"Shifting API traffic from EC2 to EKS on Fargate overloaded two CoreDNS pods and caused mass name-resolution errors","ko":"EC2 에서 EKS on Fargate 로 트래픽을 옮기던 중 CoreDNS 과부하로 이름 해석 오류가 쏟아져 롤백했다"},"domain":["kubernetes","aws","networking"],"impact":{"scope":"Requests to the backend API on EKS failed to reach the database and outside services through the proxy, so the traffic shift was rolled back","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"MonotaRO assumed that a managed service would not cause problems and so skipped researching other companies' migrations, load testing and monitoring. Staging differed from production in its network layout, so ndots differed between them, and staging databases could not take a high-load test. Looking at the CoreDNS logs and resolv.conf instead of guessing found that ndots:5 made lookups of outside names try every search domain first.","ko":"MonotaRO 는 관리형 서비스이니 문제가 없으리라 짐작하고 사전 사례 조사, 부하 시험, 감시 같은 측정을 하지 않아 이번 문제가 났다고 보았다. 그래서 짐작하지 말고 측정하라는 원칙을 다시 새겼다. 운영 환경과 스테이징 환경의 차이가 컸다. 네트워크 구성이 달라 환경마다 ndots 가 달랐고, 스테이징 환경의 데이터베이스 사양으로는 고부하 시험을 할 수 없었다. 직접 관리하지 않는 부분의 리소스도 잘 알지 못했다. 쿠버네티스로 옮긴 다른 회사의 사례를 조사했다면 이번 문제를 어느 정도 예측할 수 있었다고 했다."},"related":[],"sources":[{"url":"https://tech-blog.monotaro.com/entry/2023/08/24/090000","kind":"blog","archived":"https://web.archive.org/web/20260208032515/https://tech-blog.monotaro.com/entry/2023/08/24/090000"}],"summary":{"en":"MonotaRO was moving a monolithic backend API with more than 200 endpoints from EC2 to EKS, with Fargate for the nodes and Datadog and Sentry for monitoring. The ALB shifted the share of requests per endpoint step by step. After some endpoints had been moved, raising the rest from 1% to 25% produced mass name resolution errors, and the traffic was moved back to the EC2 version. The two CoreDNS pods had a 100m CPU request and a 170Mi memory limit, so Fargate ran each on a 0.25 vCPU machine, and the load on them exceeded the request. An HPA for CoreDNS was added as a stopgap and the migration was completed with it, but it raised running cost and scale-in still caused occasional resolution errors.\n\nThe CoreDNS logs showed each lookup of example.com first trying example.com.default.svc.cluster.local, example.com.svc.cluster.local and example.com.cluster.local for both A and AAAA records, each answered NXDOMAIN, before example.com. answered NOERROR. The pod's /etc/resolv.conf had options ndots:5, so any name with fewer than five dots was tried with every search domain first. The CoreDNS cache TTL had never been set and was the default of 5 seconds, shorter than the TTL of the DNS servers behind CoreDNS. The occasional errors on scale-in came from CoreDNS pods being cut off during termination.\n\nThe permanent fixes were dnsConfig with ndots set to \"1\" in the application Deployments, cache 600 in the Corefile in the coredns ConfigMap (CoreDNS reloaded the changed Corefile by itself without a pod restart), and lameduck 30s in the health plugin so a terminating CoreDNS pod keeps serving for 30 seconds. A preStop sleep was not possible because the CoreDNS image is a distroless image from Google with no shell.","ko":"MonotaRO 의 SRE 그룹은 엔드포인트가 200개가 넘는 모놀리스 백엔드 API 를 EC2 에서 Fargate 노드를 쓰는 EKS 로 옮기고 있었다. ALB 로 엔드포인트마다 요청 비율을 바꾸며 진행했고, 일부 엔드포인트를 옮긴 뒤 나머지를 모두 옮기려고 비율을 1% 에서 25% 로 올렸다. 트래픽이 적은 시간대였고 전환 때 요청량은 1000RPS 안팎이었다. 이때 Sentry 에 이름 해석 오류가 대량으로 나서 데이터베이스와 프록시에 연결하지 못했고, 팀은 요청 비율을 VM 쪽으로 되돌렸다.\n\nCoreDNS 는 CPU requests 100m, 메모리 limits 170Mi 로 설정되어 Fargate 에서 0.25vCPU 머신에 올라갔고, replicas: 2 로 파드 두 개만 돌았다. 부하가 큰 원인은 파드의 /etc/resolv.conf 에 있는 options ndots:5 였다. 점이 다섯 개보다 적은 이름은 search 도메인을 차례로 붙여 질의하므로, example.com 하나를 풀 때마다 NXDOMAIN 질의가 여러 번 먼저 났다. 캐시 TTL 도 따로 정하지 않아 기본값 5초였고, 이는 CoreDNS 가 질의를 넘기는 상위 DNS 의 캐시 TTL 보다 짧았다.\n\n팀은 먼저 기존 CoreDNS 설정을 바꾸지 않고 HPA 로 CoreDNS 파드를 오토스케일하게 해 EKS 이전을 마쳤다. 그러나 비용이 계속 들고, 스케일인 때 파드가 끝나면서 처리 중인 요청이 끊겨 가끔 이름 해석 오류가 났다. 이후 애플리케이션 Deployment 의 dnsConfig 로 ndots 를 1 로 낮추고, CoreDNS ConfigMap 의 cache 를 600 으로 늘렸다. CoreDNS 이미지는 distroless 라 preStop 에서 sleep 을 쓸 수 없었으므로, health 플러그인의 lameduck 30s 로 종료를 늦췄다. 팀은 관리형 서비스이니 문제가 없으리라 짐작하고 사례 조사와 부하 시험, 감시를 하지 않은 것이 이번 문제의 원인이라고 보았다."},"trigger":{"en":"MonotaRO raised the share of requests for the remaining API endpoints routed by the ALB to the EKS version from 1% to 25%.","ko":"ALB 로 엔드포인트마다 요청 비율을 바꾸며 이전하던 중, 남은 엔드포인트의 요청 비율을 EKS 쪽으로 1% 에서 25% 로 올렸다."},"severity":"unknown","symptoms":["dns-resolution-failures","high-error-rate"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Sentry showed a large number of errors about connecting to the database and outside services right after the traffic share was raised.","ko":"요청 비율을 EKS 쪽으로 올린 직후 Sentry 에 DNS 이름 해석 오류가 대량으로 올라온 것을 확인했다."},"components":["coredns","dns-resolver","cpu-limit","hpa","pod-termination"],"root_cause":"capacity","remediation":[{"text":{"en":"Shifted the requests back to the EC2 version to stop the errors","ko":"오류를 멈추려고 요청 비율을 VM 쪽으로 되돌렸다."}},{"text":{"en":"Added an HPA to autoscale the CoreDNS pods as a stopgap, which let the migration to EKS finish","ko":"임시 조치로 HPA 리소스를 만들어 CoreDNS 파드를 오토스케일하게 했고, 그 뒤 EKS 이전을 마쳤다."}}],"action_items":[{"text":{"en":"Set ndots to 1 through dnsConfig in the application Deployments","ko":"애플리케이션 Deployment 의 dnsConfig 에 ndots 값 \"1\" 을 넣어 파드가 항상 FQDN 으로 이름을 해석하게 했다."}},{"text":{"en":"Set cache 600 in the Corefile in the coredns ConfigMap to lengthen the CoreDNS cache TTL","ko":"CoreDNS ConfigMap 의 Corefile 에서 cache 를 600 으로 바꿔 캐시 TTL 을 늘렸다."}},{"text":{"en":"Add lameduck 30s to the CoreDNS health plugin so CoreDNS keeps serving while it is scaled in","ko":"CoreDNS ConfigMap 의 health 플러그인에 lameduck 30s 를 넣어, 스케일인 때 파드가 바로 끝나지 않고 남은 처리를 이어 가게 했다."}},{"text":{"en":"Consider reducing the differences between production and staging, weighed against cost","ko":"운영 환경과 스테이징 환경의 차이를 줄이는 것을 비용과 균형을 보며 검토한다."}}],"contributing":["default-config-unchanged","no-resource-limits","staging-not-representative","assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"When MonotaRO raised the share of requests for the remaining endpoints of its backend API sent to EKS from 1% to 25%, Sentry filled with errors saying the API could not reach the database and outside services. The errors were about DNS name resolution, and the traffic had to be shifted back to the EC2 version.\n\nThe API is written in Python. Connections through the proxy failed with NewConnectionError('<urllib3.connection.HTTPSConnection object at 12345678>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'), and database connections failed with OperationalError:(2005, \"Unknown MySQL server host 'db.example.com' (-3)\"). The switch was done at a quiet time, at about 1000 RPS against a peak of 2000 to 3000 RPS.\n\nName resolution in the cluster is done by CoreDNS, so the team suspected CoreDNS first. After moving the traffic back, the CoreDNS CPU and memory use showed load above the 100m CPU request in the CoreDNS pod spec. With that small request, Fargate had provisioned each CoreDNS pod on a 0.25 vCPU machine, and the Deployment had replicas: 2, so only two CoreDNS pods ran.\n\nAs a stopgap the team autoscaled CoreDNS with an HPA, which let the migration finish, but name resolution errors still appeared from time to time when the HPA scaled CoreDNS in.","ko":"남은 엔드포인트의 요청 비율을 EKS 쪽으로 1% 에서 25% 로 올리자 Sentry 에 DNS 이름 해석 오류가 대량으로 올라왔다. API 가 데이터베이스와 외부 서비스에 연결하지 못했다.\n\n프록시 연결에서는 NewConnectionError('<urllib3.connection.HTTPSConnection object at 12345678>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution') 가 났다. 데이터베이스 연결에서는 OperationalError:(2005, \"Unknown MySQL server host 'db.example.com' (-3)\") 가 났다.\n\n쿠버네티스에서 이름 해석은 CoreDNS 가 맡으므로 팀은 곧바로 CoreDNS 를 의심했다. 요청 비율을 VM 쪽으로 되돌린 뒤 CPU 와 메모리 사용량을 보니, CPU requests 가 100m 인 CoreDNS 파드에 그보다 큰 부하가 걸려 있었다. CoreDNS 는 replicas: 2 로 파드 두 개뿐이었고, Fargate 에서 0.25vCPU 머신으로 프로비저닝되어 있었다.\n\nCoreDNS 로그에는 example.com.default.svc.cluster.local, example.com.svc.cluster.local, example.com.cluster.local 의 A 와 AAAA 질의가 NXDOMAIN 으로 끝난 뒤에야 example.com. 질의가 NOERROR 로 풀리는 줄이 이어졌다.\n\nHPA 로 CoreDNS 파드를 오토스케일하게 한 뒤에는 CoreDNS 가 스케일인할 때 가끔 이름 해석 오류가 났다."},"updated_at":"2026-11-14T12:34:08.312728Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0433","org_slug":"monotaro"}