{"id":"IL-0440","org":"Cybozu","date":"2024-08-20","docs":["https://elixir.bootlin.com/linux/v6.1.9/source/include/net/dropreason.h#L234"],"title":{"en":"Pods on Cybozu's Neco Kubernetes platform got frequent no such host and i/o timeout DNS errors from dropped packets","ko":"파드가 보낸 DNS 쿼리 패킷이 노드에서 버려져 클러스터 내부 서비스의 이름 조회가 no such host 와 i/o timeout 으로 자주 실패했다"},"domain":["kubernetes","networking"],"impact":{"scope":"Name resolution of cluster Services from a MySQL cluster monitoring application failed intermittently","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Raising netdev_max_backlog makes the same failure less likely and the new alerts catch it early, but it is not a root fix: a pod that sends more packets can fill the backlog queue again. Cybozu sees replacing the veth interface with netkit, which skips the backlog queue, as the root fix.","ko":"netdev_max_backlog 를 늘리고 softnet 알림과 런북을 두면 같은 장애가 덜 나고, 나더라도 일찍 알아챌 수 있다. 다만 값을 늘리는 것은 근본 대책이 아니어서, 더 많은 패킷을 보내는 파드가 배포되면 backlog 큐가 다시 차고 패킷이 버려질 수 있다."},"related":[],"sources":[{"url":"https://blog.cybozu.io/entry/2024/08/20/122920","kind":"blog","archived":"https://web.archive.org/web/20260520142221/https://blog.cybozu.io/entry/2024/08/20/122920"}],"summary":{"en":"Neco runs two DNS servers: node-dns, an Unbound caching server on every node, and cluster-dns, the authoritative CoreDNS server for the cluster. A pod sends its query to node-dns, which answers from its cache or asks cluster-dns. Pod networking is mainly Cilium, mostly without an overlay network; a packet sent by a pod passes through the pod's veth interface and the Linux kernel network stack before reaching the NIC.\n\nThe monitoring application sent about 15,000 packets a second from one pod. On the way from the pod's veth interface to the host side, packets are put on a per-CPU backlog queue, whose length is set by the kernel parameter net.core.netdev_max_backlog. On the Kubernetes nodes it was the default 1000, so with this pod on the node the queue filled and outgoing packets, including DNS queries, were dropped. A lookup of the Service moco-1 sends five queries in turn because of the search domains (moco-1.default.svc.cluster.local, moco-1.svc.cluster.local, moco-1.cluster.local, moco-1.local and moco-1.). When only the first query was dropped, the others reached node-dns and got NXDOMAIN, so the application reported no such host; when all five were dropped, no answer came and the application reported i/o timeout.\n\nCybozu reproduced the drops in dctest, a virtual data center built from VMs that runs a Kubernetes cluster, with a Go program sending packets at a similar rate for about an hour. /proc/net/softnet_stat showed 790 and 183 dropped packets on CPU cores 8 and 9, and `perf record -e skb:kfree_skb` on the pod's cgroup gave the drop reason CPU_BACKLOG. The kernel describes that reason as a full backlog queue or the RPS flow limit, and the RPS flow limit was disabled in this environment. Cybozu raised netdev_max_backlog and added softnet alerts with a runbook. It notes that this is not a root fix, since a pod sending even more packets could fill the queue again, and plans to evaluate Cilium's netkit device (Linux kernel 6.7, Cilium 1.16), which sends pod packets straight to the physical device without passing through the backlog queue.","ko":"사이보즈의 Cloud Platform 팀은 Neco 라는 쿠버네티스 플랫폼을 운영한다. 클러스터 네트워크에는 주로 Cilium 을 쓰고, 일부 용도를 빼면 오버레이 네트워크를 쓰지 않는다. 파드의 DNS 조회는 노드마다 도는 캐시 DNS 서버 node-dns 를 먼저 거치고, 캐시에 없으면 클러스터 안의 권한 DNS 서버 cluster-dns 로 간다. node-dns 는 Unbound 이고 cluster-dns 는 CoreDNS 다.\n\nMySQL 클러스터 감시 애플리케이션은 파드 하나에서 초당 1만 5천 패킷쯤을 보냈다. 파드가 보낸 패킷은 파드에 연결된 veth 인터페이스를 지나 CPU 의 backlog 큐에 들어간 뒤 호스트 쪽 veth 로 넘어간다. backlog 큐의 길이를 정하는 커널 파라미터 net.core.netdev_max_backlog 는 노드에서 기본값 1000 이었다. 큐가 가득 차자 패킷이 버려졌다. 버려진 패킷에 DNS 쿼리 패킷이 섞여 있어서 애플리케이션에 no such host 와 i/o timeout 오류가 났다.\n\ndctest 는 데이터센터 구성을 VM 으로 통째로 가상화한 환경이다. 팀은 dctest 에 이번 장애와 비슷한 빈도로 패킷을 보내는 Go 프로그램을 배포해 1시간쯤 돌렸다. 그러자 /proc/net/softnet_stat 에서 CPU 코어 8 과 9 에 각각 790개와 183개의 드롭이 재현되었다. perf 로 skb:kfree_skb 이벤트를 기록하자 드롭 이유가 CPU_BACKLOG 로 나왔다. RPS flow limit 을 끈 환경이었으므로 backlog 큐가 가득 찬 것이 원인임을 다시 확인했다. 팀은 netdev_max_backlog 를 늘리고 softnet 알림과 그 런북을 마련했다. 이 조치는 근본 대책이 아니어서, veth 를 netkit 으로 바꾸는 방안을 다음에 검증하기로 했다."},"trigger":{"en":"The number of MySQL clusters watched by the monitoring application increased, and after that the name resolution errors became frequent.","ko":"MySQL 클러스터 감시 애플리케이션이 감시하는 클러스터 수가 늘어났고, 그 뒤부터 이름 조회 오류가 잦아졌다."},"severity":"unknown","symptoms":["dns-resolution-failures","intermittent-timeouts","packet-loss"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"A team using the platform contacted the Cloud Platform team, describing the errors as a performance problem in the cluster DNS server.","ko":"Cloud Platform 팀은 쿠버네티스 플랫폼을 쓰는 팀에게서 클러스터 DNS 서버에 성능 문제가 있다는 문의를 받고 알게 됐다."},"components":["linux-kernel","dns-resolver"],"root_cause":"capacity","remediation":[{"text":{"en":"Raised the kernel parameter netdev_max_backlog on the nodes","ko":"커널 파라미터 net.core.netdev_max_backlog 값을 늘렸다."}},{"text":{"en":"Set up alerts on softnet","ko":"softnet 지표에 알림을 설정했다."}},{"text":{"en":"Wrote a runbook for the softnet alerts","ko":"softnet 알림에 대응하는 런북을 마련했다."}}],"action_items":[{"text":{"en":"Evaluate Cilium's netkit device so pod packets bypass the backlog queue","ko":"veth 인터페이스를 netkit 인터페이스로 바꿔 파드의 송신 패킷이 backlog 큐를 거치지 않고 물리 장치로 바로 가게 하는 방안을 검증한다. netkit 은 리눅스 커널 6.7 에 들어온 가상 네트워크 장치이고, Cilium 1.16 부터 쓸 수 있다."}}],"contributing":["default-config-unchanged"],"recurrence_of":null,"symptoms_text":{"en":"A team using Neco, the Kubernetes platform run by Cybozu's Cloud Platform team, reported what looked like a performance problem in the cluster DNS servers. An application in a pod that resolved cluster Services at hundreds of requests per second frequently got `no such host` and `i/o timeout` errors.\n\nThe application monitors MySQL clusters: it connects to each cluster every second, runs SQL queries to check the cluster works, and recreates its session every second. The errors became frequent after the number of MySQL clusters it monitored grew.\n\nIn the application log the failures appeared as \"write check failed\" with the error \"ping failed: dial tcp: lookup moco-1: i/o timeout\". The log of node-dns, the Unbound caching DNS server on each node, had no query from the application at the moments the application timed out; in one example about 10 seconds passed between two logged queries for moco-1.default.svc.cluster.local. The same gaps showed up in logs from other periods.\n\nThe team first suspected the DNS servers. Metrics and logs showed that node-dns and cluster-dns, the CoreDNS server in the cluster, handled their requests normally and did not return NXDOMAIN for names that existed, so the DNS queries seemed to be lost somewhere between the application and node-dns. The cadvisor metric container_network_transmit_packets_dropped_total showed about 100 packets a second dropped while being sent from the pod, and the Node exporter metric node_softnet_dropped_total, which counts packets dropped because the CPU backlog queue was full, was rising as well.","ko":"파드 안의 애플리케이션이 초당 수백 건씩 클러스터 내부 서비스의 이름을 조회하자 no such host 와 i/o timeout 오류가 자주 났다. 쿠버네티스 플랫폼을 쓰는 팀은 클러스터의 DNS 서버에 성능 문제가 있다고 보고 Cloud Platform 팀에 문의했다.\n\n오류가 난 애플리케이션은 MySQL 클러스터를 감시하는 프로그램이었다. 이 프로그램은 매초 세션을 새로 만들어 MySQL 클러스터에 연결하고 SQL 쿼리가 제대로 실행되는지 확인한다. 감시하는 MySQL 클러스터 수가 늘어난 뒤부터 이름 조회 오류가 잦아졌다.\n\n애플리케이션 로그에는 \"write check failed\" 메시지와 함께 \"ping failed: dial tcp: lookup moco-1: i/o timeout\" 오류가 찍혔다. 같은 시각 node-dns 인 Unbound 의 로그에서는 moco-1.default.svc.cluster.local. 조회 기록 사이가 10초쯤 비어 있었다. 애플리케이션의 조회가 node-dns 에 기록되지 않은 때와 타임아웃이 난 때가 겹쳤고, 다른 시간대의 로그에서도 같은 일이 보였다.\n\n팀은 처음에 node-dns 와 cluster-dns 의 성능을 의심하고 메트릭과 로그를 봤다. 두 DNS 서버는 요청을 정상으로 처리하고 있었고, 실제로 있는 도메인을 NXDOMAIN 으로 응답하지도 않았다. 그래서 애플리케이션과 node-dns 사이 경로에서 패킷이 사라진다고 보고 네트워크를 조사했다.\n\ncAdvisor 의 container_network_transmit_packets_dropped_total 에서는 파드가 송신 중에 버린 패킷이 초당 100개쯤 나타났다. Node exporter 의 node_softnet_dropped_total 도 늘고 있었다. 이 지표는 커널이 패킷을 CPU 의 backlog 큐에 넣으려 할 때 큐가 가득 차서 버린 패킷 수다.\n\nmoco-1 을 조회하면 검색 도메인을 붙인 쿼리 다섯 개가 나간다. moco-1.default.svc.cluster.local 쿼리만 버려지고 나머지 쿼리가 node-dns 에 닿으면, 나머지 쿼리는 모두 NXDOMAIN 을 받으므로 애플리케이션은 no such host 를 냈다. 다섯 쿼리가 모두 버려지면 응답이 오지 않아 i/o timeout 이 났다."},"updated_at":"2026-11-14T12:34:08.958108Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0440","org_slug":"cybozu"}