IL-0071 Onkar Raghunath Shelke · 2026-09-17 (게시일)kubernetesobservability
글쓴이가 운영하던 클러스터에서 워커 노드 한 대의 inode 가 바닥날 추세라는 알림이 왔다. 디스크 사용률은 83%, inode 사용률은 67% 였다. 알림이 장애로 이어지기 전에 문제를 잡았다. 글쓴이는 지금 그 클러스터에 접근할 수 없어서, 일부 수치는 장애 기록에서 가져왔고 나머지는 계산으로 되짚었다고 밝혔다.
kubelet 은 이미지가 노드를 채우는 것을 이미지 가비지 컬렉션으로 막는다. 이미지 파일 시스템 사용률이 imageGCHighThresholdPercent 의 기본값인 85 를 넘으면, kubelet 은 쓰지 않는 이미지를 오래된 것부터 지워 imageGCLowThresholdPercent 의 기본값인 80 까지 내린다. 두 값 모두 바이트 기준이고 파일 수는 보지 않는다. 리눅스의 기본 하드 eviction 설정에는 nodefs.inodesFree 와 imagefs.inodesFree 가 모두 5% 로 들어 있다. 그러나 이는 노드를 지키려고 파드를 축출하는 마지막 단계다. 바이트에는 85% 에서 도는 이미지 가비지 컬렉션과 하드 eviction 두 장치가 있지만, inode 에는 하드 eviction 하나뿐이다. inode 가 모자랄 때 처음 일어나는 일이 가장 큰 영향을 주는 일이다.
ext4 는 파일 시스템을 만들 때 inode_ratio 로 inode 수를 정하고 그 뒤로 바꾸지 않는다. 기본 /etc/mke2fs.conf 값은 16384 다. 비어 있지 않은 파일은 아무리 작아도 4 KiB 블록 하나를 쓰므로, 작은 파일만 있으면 디스크의 25% 를 쓴 시점에 inode 가 바닥난다. 이 노드는 inode 가 모두 약 1,630만 개로, 계산상 8192 정도의 비율로 포맷된 것으로 보였다. 이 비율이면 작은 파일만 있을 때 디스크의 50% 를 쓴 시점에 inode 가 바닥난다.
문제는 클러스터가 아니라 이미지였다. kubelet, containerd, 노드 이미지는 모두 기본값으로 돌고 있었다. 이미지는 단일 스테이지 빌드여서 소스, 빌드 결과물, 개발 의존성을 포함한 node_modules 가 모두 들어갔다. containerd 의 콘텐츠 저장소는 압축된 레이어를 다이제스트로 중복 제거하지만, overlayfs 스냅샷터는 레이어마다 따로 풀고 서로 공유하지 않는다. .dockerignore 에서 node_modules 를 빼지 않았거나 CI 가 레이어 캐시 없이 매번 npm install 을 다시 돌리면, 내용이 같아도 다이제스트가 달라져 새 스냅샷이 생긴다. 이 스냅샷은 이미지가 지워질 때까지 남고, kubelet 은 바이트 기준 85% 에 닿아야 이미지를 지운다. 사람들이 주로 보는 디스크 사용률이 기준선을 넘지 않았기 때문에 아무도 알아채지 못했다.
워커 노드 한 대에서 NodeFilesystemFilesFillingUp 알림이 울려 담당자가 호출됐다. inode 가 바닥날 추세라는 알림이었다.
담당자는 누구나 그렇듯 디스크부터 봤다. 디스크는 83% 사용 중으로 103G 를 쓰고 22G 가 남아 있었다. inode 는 67% 사용 중으로 11M 을 쓰고 5.3M 이 남아 있었다. 더 찬 쪽은 디스크였지만 알림은 inode 쪽에서 왔다. NodeFilesystemFilesFillingUp 은 predict_linear 로 만든 추세 알림이라, 현재 수준이 아니라 늘어나는 기울기를 보고 울린다.
du --inodes -xS /var | sort -rh | head -n 20 으로 파일이 많은 디렉터리를 찾았다. 맨 위는 로그도 파드 볼륨도 아니었다. containerd 의 스냅샷 저장소인 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/ 였다. 여러 스냅샷에 app/node_modules/@mui/icons-material 이라는 같은 경로가 들어 있었고, 스냅샷마다 파일이 21,553개였다. 이미지 하나에 든 파일은 모두 4만 개가 넘었다.
증상 ? 노드 디스크 가득 참
node_exporter mixin 에 들어 있는 NodeFilesystemFilesFillingUp 알림이 inode 사용률 67% 에서 울렸다. predict_linear 로 inode 가 바닥날 시점을 예측하는 추세 알림이다.
–
kubelet 의 백그라운드 정리는 바이트를 기준으로 돈다. 용량보다 파일 수가 먼저 차는 워크로드에는 미리 손쓸 장치가 없다. kubelet 은 inode 가 5% 남았을 때 파드 축출로 처음 개입한다. 파드 축출은 노드를 지키는 마지막 수단일 뿐 해결책이 아니다.
ext4 의 inode 수는 포맷할 때 정해진다. 작은 파일이 많으면 디스크가 넉넉해도 inode 가 먼저 바닥난다.
추세 알림은 증가가 잠시 멈추면 조용해진다. kubelet 의 95% eviction 지점보다 한참 앞에서 울리는 고정 기준 알림을 함께 둔다.
node_modules 뿐 아니라 Python virtualenv, 벤더링한 PHP 패키지, Ruby gem 처럼 작은 파일이 많은 의존성 트리를 담은 이미지는 모두 같은 문제를 일으킨다.
같은 원인 · 공통 노드 루트 디스크, 컨테이너 런타임, kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet