- Published on
용량 남는데 No space left? inode 고갈 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 df -h를 찍어보면 디스크 사용률이 30~40%인데도, 로그가 안 써지거나 패키지 설치가 실패하면서 No space left on device가 튀어나오는 경우가 있습니다. 이때는 “용량(space)”이 아니라 **inode(파일 개수 한도)**가 바닥난 상황일 가능성이 큽니다.
특히 수많은 작은 파일을 만드는 워크로드(웹 캐시, 빌드 산출물, 컨테이너 로그, 임시파일, 메일 스풀, 이미지 썸네일 등)에서 자주 발생합니다. 이 글에서는 inode가 무엇인지부터 어디서 inode가 고갈되는지 추적하고, 안전하게 정리하며, 재발을 막는 운영 팁까지 한 번에 정리합니다.
> 쿠버네티스 노드에서 이 문제가 나면 Pod가 갑자기 죽거나 로그가 끊기고, 심하면 노드 상태가 불안정해질 수 있습니다. 노드 장애 트러블슈팅 관점은 EKS Node NotReady - CNI ENI 할당 실패 해결 가이드 같은 글과 함께 보면 “노드 레벨 자원 고갈”을 체계적으로 보는 데 도움이 됩니다.
증상: 용량은 남는데 왜 저장이 안 되나?
대표 증상은 다음과 같습니다.
touch a/echo hi > file가No space left on device로 실패apt install,yum install중 임시 파일 생성 실패- 애플리케이션 로그가 더 이상 기록되지 않음
- Docker/Containerd가 레이어/로그 쓰기 실패
- 메일 서버의 큐(spool) 적재 실패
여기서 중요한 포인트는 에러 메시지가 “device에 공간이 없다”라고만 말할 뿐, 그 공간이 블록 공간인지 inode 공간인지 구분해주지 않는다는 점입니다.
inode란 무엇이고 왜 고갈되나?
inode는 유닉스/리눅스 파일시스템에서 파일(또는 디렉터리) 하나당 1개씩 소비되는 메타데이터 엔트리입니다. 파일의 실제 내용은 데이터 블록에 저장되지만, 파일의 소유자/권한/시간/블록 위치 등의 정보는 inode에 저장됩니다.
- 수많은 작은 파일 → 데이터 블록은 거의 안 쓰는데 inode만 빠르게 소비
- inode는 파일시스템 생성 시점에 “대략” 정해짐(ext4는 동적 할당 느낌이 있지만 결국 한계가 존재)
- inode가 100%면 더 이상 새 파일/디렉터리를 만들 수 없음 → 용량이 남아도 쓰기 실패
1단계: inode 고갈 여부 확인 (df -i)
가장 먼저 inode 사용량을 확인합니다.
# 블록(용량) 사용량
$ df -h
# inode 사용량
$ df -i
출력 예시는 대략 이런 형태입니다.
df -h는 40% 사용df -i는 문제 파티션이IUse% 100%
특히 /, /var, /tmp, Docker 데이터 디렉터리가 있는 마운트 지점에서 자주 발생합니다.
파일시스템 타입도 확인
파일시스템 타입에 따라 inode 특성이 다르므로 같이 확인합니다.
$ df -T
$ mount | column -t
2단계: 어떤 디렉터리가 inode를 먹는지 빠르게 찾기
inode 고갈은 “어떤 디렉터리에 파일이 너무 많다”로 귀결됩니다. 핵심은 파일 개수를 세는 것입니다.
(A) 최상위 디렉터리부터 큰 범위로 스캔
아래는 /var 아래에서 “파일 개수”가 많은 상위 디렉터리를 찾는 방법입니다.
# /var 바로 아래 디렉터리들의 파일 개수 대략 파악
sudo find /var -xdev -type f 2>/dev/null | awk -F/ '{print "/"$2"/"$3}' | sort | uniq -c | sort -nr | head
-xdev: 다른 파일시스템으로 넘어가지 않게(중요)awk로 2~3레벨까지만 묶어서 “어느 영역이 큰지” 빠르게 본 뒤, 그 디렉터리로 더 좁혀 들어갑니다.
(B) 디렉터리별 inode(파일 개수) Top N
좀 더 직관적으로 디렉터리별 파일 개수를 보고 싶다면:
# 현재 경로 하위 디렉터리별 파일 개수 (1레벨)
for d in */; do
echo -n "$d ";
find "$d" -type f 2>/dev/null | wc -l;
done | sort -k2 -nr | head
(C) /tmp, /var/tmp, 캐시 디렉터리 우선 확인
inode 고갈의 단골은 다음입니다.
/tmp,/var/tmp: 정리 안 된 임시파일/var/log: 과도한 로그 파일(특히 날짜별로 쪼개진 작은 로그)/var/lib/docker또는/var/lib/containerd: 이미지/레이어/컨테이너 로그/var/cache/*: 패키지 캐시, 앱 캐시- 애플리케이션이 만드는 세션 파일/업로드 임시 조각
3단계: 자주 터지는 원인별 해결책
3.1 로그가 너무 잘게 쌓이는 경우 (logrotate 점검)
작은 로그 파일이 수십만 개면 inode가 급속히 소모됩니다.
logrotate 상태 확인
sudo logrotate -d /etc/logrotate.conf | less
sudo cat /var/lib/logrotate/status | tail
오래된 압축 로그 정리(안전하게)
# 예: 14일 지난 gz 로그 삭제
sudo find /var/log -type f -name "*.gz" -mtime +14 -print -delete
# 예: 30일 지난 rotated 로그 삭제
sudo find /var/log -type f -regex ".*\.log\.[0-9]+$" -mtime +30 -print -delete
> 운영 환경에서는 반드시 -delete 전에 -print로 대상이 맞는지 확인하고, 서비스별 보관 정책/감사 요구사항을 먼저 점검하세요.
3.2 /tmp, /var/tmp에 임시파일이 방치된 경우
systemd 기반이라면 tmpfiles.d 정책으로 자동 정리가 가능하지만, 이미 쌓인 파일은 수동 정리가 필요합니다.
# 7일 지난 /tmp 파일 삭제 예시
sudo find /tmp -xdev -type f -mtime +7 -print -delete
# 디렉터리까지 정리하려면(주의):
sudo find /tmp -xdev -type d -empty -print -delete
systemd-tmpfiles로 정책 확인
systemd-tmpfiles --cat-config | less
# 또는
cat /usr/lib/tmpfiles.d/tmp.conf
cat /etc/tmpfiles.d/*.conf 2>/dev/null
3.3 Docker/Containerd의 컨테이너 로그/레이어가 inode를 먹는 경우
컨테이너 환경에서는 “용량”보다 “파일 개수”가 먼저 한계에 도달하는 케이스가 꽤 있습니다.
Docker 로그 파일 확인
sudo find /var/lib/docker/containers -name "*-json.log" -type f -printf '%s %p\n' 2>/dev/null | sort -nr | head
로그가 커서 용량도 먹지만, 컨테이너가 많으면 로그 파일 자체가 inode를 많이 씁니다.
로그 로테이션 설정(Docker)
/etc/docker/daemon.json 예시:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
적용:
sudo systemctl restart docker
> 쿠버네티스라면 노드 런타임(containerd)과 로그 경로가 다를 수 있습니다. 노드에서 /var/log/containers, /var/log/pods도 함께 확인하세요.
3.4 애플리케이션 캐시/세션 파일 폭증
예: PHP 세션 파일이 /var/lib/php/sessions에 수십만 개 쌓이는 케이스.
sudo find /var/lib/php/sessions -type f | wc -l
sudo find /var/lib/php/sessions -type f -mtime +7 -print -delete
이 경우 근본 원인은 다음 중 하나입니다.
- 세션 만료/GC 설정이 비정상
- 캐시를 파일로 두고 TTL/정리 로직이 없음
- 트래픽 급증으로 세션 파일이 폭증
파일 기반 캐시는 Redis/Memcached 같은 외부 캐시로 옮기는 것도 재발 방지에 효과적입니다.
4단계: 삭제가 어려울 때(열려 있는 삭제 파일, 숨은 inode 점유)
inode가 부족한데 “지웠는데도 회복이 없다”면, 프로세스가 삭제된 파일을 계속 열고 있는 상태일 수 있습니다. 이때는 디렉터리에서 파일이 사라져도 inode/공간이 바로 반환되지 않습니다.
lsof로 (deleted) 파일 찾기
sudo lsof | grep -E "\(deleted\)" | head -n 50
특정 프로세스가 큰/많은 삭제 파일을 잡고 있으면, 보통 해결은:
- 해당 프로세스 재시작(가장 흔함)
- 로그 파일이라면 logrotate 설정에서
copytruncate또는 서비스에SIGHUP로 재오픈 유도
프로세스 재시작이 어려운 경우, 열린 FD를 직접 truncate하는 방법도 있지만 운영 리스크가 커서 신중해야 합니다.
5단계: inode 고갈을 예방하는 운영 체크리스트
5.1 모니터링에 inode 지표 추가
대부분의 모니터링은 disk_used_percent만 보는데, inode는 별도입니다.
- Prometheus node_exporter:
node_filesystem_files_free,node_filesystem_files - 알람 기준 예시:
IUse% > 80%경고,> 90%치명
5.2 로그/임시파일/캐시의 “파일 개수”를 줄이는 설계
- 로그는 파일 수가 늘기 쉬움 → 로테이션 + 보관 기간 + 압축 정책
- 캐시는 파일 대신 KV 스토어(Redis 등) 고려
- 빌드/배치 워크 디렉터리는 작업 후 정리(성공/실패 모두)
5.3 파일시스템 설계(가능하면) 재검토
- inode가 자주 고갈되는 볼륨은 작은 파일이 많은 워크로드에 부적합할 수 있음
- ext4 생성 시 inode 비율 조정이 가능하지만(예:
mkfs.ext4 -i), 운영 중인 루트 파티션에 적용하려면 재구성이 필요 - 워크로드 특성에 맞게 별도 파티션/볼륨 분리(
/var,/var/lib/docker등)도 효과적
실전 트러블슈팅 시나리오 예시
상황: df -h는 여유, 배포 중 npm ci가 ENOSPC로 실패.
- inode 확인
df -i
- 문제 마운트 지점이
/라면/var부터 탐색
sudo find /var -xdev -type f 2>/dev/null | wc -l
- 상위 디렉터리별로 범인 찾기
sudo find /var -xdev -type f 2>/dev/null | awk -F/ '{print "/"$2"/"$3}' | sort | uniq -c | sort -nr | head
- 예를 들어
/var/log/app가 범인이면, 오래된 로그 삭제 + logrotate 보강
sudo find /var/log/app -type f -mtime +14 -print -delete
- 재발 방지: 로테이션/보관 정책 적용, inode 알람 추가
마무리
No space left on device는 반드시 “디스크 용량 부족”이 아닙니다. df -i로 inode를 먼저 확인하고, 파일 개수 폭증 지점을 find로 좁혀 들어가면 대부분 빠르게 해결됩니다. 정리 후에는 logrotate, tmpfiles, 컨테이너 로그 정책, 모니터링까지 묶어 재발을 막는 것이 핵심입니다.
운영 환경에서 비슷한 “자원 고갈” 문제는 디스크뿐 아니라 메모리/네트워크/DNS 등 다양한 축에서 연쇄적으로 터지기도 합니다. 예를 들어 OOM으로 서비스가 재시작 루프에 빠지면 로그가 폭증해 inode까지 고갈되는 식입니다. 이런 관점의 실전 진단은 Kubernetes OOMKilled 진단과 메모리 누수 추적 실전도 함께 참고해두면 좋습니다.