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

사례6건

6건 중 1~5번째

  1. 증상

    2025년 7월 9일 OpenShift 위의 Klamm 캐시 Redis 클러스터가 멈췄다. Redis 로그에는 "Fail to fsync the AOF file: No space left on device" 오류가 남았다. AOF 는 Redis 가 쓰기 명령을 차례로 덧붙여 저장하는 파일이다.

    촉발

    Klamm Laravel 워커 하나에서 I/O 작업이 예상하지 못하게 급증해 Redis 캐시 요청이 크게 늘었고, AOF 가 빠르게 커지면서 Redis 마스터의 영구 볼륨을 채웠다.

    원인
    구성 요소
    기여 요인
    조치
    • Redis 스테이트풀셋과 디플로이먼트를 멈췄다.
    • 깨진 PVC 를 지우고 다시 만들었다. 이 과정에서 Redis 볼륨의 데이터를 잃었다.
    • 스테이트풀셋과 디플로이먼트를 다시 시작했다.
    후속 조치
    • Klamm Redis 캐시 볼륨을 100Mi 로 늘린다.
    • 네임스페이스에 Sysdig 모니터링을 설정한다.
    • Redis 클러스터에서 레플리카 파드를 쓰도록 설정한다.
  2. 증상

    2025년 5월 16일, Neon 의 AWS us-east-1 클러스터에서 새 파드가 스케줄링은 되지만 IP 할당 오류를 내며 시작하지 못했다. 새 컴퓨트 파드를 시작할 수 없어 유휴 데이터베이스를 깨울 수도 없었다.

    촉발

    5월 16일 유휴 컴퓨트를 종료하는 Neon 컨트롤 플레인의 주기 작업이 실패했다. 종료는 멈췄는데 생성은 계속되어 활성 컴퓨트가 몇 분 만에 약 8천 개로 늘었다. 5월 19일에는 후속 조치로 WARM_IP_TARGET=1 을 다시 배포한 것이 계기가 되어 같은 장애가 다시 났다.

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

    노드가 추가되면서 새 파드가 스케줄링은 되지만 시작하지 못하고 IP 할당 오류를 내는 것이 보였다. 5월 19일에는 WARM_IP_TARGET=1 배포 직후 IP 배정 실패와 파드 시작 성공률 하락이 바로 나타났다.

    조치
    • 5월 16일 노드에 묶인 IP 를 풀려고 WARM_IP_TARGET=1 을 설정했다.
    • 컨트롤 플레인의 실패하던 주기 작업을 안정시켜 유휴 컴퓨트 약 7천 개를 종료했다.
    • aws-node DaemonSet 을 재시작해 ipamd 를 다시 초기화했고, 재시작할 때마다 잠시 나아졌다.
    • WARM_IP_TARGET, MINIMUM_IP_TARGET, WARM_ENI_TARGET 을 모두 0 으로 바꿔 파드 시작 성공률을 정상으로 되돌렸다.
    • 5월 19일 WARM_IP_TARGET=1 을 다시 배포했다가 같은 증상이 나오자 0 으로 되돌렸다.
    • 미리 만들어 두는 컴퓨트 풀을 늘려 약 10% 의 파드 시작 실패를 메우고 사용자 영향을 줄였다.
    후속 조치
    • ipamd 의 동작을 개선하는 PR 을 AWS CNI 에 올렸다.
    • 서브넷에서 IP 약 100개가 왜 할당되지 않고 남았는지 AWS Support 와 확인한다.
    • 수평 확장을 지원하도록 아키텍처를 바꾼다.
    교훈

    AWS CNI 기본 설정에서는 노드마다 적어도 ENI 한두 개 분량의 IP 가 더 잡히고, IP 는 ENI 가 통째로 비어야만 반납된다. 그래서 파드에 배정된 IP 수만 보고 서브넷 여유를 판단하면 안 된다. 이번에는 IP 의 71% 만 파드에 배정된 상태에서 서브넷이 바닥났다. WARM_IP_TARGET 은 지정하지 않을 때, 0 일 때, 1 일 때 동작이 서로 크게 다르다. cooldown 중인 IP 도 여유 IP 로 세기 때문에 WARM_IP_TARGET=1 은 파드가 지워질 때마다 그 노드의 파드 시작을 30초 동안 막을 수 있다. 문서를 잘못 읽고 바꾼 설정도, 원래대로 돌린 설정도 예상과 다르게 동작했고, 되돌리는 것만으로는 망가진 상태가 회복되지 않았다. 서브넷 크기는 부하 시험에서 정한 상한인 1만 컴퓨트를 넘지 않는다는 가정에 맞춰 잡혀 있었다. 서비스가 예상보다 빨리 커지면서 이 가정이 맞지 않게 됐다.

  3. 증상

    어느 저녁 일부 DaemonSet 이 실패하고 있다는 호출이 왔다. 당직자가 서버에 들어가 파드 상태를 보니 다음 오류가 찍혀 있었다: name=“aws-cni” failed (add): add cmd: failed to assign an IP address to container

    촉발

    첫 호출의 계기는 원문에 나오지 않는다. 호출은 6월, kube-aws 기반 SCHIP 에서 EKS 기반 클러스터로 옮기던 중에 왔다. 원문은 클러스터의 IP 가 바닥나는 경우로, 잡이 폭주하거나 실패하면서도 회수될 때까지 IP 를 붙잡고 있어 파드가 갑자기 늘어나는 경우를 든다.

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

    어느 저녁 일부 DaemonSet 이 실패한다는 호출이 왔다. 당직자가 서버에 들어가 파드 상태를 확인하다 aws-cni 의 IP 할당 실패 오류를 보았다. 팀은 그전부터 IP 사용량을 감시하고 있었다.

    조치
    • 8월 말부터 작은 클러스터에 Secondary CIDR 를 쓰는 VPC-CNI 커스텀 네트워킹을 배포했다.
    • 9월에 사용 가능한 IP 가 너무 적어지자 EKS 이전을 잠시 멈췄다.
    • 커스텀 네트워킹을 나머지 클러스터 전체에 배포했다.
    • Karpenter 배포에 --reserved-enis 파라미터를 더했다.
    후속 조치
    • 커스텀 네트워킹을 나머지 클러스터 전체에 배포해 IP 여유를 되찾고 EKS 이전을 끝냈다.
    • Karpenter 배포에 --reserved-enis 파라미터를 더해 커스텀 네트워킹이 쓰는 첫 번째 ENI 를 빼고 노드당 파드 수를 계산하게 했다.
    교훈

    플랫폼 팀이나 클러스터 운영자가 용량 계획에 시간을 들이지 않으면, VPC-CNI 는 큰 규모의 운영 환경에 바로 쓸 수 있는 도구가 아니다. 선택지마다 장단점이 있다. 지금 큰 클러스터를 새로 띄운다면 더 싸고 앞으로도 오래 쓸 수 있는 IPv6 를 쓸 수 있는지 먼저 확인할 만하다. 작은 클러스터에서 되던 것이 규모를 키워도 된다고 가정하면 안 된다. 예측과 용량 계획을 잘하면 이 요구는 감당할 수 있다. 하지만 폭주하는 잡이나, 실패하면서도 회수될 때까지 IP 를 붙잡는 잡을 여럿 띄우는 잘못된 설정 때문에 파드가 갑자기 늘 수 있다.

  4. 증상

    어느 월요일 아침, 엔지니어가 쿠버네티스 클러스터 경고를 보려고 Prometheus 대시보드를 열었는데 접속되지 않았다. Prometheus 서버 파드는 CrashLoopBackOff 상태였다.

    촉발

    Prometheus 가 시작한 지 5분 만에 시계열 수가 2,162,256 까지 급증해 메모리 8GB 호스트의 메모리를 거의 다 썼다.

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

    월요일 아침 엔지니어가 클러스터 경고를 확인하려고 Prometheus 대시보드를 열었다가 접속되지 않는 것을 보고 알았다.

    조치
    • Prometheus configMap 에서 불필요한 설정 대부분을 지워 컨테이너가 더 이상 비정상 종료되지 않게 했다.
    후속 조치
    • Prometheus 파드의 requests 와 limits 를 알맞게 고친다. 지금 문제를 완전히 풀지는 못해도 비슷한 문제를 막아 해결 시간을 줄인다.
    • 불필요한 수집 지표를 줄이도록 수집 대상이나 수집 간격을 조정한다.
    • Prometheus 에 필요한 메모리를 예측해 새 호스트를 마련하고 deploy-topology: Prometheus 같은 노드 레이블을 붙인 뒤, 매니페스트에 nodeSelector 를 넣어 Prometheus 파드가 그 호스트에만 배치되게 한다.
    교훈

    원인은 둘이었다. 첫째, Prometheus 파드의 requests 와 limits 를 잘못 잡아 Prometheus 서버 컨테이너가 높은 oom_score_adj 를 받았다. 둘째, 대규모 클러스터에서 Prometheus 가 쓸 메모리를 미리 예측하지 않았다. 쿠버네티스에서 파드의 QoS 클래스는 메모리가 모자랄 때 어느 컨테이너가 먼저 죽는지에 영향을 준다. 커널의 OOM Killer 는 메모리 사용량과 oom_score_adj 로 점수를 매기고, kubelet 은 QoS 클래스에 따라 oom_score_adj 를 정한다.

  5. 증상

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

    촉발

    집계 서비스가 도는 노드의 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 사용률은 지표로 보고 경보를 걸어 둬야 한다.

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

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

원인별1

용량 한계6건

  1. IL-0067 Redis 마스터의 50Mi 영구 볼륨이 가득 차 AOF 가 깨졌고, Redis 파드가 크래시 루프에 빠져 Klamm 운영 서비스가 멈췄다
  2. IL-0006 AWS VPC CNI 가 자원이 찬 노드에 IP 를 붙잡아 두어 us-east-1 서브넷 IP 가 바닥났고, WARM_IP_TARGET 을 바꾼 조치가 파드 시작 실패를 더 오래 끌었다
    Neon2025
  3. IL-0007 EKS 클러스터에서 VPC-CNI 가 파드마다 VPC IP 를 쓰다가 서브넷 IP 가 바닥나 파드가 IP 를 받지 못했다
  4. IL-0176 시계열 급증으로 호스트 메모리가 바닥나자 oom_score_adj 999 를 받은 Prometheus 컨테이너가 커널 OOM Killer 에게 먼저 죽어 CrashLoopBackOff 에 빠졌다.
    LINE2020
  5. IL-0013 노드 RAM 을 절반으로 줄이자 conntrack 테이블이 가득 차 집계 서비스에 연결 리셋과 오류가 늘었다
  6. IL-0075 클러스터가 EC2 인스턴스 약 290대로 커지자 노드마다 도는 Datadog 에이전트가 쿠버네티스 API 를 폴링하면서 API 서버가 CPU 와 메모리를 다 써 쿠버네티스 컨트롤 플레인을 쓸 수 없게 됐다.