Incident Library

IL-0440 Cybozu · 2024-08-20 (게시일)kubernetesnetworking

파드가 보낸 DNS 쿼리 패킷이 노드에서 버려져 클러스터 내부 서비스의 이름 조회가 no such host 와 i/o timeout 으로 자주 실패했다

사이보즈의 Cloud Platform 팀은 Neco 라는 쿠버네티스 플랫폼을 운영한다. 클러스터 네트워크에는 주로 Cilium 을 쓰고, 일부 용도를 빼면 오버레이 네트워크를 쓰지 않는다. 파드의 DNS 조회는 노드마다 도는 캐시 DNS 서버 node-dns 를 먼저 거치고, 캐시에 없으면 클러스터 안의 권한 DNS 서버 cluster-dns 로 간다. node-dns 는 Unbound 이고 cluster-dns 는 CoreDNS 다.

MySQL 클러스터 감시 애플리케이션은 파드 하나에서 초당 1만 5천 패킷쯤을 보냈다. 파드가 보낸 패킷은 파드에 연결된 veth 인터페이스를 지나 CPU 의 backlog 큐에 들어간 뒤 호스트 쪽 veth 로 넘어간다. backlog 큐의 길이를 정하는 커널 파라미터 net.core.netdev_max_backlog 는 노드에서 기본값 1000 이었다. 큐가 가득 차자 패킷이 버려졌다. 버려진 패킷에 DNS 쿼리 패킷이 섞여 있어서 애플리케이션에 no such host 와 i/o timeout 오류가 났다.

dctest 는 데이터센터 구성을 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 으로 바꾸는 방안을 다음에 검증하기로 했다.

심각도
–
영향 범위
Name resolution of cluster Services from a MySQL cluster monitoring application failed intermittently
영향 사용자
–
지역
–
감지까지
–
복구까지
–

증상

파드 안의 애플리케이션이 초당 수백 건씩 클러스터 내부 서비스의 이름을 조회하자 no such host 와 i/o timeout 오류가 자주 났다. 쿠버네티스 플랫폼을 쓰는 팀은 클러스터의 DNS 서버에 성능 문제가 있다고 보고 Cloud Platform 팀에 문의했다.

오류가 난 애플리케이션은 MySQL 클러스터를 감시하는 프로그램이었다. 이 프로그램은 매초 세션을 새로 만들어 MySQL 클러스터에 연결하고 SQL 쿼리가 제대로 실행되는지 확인한다. 감시하는 MySQL 클러스터 수가 늘어난 뒤부터 이름 조회 오류가 잦아졌다.

애플리케이션 로그에는 "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 에 기록되지 않은 때와 타임아웃이 난 때가 겹쳤고, 다른 시간대의 로그에서도 같은 일이 보였다.

팀은 처음에 node-dns 와 cluster-dns 의 성능을 의심하고 메트릭과 로그를 봤다. 두 DNS 서버는 요청을 정상으로 처리하고 있었고, 실제로 있는 도메인을 NXDOMAIN 으로 응답하지도 않았다. 그래서 애플리케이션과 node-dns 사이 경로에서 패킷이 사라진다고 보고 네트워크를 조사했다.

cAdvisor 의 container_network_transmit_packets_dropped_total 에서는 파드가 송신 중에 버린 패킷이 초당 100개쯤 나타났다. Node exporter 의 node_softnet_dropped_total 도 늘고 있었다. 이 지표는 커널이 패킷을 CPU 의 backlog 큐에 넣으려 할 때 큐가 가득 차서 버린 패킷 수다.

moco-1 을 조회하면 검색 도메인을 붙인 쿼리 다섯 개가 나간다. moco-1.default.svc.cluster.local 쿼리만 버려지고 나머지 쿼리가 node-dns 에 닿으면, 나머지 쿼리는 모두 NXDOMAIN 을 받으므로 애플리케이션은 no such host 를 냈다. 다섯 쿼리가 모두 버려지면 응답이 오지 않아 i/o timeout 이 났다.

증상 DNS 조회 실패간헐적 타임아웃패킷 손실

원인

촉발
MySQL 클러스터 감시 애플리케이션이 감시하는 클러스터 수가 늘어났고, 그 뒤부터 이름 조회 오류가 잦아졌다.

감지

Cloud Platform 팀은 쿠버네티스 플랫폼을 쓰는 팀에게서 클러스터 DNS 서버에 성능 문제가 있다는 문의를 받고 알게 됐다.

조치

후속 조치

교훈

netdev_max_backlog 를 늘리고 softnet 알림과 런북을 두면 같은 장애가 덜 나고, 나더라도 일찍 알아챌 수 있다. 다만 값을 늘리는 것은 근본 대책이 아니어서, 더 많은 패킷을 보내는 파드가 배포되면 backlog 큐가 다시 차고 패킷이 버려질 수 있다.

출처

참고 문서

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: 리눅스 커널, 클라이언트 쪽 DNS 리졸버와 캐시. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.