- Published on
No space left on device인데 용량 남을 때 - inode 0% 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 No space left on device가 떴는데 df -h로 보면 디스크 용량이 넉넉한 경우가 있습니다. 이때 대부분의 범인은 inode 고갈입니다. 파일 시스템은 “바이트 용량”뿐 아니라 “파일 개수(=inode)”도 한정되어 있고, 작은 파일이 폭증하면 바이트는 남아도 새 파일을 만들 수 없어 동일한 에러가 발생합니다.
이 글은 다음을 목표로 합니다.
용량은 남는데왜 에러가 나는지 원리 이해inode 0%(또는IUse 100%) 상황을 빠르게 진단- 로그/캐시/임시파일/컨테이너 레이어 등 흔한 원인별 정리
- 즉시 복구(응급조치) + 재발 방지(운영 설계)
> 운영 환경에서 이런 문제는 대개 “파일이 너무 많아져서” 발생합니다. EKS/컨테이너 환경에서도 노드의 /var/lib/containerd, /var/log, emptyDir 등에 작은 파일이 쌓이며 동일하게 터질 수 있습니다. 관련 트러블슈팅 관점은 EKS에서 503 Service Unavailable 원인 10분 진단처럼 “증상→원인 후보→검증” 흐름을 적용하면 빠르게 좁힐 수 있습니다.
1) 핵심 원리: 디스크 용량 vs inode
- 디스크 용량(Blocks): 파일 내용이 차지하는 바이트
- inode: 파일 메타데이터(권한/소유자/타임스탬프/블록 포인터 등)
파일 하나당 inode가 1개 필요합니다. 예를 들어 1KB짜리 파일 1,000만 개는 용량은 10GB 수준일 수 있지만 inode는 1,000만 개를 소모합니다. inode가 바닥나면 커널은 새 파일 생성/링크/임시파일 생성 등을 실패시키며 ENOSPC(No space left on device)를 반환합니다.
inode 고갈이 특히 잘 나는 패턴
- 애플리케이션이 요청당 임시파일을 생성하고 삭제를 누락
- 로그가 파일 단위로 쪼개져 쌓임(예: request-id별 파일)
- 스풀 디렉터리(
/var/spool/*), 큐, 세션 파일이 무한 증가 - 컨테이너 런타임 저장소(
/var/lib/docker,/var/lib/containerd)에 레이어/스냅샷/로그가 누적 - CI/CD 워크스페이스에 작은 아티팩트가 폭증
2) 1분 진단: 진짜 inode가 문제인지 확인
2.1 df -h와 df -i를 함께 본다
# 용량(바이트) 기준
sudo df -h
# inode(파일 개수) 기준
sudo df -i
df -i 출력에서 특정 마운트의 IUse%가 100%라면 inode 고갈입니다.
2.2 에러가 발생한 경로가 어느 파일시스템인지 확인
# 문제가 난 디렉터리가 어느 마운트에 속하는지
df -h /문제/경로
df -i /문제/경로
실무에서 흔한 함정은 /tmp가 별도 파티션이거나, 컨테이너 환경에서 /var/lib/...가 별도 디스크(EBS)로 마운트되어 있다는 점입니다. “전체 디스크는 여유”인데 “특정 마운트만 inode 100%”일 수 있습니다.
2.3 inode를 잡아먹는 디렉터리 찾기(파일 개수 기준)
용량이 아니라 파일 개수가 핵심이므로 du보다 “파일 수 카운트”가 유효합니다.
# 최상위에서 디렉터리별 파일 개수(대략적)
# -x: 다른 파일시스템으로 넘어가지 않게(중요)
# 주의: 파일이 매우 많으면 시간이 걸릴 수 있음
sudo bash -c 'for d in /*; do echo -n "$d "; find "$d" -xdev -type f 2>/dev/null | wc -l; done' | sort -k2 -n
특정 경로가 의심되면 더 좁혀봅니다.
sudo find /var/log -xdev -type f | wc -l
sudo find /var/lib -xdev -type f | wc -l
sudo find /tmp -xdev -type f | wc -l
3) 응급 복구: “inode를 즉시 확보”하는 방법
3.1 가장 효과적인 1차 조치: 불필요한 작은 파일 삭제
대표적으로 다음을 먼저 의심합니다.
- 오래된 로그(압축/로테이션 미적용)
- 임시 디렉터리(
/tmp,/var/tmp)에 쌓인 찌꺼기 - 애플리케이션 캐시/세션
예시(로그 7일 초과 삭제):
# /var/log 하위에서 7일 초과 파일 삭제
sudo find /var/log -xdev -type f -mtime +7 -print -delete
예시(tmp 3일 초과 삭제):
sudo find /tmp -xdev -type f -mtime +3 -print -delete
sudo find /var/tmp -xdev -type f -mtime +3 -print -delete
> 주의: 서비스가 사용하는 경로를 무턱대고 지우면 장애가 커집니다. 먼저 -print로 목록을 확인하고, 애플리케이션/런타임 특성을 파악한 뒤 삭제하세요.
3.2 “삭제했는데도” inode가 안 돌아온다? 열린 파일 핸들 문제
리눅스는 프로세스가 파일을 열고 있는 상태라면, 파일을 rm으로 지워도 디스크(또는 inode/블록)가 즉시 반환되지 않을 수 있습니다(정확히는 디렉터리 엔트리는 제거되지만, 참조가 남아 공간이 해제되지 않음).
다음으로 “삭제됐지만 열린 상태인 파일”을 찾습니다.
# (deleted)로 표시되는 열린 파일 확인
sudo lsof +L1
# 특정 마운트에서만 보고 싶다면
sudo lsof +L1 /var
해결은 보통 해당 프로세스를 재시작하거나, 로그 파일을 사용하는 데몬의 경우 **logrotate 신호(예: nginx -s reopen)**를 보내는 방식입니다.
3.3 컨테이너 노드에서 자주 터지는 위치
- Docker:
/var/lib/docker - containerd:
/var/lib/containerd - kubelet:
/var/lib/kubelet - 로그:
/var/log/containers,/var/log/pods
컨테이너 런타임 정리 예시:
# Docker 사용 시: 안 쓰는 이미지/컨테이너/네트워크 정리
sudo docker system prune -af
# containerd(crictl) 사용 시: 미사용 이미지 정리(환경에 따라 다름)
sudo crictl images
sudo crictl rmi --prune
노드가 EKS라면, 장애가 난 노드만 드레이닝 후 재부팅/교체하는 운영도 흔합니다. 이런 유형의 “노드 리소스 고갈”은 네트워크 자원 고갈(IP)과도 결이 비슷한데, 진단 접근은 EKS VPC CNI IP 누수로 Pod IP 고갈 해결하기처럼 “고갈 지표 확인→고갈 원인 추적→재발 방지”로 가져가면 좋습니다.
4) 근본 원인 찾기: 어떤 파일이 inode를 잡아먹었나
4.1 “파일이 많은 디렉터리” Top N 찾기
깊이 2~3 정도로만 빠르게 훑는 방식이 현실적입니다.
# /var 아래에서 depth 3까지 디렉터리별 파일 개수 집계(시간 주의)
sudo find /var -xdev -type f -printf '%h\n' 2>/dev/null \
| awk -F/ 'NF{print "/"$2"/"$3"/"$4}' \
| sort | uniq -c | sort -nr | head -50
출력 상위에 뜨는 디렉터리를 기준으로 애플리케이션/에이전트(로그 수집기, 모니터링, 배치 작업) 설정을 확인합니다.
4.2 자주 보는 원인별 체크리스트
- logrotate 미설정/실패:
/etc/logrotate.d/*,journalctl --disk-usage확인 - 애플리케이션 임시파일: 업로드/압축/리포트 생성 작업의 실패 시 cleanup 누락
- 메일 스풀:
/var/spool/postfix,/var/spool/mail - 패키지/캐시:
/var/cache/*(단, inode 폭증은 보통 캐시의 “파일 수”가 많을 때) - 코어덤프:
/var/lib/systemd/coredump(용량도 같이 먹는 경우가 많음)
5) 재발 방지: 운영 설계(가장 중요)
5.1 로그 정책: 파일 개수 폭증을 막는다
- “요청당 파일” 같은 설계를 피하고, 가능하면 단일 파일 + 로테이션
- 로테이션은 “용량”뿐 아니라 “파일 개수” 관점에서
rotate값을 관리
logrotate 예시:
/var/log/myapp/*.log {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}
5.2 systemd-journald 사용 시 상한 설정
# 현재 사용량
journalctl --disk-usage
# 설정 파일 편집
sudo sed -i 's/^#\?SystemMaxUse=.*/SystemMaxUse=1G/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
5.3 파일 시스템 선택/생성 시 inode 고려(ext4)
ext4는 파일시스템 생성 시 inode가 결정됩니다(일반적으로 충분하지만, 작은 파일이 비정상적으로 많은 워크로드면 부족할 수 있음).
- “작은 파일 수천만 개”가 예상되면 별도 파티션을 두고 inode 밀도를 높여 생성 고려
- 이미 만든 ext4의 inode 수는 온라인에서 쉽게 늘릴 수 없습니다(재생성/마이그레이션이 현실적)
새 파티션 생성 시(예시):
# inode 비율(-i) 조정: 바이트당 inode 하나를 얼마나 촘촘히 만들지
# 값이 작을수록 inode가 더 많이 생성됨(메타데이터 오버헤드 증가)
sudo mkfs.ext4 -i 16384 /dev/nvme1n1
5.4 모니터링: inode도 알람에 넣어야 한다
용량 알람만 걸어두면 inode 고갈은 예고 없이 터집니다.
- 노드/서버별
df -i의IUse%를 수집 - 70/85/95% 3단계 알람 추천
간단한 크론 기반 체크 예시:
#!/usr/bin/env bash
set -euo pipefail
THRESHOLD=85
while read -r fs inodes iused ifree iuse mount; do
# 헤더 스킵
[[ "$fs" == "Filesystem" ]] && continue
pct=${iuse%%%}
if (( pct >= THRESHOLD )); then
echo "[ALERT] inode usage ${pct}% on ${mount} (${fs})"
fi
done < <(df -iP)
6) 자주 묻는 질문(실무 포인트)
Q1. df -h는 여유인데 왜 ENOSPC가 동일하게 뜨나요?
애플리케이션 입장에선 “새 파일 생성 실패”가 곧 ENOSPC입니다. 커널이 실패 이유를 inode 고갈/블록 고갈로 구분해주긴 하지만, 많은 라이브러리/툴은 이를 뭉뚱그려 No space left on device로 표시합니다.
Q2. inode 100%면 바로 서비스가 죽나요?
새 파일 생성이 필요한 순간부터 문제가 터집니다.
- 로그 기록 실패 → 프로세스가 종료/크래시
- 소켓/임시파일 생성 실패
- DB/캐시가 임시 파일을 못 만들어 장애
장애 양상은 다양하지만 “갑자기 여러 컴포넌트가 연쇄적으로 이상”해지는 패턴이 흔합니다.
Q3. 삭제했는데도 회복이 안 되면?
- 다른 마운트가 문제인지 재확인(
df -i /경로) (deleted)열린 파일 확인(lsof +L1) 후 프로세스 재시작
이 두 가지가 대부분입니다.
마무리: 체크 순서(요약)
df -h와 함께 반드시df -i확인- 문제가 난 경로의 마운트 지점을 특정(
df -i /path) - 파일 개수 폭증 디렉터리를 찾고(
find ... | wc -l), 안전하게 정리 - 삭제 후에도 회복이 없으면
lsof +L1로 열린 삭제 파일 점검 - logrotate/journald 상한 + inode 모니터링으로 재발 방지
inode 고갈은 “용량 관리”만 해서는 막기 어렵고, 워크로드 특성(작은 파일 폭증)을 이해해 설계/정책/모니터링을 함께 가져가야 재발을 끊을 수 있습니다.