Incident Library
적용된 필터 구성 요소kubelet기여 요인한계 미인지모두 풀기검색어 없이 필터만 적용한 목록입니다. 최근 사례부터입니다.
추천 필터 검색하면 검색어에서 찾은 분류 값이 여기에 나옵니다.

그래프

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

사례3건

3건 중 1~3번째

  1. 증상

    워커 노드 한 대에서 NodeFilesystemFilesFillingUp 알림이 울려 담당자가 호출됐다. inode 가 바닥날 추세라는 알림이었다.

    촉발

    단일 스테이지로 빌드한 이미지에 소스, 빌드 결과물, 개발 의존성까지 포함한 node_modules 전체가 들어갔고, 새 레이어 다이제스트가 생길 때마다 노드에 새 스냅샷이 풀렸다.

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

    node_exporter mixin 에 들어 있는 NodeFilesystemFilesFillingUp 알림이 inode 사용률 67% 에서 울렸다. predict_linear 로 inode 가 바닥날 시점을 예측하는 추세 알림이다.

    후속 조치
    • 멀티 스테이지 Dockerfile 로 바꿔, 한 스테이지에서 의존성을 설치하고 빌드한 뒤 최종 이미지에는 nginx:alpine 위에 컴파일된 결과물만 넣는다.
    • 추세 알림과 별도로 루트 파일 시스템의 inode 사용률이 0.80 을 넘으면 울리는 고정 기준 알림을 for: 30m 으로 둔다.
    • 노드마다 inode 사용률에서 디스크 사용률을 뺀 값을 큰 순서로 보여 주는 Prometheus 쿼리를 대시보드에 둔다.
    • 노드에 이미 쌓인 오래된 스냅샷은 crictl rmi --prune 으로 어떤 컨테이너도 쓰지 않는 이미지를 지워 정리한다.
    교훈

    kubelet 의 백그라운드 정리는 바이트를 기준으로 돈다. 용량보다 파일 수가 먼저 차는 워크로드에는 미리 손쓸 장치가 없다. kubelet 은 inode 가 5% 남았을 때 파드 축출로 처음 개입한다. 파드 축출은 노드를 지키는 마지막 수단일 뿐 해결책이 아니다. ext4 의 inode 수는 포맷할 때 정해진다. 작은 파일이 많으면 디스크가 넉넉해도 inode 가 먼저 바닥난다. 추세 알림은 증가가 잠시 멈추면 조용해진다. kubelet 의 95% eviction 지점보다 한참 앞에서 울리는 고정 기준 알림을 함께 둔다. node_modules 뿐 아니라 Python virtualenv, 벤더링한 PHP 패키지, Ruby gem 처럼 작은 파일이 많은 의존성 트리를 담은 이미지는 모두 같은 문제를 일으킨다.

  2. 증상

    JupyterHub 의 KubeSpawner 로 컨테이너를 만들 때 처음 보는 오류가 나며 생성이 실패했다. 오류는 SSL 인증서가 만료됐다는 내용이었다.

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

    KubeSpawner 가 컨테이너를 만들다가 처음 보는 SSL 인증서 오류를 냈다. 글쓴이는 오류 로그를 검색해 클러스터 내부 인증서 만료가 원인임을 알았다.

    조치
    • `kubeadm alpha certs check-expiration` 으로 인증서별 남은 기간을 확인했다.
    • 기존 인증서 파일과 `/etc/kubernetes/*.conf` 파일을 백업 디렉터리로 옮겼다.
    • `kubeadm init phase certs all` 로 모든 인증서를 새로 만들었다.
    • `kubeadm init phase kubeconfig all` 로 admin.conf 를 비롯한 kubeconfig 파일을 새로 만들었다.
    • 새 admin.conf 를 `~/.kube/config` 로 복사하고 kubelet 을 재시작했다.
    • 예전 인증 정보를 들고 있던 kube-system 컨테이너들을 `docker restart` 로 모두 재시작했다.
    교훈

    kubeadm 클러스터의 인증서가 만료되면 kubectl 이 API 서버에 연결하지 못하므로 kubectl 로는 갱신할 수 없고, 노드에서 kubeadm 으로 직접 갱신해야 한다. 갱신 뒤에는 인증서만이 아니라 kubeconfig 파일도 새로 만들어야 하고, 예전 인증 정보를 들고 있는 kube-system 파드도 재시작해야 한다. 글쓴이는 클러스터를 처음부터 직접 구성해 봤다면 인증서를 쓴다는 것을 바로 알았을 것이고, 명령 한 줄로 막을 수 있는 문제였다고 돌아봤다.

  3. 증상

    Omio 는 6개월 동안 원인을 알 수 없는 멈춤을 겪었다. 애플리케이션이 멈추거나 헬스 체크에 응답하지 않았고, 네트워크 연결이 끊겼다.

    촉발

    쿠버네티스는 CPU limit 을 커널 CFS 의 쿼터로 구현한다. 주기는 100ms 이고, limit 2코어는 주기마다 CPU 시간 200ms 가 된다. 스레드 10개가 동시에 돌면 20ms 만에 쿼터를 다 쓰고, 남은 80ms 동안 파드의 모든 스레드가 멈춘다. 여기에 커널 CFS 스케줄러의 알려진 버그가 불필요한 스로틀링을 일으켜, 컨테이너가 허용된 쿼터를 다 쓰기도 전에 스로틀링됐다.

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

    팀은 6개월 동안 애플리케이션이 멈추거나 헬스 체크에 응답하지 않는 현상을 겪었지만, 쿠버네티스가 cgroup 지표를 내보내지 않아 스로틀링을 바로 알아채지 못했다. 파드 안에서 /sys/fs/cgroup/cpu/cpu.stat 의 nr_throttled 와 throttled_time 을 읽어 스로틀링 비율을 확인했다.

    조치
    • 버그를 고친 최신 커널이 모든 클러스터에 배포될 때까지 CPU limit 을 껐다.
    후속 조치
    • CFS 쿼터 버그를 고친 최신 커널을 모든 클러스터에 배포한다.
    • 사용률, 포화도, 오류를 보는 USE 지표와 함께 API 지연과 오류율을 지켜보며 결과가 기대한 대로인지 확인한다.
    교훈

    CFS 쿼터 버그가 고쳐지지 않은 커널을 쓴다면 쿠버네티스에서 CPU limit 을 없애거나 kubelet 에서 CFS 쿼터를 끄는 편이 낫다. CPU request 와 limit 은 설정이 비슷해 보여도 구현이 전혀 다르다. CPU 스로틀링은 쿠버네티스 기본 지표에 드러나지 않으므로 cpu.stat 을 직접 확인해야 한다. limit 을 끄면 최적화되지 않은 애플리케이션이 노드의 CPU 를 독차지할 수 있으니, JVM, Go, Node 런타임을 미리 튜닝해 둬야 한다.

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

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

원인별3