Incident Library
  • 원인
  • 구성 요소
  • 증상
  • 기여 요인
  • 사례

사례7건

7건 중 1~5번째

  1. 증상

    처음 들어온 보고는 CI 가 간헐적으로 실패한다는 것이었다. 실패는 tensorstore 안에서 났고, 오류는 ./tensorstore/util/result.h:420: Status not ok: status(): UNAVAILABLE: CURL error Could not resolve hostname: Could not resolve host: company-bucket.s3.amazonaws.com 이었다.

    촉발

    호스트 네트워크를 쓰는 시스템 관리용 파드가 몇 초마다 sudo 명령을 실행했다. sudo 는 기본 설정에서 실행할 때마다 노드 자신의 호스트 이름을 조회했다. 파드의 resolv.conf 에 있는 ndots:5 와 검색 도메인 네 개 때문에 이 조회 하나가 A 와 AAAA 를 합쳐 질의 열 번이 됐고, CoreDNS 캐시에 걸리지 않은 질의가 VPC DNS 리졸버로 몰렸다.

    원인
    구성 요소
    기여 요인
    감지

    CI 가 간헐적으로 실패한다는 보고가 시작이었다. tensorstore 가 company-bucket.s3.amazonaws.com 의 DNS 조회에 실패했다는 오류를 냈다. 글쓴이는 kubectl logs 로 CoreDNS 파드 로그를 뒤져 i/o timeout 오류를 찾았다.

    조치
    • 컨테이너 이미지에 /etc/sudoers.d/00-no-fqdn 파일로 Defaults !fqdn 을 넣어 sudo 가 호스트 이름을 조회하지 않게 했다.
    교훈

    EC2 에서 링크 로컬 주소로 가는 VPC DNS 질의에는 초당 1024 패킷의 한도가 있다. 한도를 넘은 UDP 패킷은 버려지는데, 보낸 쪽은 이를 알 수 없다. 노드에서 ethtool -S eth0 | grep linklocal_allowance_exceeded 로 이 한도에 걸렸는지 볼 수 있다. ndots:5 와 검색 도메인 때문에 FQDN 이 아닌 이름 하나가 질의 다섯 번이 되고, AAAA 까지 물으면 열 번이 된다. sudo 는 기본 설정에서 실행할 때마다 호스트 이름을 조회하므로, sudo 를 자주 부르는 파드는 DNS 트래픽을 많이 만든다.

  2. 증상

    프로덕션 EKS 클러스터에서 원인을 바로 알기 어려운 서비스 에러가 간헐적으로 났다. RDS 나 Redis 같은 외부 서비스를 호출할 때 연결이 가끔 실패했다. DNS 요청이 몰릴 때는 도메인 조회 응답이 늦어지거나 조회가 아예 실패했다.

    촉발

    모든 CoreDNS 파드가 같은 노드에 배치되었고, 그 노드의 메모리 사용률이 급증해 메모리가 고갈됐다.

    원인
    구성 요소
    기여 요인
    감지

    글은 전면 장애를 어떻게 알아챘는지 적지 않았다. 그 전의 DNS 문제는 원인을 알기 어려운 서비스 에러가 간헐적으로 나면서 드러났다. 팀은 RDS 와 Redis 호출이 가끔 실패하는 현상을 추적하다가 DNS 이름 해석 실패가 근본 원인이라는 것을 확인했다.

    조치
    • CoreDNS 앞에서 DNS 질의를 받아 노드마다 캐시하는 NodeLocalDNS Cache 를 데몬셋으로 배포해 CoreDNS 부하를 줄였다.
    • CoreDNS 에 maxUnavailable: 1 인 PodDisruptionBudget 과 topology.kubernetes.io/zone 기준의 topologySpreadConstraints 를 적용해 파드가 한 곳에 몰리지 않게 했다.
    • Deployment 의 dnsConfig 로 ndots 값을 5 에서 2 로 낮춰 불필요한 DNS 조회를 줄였다.
    교훈

    쿠버네티스의 ndots:5 기본값은 점이 적은 외부 도메인을 조회할 때마다 질의를 여러 번 만든다. 이 부하는 트래픽이 큰 프로덕션 환경에서야 드러난다. CoreDNS 를 파드 2개의 기본 구성으로 두면 클러스터 전체의 단일 장애점이 된다. CoreDNS 파드는 노드와 가용 영역에 흩어 둔다. 별도 노드 그룹이나 Fargate 로 분리하면 다른 워크로드와 자원을 다투지 않는다. Affinity 와 PriorityClassName 으로 우선순위를 주면 더 안정적이다. CoreDNS 의 캐시와 bufsize 조정은 증상을 잠깐 줄였을 뿐이다. ndots 값은 여러 자료가 1이나 2를 권하지만 각 환경에 맞춰 정해야 한다.

  3. 증상

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

    촉발

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

    원인
    구성 요소
    기여 요인
    감지

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

    조치
    • 커널 파라미터 net.core.netdev_max_backlog 값을 늘렸다.
    • softnet 지표에 알림을 설정했다.
    • softnet 알림에 대응하는 런북을 마련했다.
    후속 조치
    • veth 인터페이스를 netkit 인터페이스로 바꿔 파드의 송신 패킷이 backlog 큐를 거치지 않고 물리 장치로 바로 가게 하는 방안을 검증한다. netkit 은 리눅스 커널 6.7 에 들어온 가상 네트워크 장치이고, Cilium 1.16 부터 쓸 수 있다.
    교훈

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

  4. 증상

    남은 엔드포인트의 요청 비율을 EKS 쪽으로 1% 에서 25% 로 올리자 Sentry 에 DNS 이름 해석 오류가 대량으로 올라왔다. API 가 데이터베이스와 외부 서비스에 연결하지 못했다.

    촉발

    ALB 로 엔드포인트마다 요청 비율을 바꾸며 이전하던 중, 남은 엔드포인트의 요청 비율을 EKS 쪽으로 1% 에서 25% 로 올렸다.

    원인
    구성 요소
    기여 요인
    감지

    요청 비율을 EKS 쪽으로 올린 직후 Sentry 에 DNS 이름 해석 오류가 대량으로 올라온 것을 확인했다.

    조치
    • 오류를 멈추려고 요청 비율을 VM 쪽으로 되돌렸다.
    • 임시 조치로 HPA 리소스를 만들어 CoreDNS 파드를 오토스케일하게 했고, 그 뒤 EKS 이전을 마쳤다.
    후속 조치
    • 애플리케이션 Deployment 의 dnsConfig 에 ndots 값 "1" 을 넣어 파드가 항상 FQDN 으로 이름을 해석하게 했다.
    • CoreDNS ConfigMap 의 Corefile 에서 cache 를 600 으로 바꿔 캐시 TTL 을 늘렸다.
    • CoreDNS ConfigMap 의 health 플러그인에 lameduck 30s 를 넣어, 스케일인 때 파드가 바로 끝나지 않고 남은 처리를 이어 가게 했다.
    • 운영 환경과 스테이징 환경의 차이를 줄이는 것을 비용과 균형을 보며 검토한다.
    교훈

    MonotaRO 는 관리형 서비스이니 문제가 없으리라 짐작하고 사전 사례 조사, 부하 시험, 감시 같은 측정을 하지 않아 이번 문제가 났다고 보았다. 그래서 짐작하지 말고 측정하라는 원칙을 다시 새겼다. 운영 환경과 스테이징 환경의 차이가 컸다. 네트워크 구성이 달라 환경마다 ndots 가 달랐고, 스테이징 환경의 데이터베이스 사양으로는 고부하 시험을 할 수 없었다. 직접 관리하지 않는 부분의 리소스도 잘 알지 못했다. 쿠버네티스로 옮긴 다른 회사의 사례를 조사했다면 이번 문제를 어느 정도 예측할 수 있었다고 했다.

  5. 증상

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

    촉발

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

    원인
    구성 요소
    기여 요인
    조치
    • conntrack 상한이 기본값과 다른 값으로 설정되어 있었고 그 값은 kube-proxy 가 정하고 있었다. 팀은 kube-proxy 설정에서 conntrack 의 maxPerCore 를 늘렸다.
    • EKS 에 빠져 있던 dns-autoscaler 를 넣었다. CoreDNS 파드가 어느 정도 늘자 서비스가 크게 안정됐다.
    • CoreDNS 파드가 축출될 때 나던 DNS 오류를 없애려고 각 노드에 DaemonSet 으로 실행되는 node-local-dns 를 넣었다.
    • node-local-dns 설정에서 force_tcp 를 빼고 prefer_udp 로 바꾸자 DNS 가 안정됐다.
    교훈

    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 안정이 중요하다.

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

이 7건에서 가장 많이 나오는 구성 요소: 클라이언트 쪽 DNS 리졸버와 캐시, CoreDNS. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별1

용량 한계7건

  1. IL-0048 대형 EKS 클러스터에서 CoreDNS 가 VPC DNS 리졸버로 보내는 질의가 링크 로컬 트래픽의 초당 1024 패킷 한도에 걸려 DNS 조회가 간헐적으로 타임아웃됐다.
  2. IL-0027 CoreDNS 파드가 모두 한 노드에 몰린 채 그 노드의 메모리가 고갈되자 DNS 가 응답하지 못해 아이디어스 서비스 전체가 멈췄다
  3. IL-0440 파드가 보낸 DNS 쿼리 패킷이 노드에서 버려져 클러스터 내부 서비스의 이름 조회가 no such host 와 i/o timeout 으로 자주 실패했다
    Cybozu2024
  4. IL-0433 EC2 에서 EKS on Fargate 로 트래픽을 옮기던 중 CoreDNS 과부하로 이름 해석 오류가 쏟아져 롤백했다
  5. IL-0445 EKS 로 옮기던 레거시 시스템에서 conntrack 테이블이 넘쳐 연결이 끊겼고, 이어 CoreDNS 파드가 축출될 때와 node-local-dns 를 넣은 뒤에 DNS 오류가 났다
  6. IL-0033 kube-dns 파드 사이의 트래픽 불균형과 DNS 초당 패킷 한도 때문에 외부 DNS 조회가 많은 여러 서비스의 지연 시간이 20초까지 늘었다
    Airbnb2019
  7. IL-0001 CoreDNS 파드가 메모리 한도 100Mi 에 걸려 OOM 으로 계속 죽으면서 쿠버네티스 클러스터 전체의 DNS 가 멈췄다