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

사례4건

4건 중 1~4번째

  1. 증상

    프로덕션 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를 권하지만 각 환경에 맞춰 정해야 한다.

  2. 증상

    트래픽이 급하게 오르내리는 환경에서 CoreDNS 에 부하가 몰렸다. 네트워크 문제가 생기고 응답 지연이 크게 늘었다.

    촉발

    트래픽이 급변하면서 수가 적은 CoreDNS 파드에 처리할 수 있는 양보다 많은 부하가 몰렸다.

    원인
    구성 요소
    기여 요인
    조치
    • 클러스터 크기에 맞춰 CoreDNS replica 를 늘리도록 CPA 를 설정하고, 인스턴스 타입에 맞게 nodesPerReplica, coresPerReplica, replica 최솟값과 최댓값을 조정했다.
    • CoreDNS 에 anti-affinity 대신 ScheduleAnyway 로 느슨하게 한 topologySpreadConstraints 를 걸어, Karpenter 의 Consolidation 과 Drift 가 CoreDNS 에 막히지 않게 했다.
    • CoreDNS 의 Error Rate, Panics, Request Duration, Request Rate 에 임곗값 알림을 걸었다.
    • NodeLocal DNSCache 를 적용해 노드마다 DNS 캐시를 두고 메인 CoreDNS 의 부하를 낮췄다.
    교훈

    CoreDNS 가 감당할 수 있는 양은 파드에 준 리소스에 따라 다르고, 그에 따라 평균 지연과 초당 질의 수인 QPS 도 달라진다. CoreDNS replica 를 무작정 늘리면 taint 와 topologySpreadConstraints 때문에 파드가 몇몇 노드에 몰려 Karpenter 의 Consolidation 을 막을 수 있다. 노드 하나가 드레인될 때 그 노드의 CoreDNS 가 한꺼번에 많이 종료되면 서비스에 영향이 갈 수 있다. NodeLocal DNSCache 를 쓰면 메인 CoreDNS 로 가는 DNS 질의가 크게 줄고 스파이크를 다루기 쉬워진다. 어느 노드에서 문제가 생겨도 메인 CoreDNS 는 영향을 받지 않는다. 그래서 서비스 전체 장애로 번질 일이 노드 하나의 문제로 그친다. 글쓴이는 CPA 와 NodeLocal DNSCache 를 함께 적용해 CoreDNS 부하 문제를 거의 없앴다고 보았다.

  3. 증상

    서비스 구조를 바꾸고 노드 풀 크기를 조정하고 하위 서비스를 샤딩한 직후, 집계 서비스에 여러 연결 문제가 생기고 오류율이 올라갔다.

    촉발

    집계 서비스가 도는 노드의 RAM 을 절반으로 줄였고, 하위 서비스를 샤딩하면서 추적해야 할 연결 수는 두 배가 됐다. conntrack_max 는 노드 RAM 에 비례하므로 노드가 추적할 수 있는 연결 수는 줄고 추적할 연결은 늘었다.

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

    팀은 먼저 서비스의 연결 문제와 오류율 상승을 알아챘다. HAProxy 가 보고하는 연결 리셋 오류가 계속 늘었고, 팀은 node_nf_conntrack_entries 를 node_nf_conntrack_entries_limit 과 겹쳐 보고 일부 노드의 conntrack 테이블이 바닥난 것을 확인했다.

    조치
    • 집계 서비스를 RAM 이 더 큰 노드에 배치했다.
    • 노드 하나에 뜨는 집계 서비스 파드 수를 제한했다.
    후속 조치
    • conntrack 사용률이 75% 를 넘으면 경보가 가도록 Prometheus 쿼리 (node_nf_conntrack_entries / on (pod) node_nf_conntrack_entries_limit / on (pod) group_right kube_pod_info) > 0.75 로 경보 규칙을 만들었다.
    교훈

    쿠버네티스 노드의 conntrack_max 는 노드 RAM 에 비례해 정해진다. 부하가 큰 애플리케이션은 특히 작은 노드에서 conntrack_max 를 쉽게 넘겨 연결 리셋과 타임아웃을 일으킨다. 노드를 작게 바꾸면 추적할 수 있는 연결 수도 함께 줄어든다. conntrack 사용률은 지표로 보고 경보를 걸어 둬야 한다.

  4. 증상

    1월 18일 금요일, Moonlight API 가 가끔 오류를 냈다. 처음 오류는 Redis 데이터베이스와 연결이 안 되는 문제로 보였다. Moonlight API 는 인증된 요청마다 Redis 에서 세션을 확인한다.

    촉발

    쿠버네티스 스케줄러가 CPU 를 많이 쓰는 파드 여러 개를 같은 노드에 배치했다. 이 파드들이 노드 CPU 를 100% 쓰자 노드 VM 이 커널 패닉으로 죽었다.

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

    API 오류가 가끔 나면서 문제가 드러났다. 쿠버네티스 모니터링이 일부 노드와 파드가 응답하지 않는다고 보고했다. 노드가 CPU 100% 상태에서 커널 패닉으로 죽는다는 패턴은 Google Cloud 지원팀이 찾아냈다.

    조치
    • 주요 배포 전부에 안티 어피니티 규칙을 넣어 파드를 여러 노드에 흩어 놓았다.
    후속 조치
    • Google Cloud 지원팀과 함께 노드 커널 패닉의 근본 원인을 찾아 고친다.
    교훈

    복제본을 세 개 두어도 스케줄러가 한 노드에 몰아 놓으면 그 노드가 단일 장애점이 된다. 사용자에게 응답하는 서비스에는 안티 어피니티 규칙을 넣어 파드를 여러 노드에 나눠 놓아야 한다. 팀은 처음에 클라우드 업체의 네트워크 장애를 원인으로 짐작했지만 실제 원인은 노드의 CPU 포화와 커널 패닉이었다.

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

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

원인별1