Incident Library

IL-0445 Chatwork · 2021-01-05 (게시일)kubernetesawsnetworking

EKS 로 옮기던 레거시 시스템에서 conntrack 테이블이 넘쳐 연결이 끊겼고, 이어 CoreDNS 파드가 축출될 때와 node-local-dns 를 넣은 뒤에 DNS 오류가 났다

Chatwork 는 2020년에 EC2 에서 돌던 레거시 시스템을 EKS 로 옮겼다. Route53 가중치로 두 쪽을 함께 돌리며 일정 비율이 넘는 요청을 EKS 로 보내자 서비스가 불안정해졌다. 일부 노드에서 conntrack 테이블이 넘쳐 애플리케이션으로 가는 연결이 끊겼고, 테이블을 채운 것은 CoreDNS 항목이었다. CoreDNS 는 EKS 기본값인 파드 2개뿐이어서 일부 CoreDNS 에 요청이 몰렸다. 다른 쿠버네티스 애플리케이션은 dns-autoscaler 가 처음부터 들어 있는 kube-aws 클러스터에서 돌았고, 팀은 EKS 에 dns-autoscaler 를 넣는 것을 잊고 있었다. 팀은 kube-proxy 설정에서 conntrack 의 maxPerCore 를 늘리고 dns-autoscaler 를 넣었다. CoreDNS 파드가 어느 정도 늘자 서비스가 크게 안정됐다.

그 뒤에도 노드가 모두 스폿 인스턴스여서, 노드가 바뀌며 CoreDNS 파드가 축출될 때 애플리케이션에 DNS 오류가 났다. 팀은 node-local-dns 를 넣었다. node-local-dns 는 각 노드에 DaemonSet 으로 실행되고, 노드가 사라질 때도 끝까지 남는다. 클러스터 전체의 DNS 요청과 CoreDNS 가 도는 노드의 conntrack 항목을 더 줄이려는 목적도 있었다.

node-local-dns 를 운영 환경에 apply 하자 DNS 가 다시 불안정해졌고, node-local-dns 로그에 timeout 이 잦았다. AWS 문서는 EC2 에서 CoreDNS 를 돌릴 때 force_tcp 를 쓰지 말라고 권한다. EC2 Nitro 인스턴스에서 DNS 요청을 전달하는 소프트웨어는 동시 TCP 연결을 2개까지만 받고, 연결마다 TCP 쿼리가 끝날 때까지 막힌다. 팀은 force_tcp 를 빼는 데서 그치지 않고 prefer_udp 로 바꿨고, DNS 가 안정됐다.

심각도
–
영향 범위
The legacy system being moved to EKS: connections to the application were dropped and applications got DNS errors
영향 사용자
–
지역
–
감지까지
–
복구까지
–

증상

Route53 가중치로 EC2 와 EKS 를 함께 돌리며 레거시 시스템을 옮기던 중, 일정 비율이 넘는 요청을 EKS 쪽으로 보내자 서비스가 갑자기 불안정해졌다. 일부 노드에서 conntrack 테이블이 한계를 넘어 애플리케이션으로 가는 연결이 드롭되고 있었다.

conntrack 테이블을 조사하니 CoreDNS 항목이 많았다. CoreDNS 는 EKS 기본값인 파드 2개로 돌고 있어서 일부 CoreDNS 에 요청이 몰렸고, 그 노드의 conntrack 테이블이 넘쳤다. 두 CoreDNS 파드의 CPU 도 꽉 차 있었다.

conntrack 상한을 늘리고 dns-autoscaler 를 넣은 뒤 대체로 안정됐지만, CoreDNS 파드가 사라질 때마다 애플리케이션에서 DNS 오류가 났다. Chatwork 의 노드 그룹은 모두 스폿 인스턴스여서 노드가 종종 교체되었고, 그때 CoreDNS 파드도 축출되었다. 글쓴이는 CoreDNS 가 축출되면서 처리 중이던 DNS 요청이 오류가 된 것으로 봤다. 자세히 추적하지는 않았지만 시점이 맞았다고 적었다.

node-local-dns 를 넣자마자 요청이 훨씬 많은 운영 환경에서 DNS 가 다시 불안정해졌다. test 와 stg 에서는 동작을 확인한 뒤였다. node-local-dns 의 로그에는 timeout 이 잦았고, 설정 파일에는 문제가 없어 보였다.

글쓴이는 eksctl 로 DNS 대상을 바꾼 노드 그룹을 만들기 전에는 node-local-dns 가 쓰이지 않는다고 생각하고 있었다. 그래서 timeout 이 잦자 크게 당황했다. 실제로는 node-local-dns 가 시작할 때 dummy device 와 iptables 설정을 만들어 kube-dns 로 가는 요청을 자기에게 돌리므로, 넣자마자 요청이 node-local-dns 를 거쳤다.

DNS 가 불안정하던 동안에는 DNS 조회가 막혀 파드 수도 쓸데없이 늘었다.

증상 간헐적 연결 실패DNS 조회 실패간헐적 타임아웃부하 불균형

원인

촉발
첫 번째 불안정은 Route53 가중치로 EKS 쪽에 보내는 요청 비율을 일정 이상으로 올리면서 시작됐다. 두 번째 불안정은 문서에 나온 매니페스트로 force_tcp 를 쓰는 node-local-dns 를 운영 환경에 apply 하면서 시작됐다.

감지

–

조치

후속 조치

–

교훈

EKS 에서 DNS 를 안정시킨 것은 dns-autoscaler 를 넣은 것과, force_tcp 없이 node-local-dns 를 넣은 것 두 가지였다. node-local-dns 를 쓸 때는 eksctl 로 kubelet 의 DNS 대상을 바꿀 필요가 없고, kube-dns 를 그대로 두면 된다. node-local-dns 를 업데이트하는 동안처럼 잠시 쓸 수 없을 때는 dummy device 덕에 애플리케이션의 DNS 요청이 CoreDNS 로 바로 가므로 클러스터 전체의 가용성이 오른다. DNS 가 불안정하면 조회가 막혀 파드가 쓸데없이 늘어나므로, 파드 수를 알맞게 유지하는 데도 DNS 안정이 중요하다.

출처

참고 문서

관련 사례

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

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: conntrack 테이블, CoreDNS, kube-proxy. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

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