- Published on
리눅스 디스크 100%? inode 고갈 진단·복구 실전
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 df -h는 여유가 있는데도 배포/로그/임시파일 생성이 실패하며 No space left on device가 발생하는 경우가 있습니다. 이때 많은 팀이 “디스크 100%”로 오해하고 용량 증설부터 시도하지만, 실제 원인은 inode 고갈인 경우가 흔합니다. inode는 “파일 하나당 1개씩 소비되는 메타데이터 슬롯”이라서, 작은 파일이 수백만 개 쌓이면 데이터 용량과 무관하게 파일 생성이 막힙니다.
이 글은 운영 환경에서 바로 적용할 수 있도록 진단 → 원인 파일 찾기 → 안전한 정리 → 복구 확인 → 재발 방지 순서로 정리합니다.
1) 증상: 디스크는 남았는데 파일이 안 만들어진다
대표적인 증상은 아래와 같습니다.
touch: cannot touch 'x': No space left on device- 로그가 더 이상 기록되지 않음(애플리케이션/웹서버)
- 컨테이너/파드에서 임시 파일 생성 실패
df -h상 사용률이 낮거나 충분한데도 장애 발생
이때는 “용량”이 아니라 inode를 확인해야 합니다.
2) 10초 진단: df -i로 inode 사용률 확인
df -i는 파일시스템별 inode 사용량을 보여줍니다.
# inode 사용률 확인
sudo df -i
# 예시 출력(가상의 값)
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/nvme0n1p1 6553600 6553500 100 100% /
IUse%가 100%이면 inode 고갈입니다.- 특정 마운트(
/,/var,/tmp,/var/lib/docker등)만 100%일 수도 있습니다.
df -h와 df -i가 엇갈릴 때의 해석
df -h여유 +df -i100%: 작은 파일 폭증df -h100% +df -i여유: 큰 파일/로그/덤프- 둘 다 100%: 둘 다 문제(대개 로그 폭증 + 작은 파일 폭증이 같이 발생)
3) inode를 많이 먹는 범인 디렉터리 찾기
inode는 “파일 개수”에 비례합니다. 즉, 용량이 아니라 파일 수를 세야 합니다.
3-1) 빠른 스캔: 상위 디렉터리별 파일 개수
아래는 루트 기준 1레벨에서 파일 개수(대략)를 세는 방법입니다.
# 루트 1레벨 디렉터리별 파일 개수(권한 오류는 무시)
for d in /*; do
[ -d "$d" ] || continue
c=$(sudo find "$d" -xdev -type f 2>/dev/null | wc -l)
printf "%10s %s\n" "$c" "$d"
done | sort -nr | head
-xdev로 다른 파일시스템(예: NFS, 별도 마운트)로 넘어가지 않게 제한합니다.- 파일 수가 비정상적으로 큰 디렉터리가 상위에 뜹니다(대개
/var,/tmp,/home,/var/lib/...).
3-2) 더 정확히: 특정 디렉터리 내부 Top N 찾기
범인 후보가 /var라면 다음처럼 좁혀갑니다.
# /var 아래 2레벨 디렉터리별 파일 개수
sudo find /var -xdev -mindepth 2 -maxdepth 2 -type f -printf '%h\n' \
| sort | uniq -c | sort -nr | head -20
이 출력에서 상위에 뜨는 경로가 inode를 잡아먹는 핵심입니다.
3-3) 자주 나오는 범인 패턴
/var/log/*로그 로테이션 실패, 디버그 로그 폭증/tmp,/var/tmp임시 파일 누수(크론/배치/SDK)/var/lib/docker/overlay2컨테이너 레이어/로그/캐시/var/lib/containerd컨테이너 런타임 캐시- 애플리케이션 캐시 디렉터리(예: 이미지 썸네일, 세션 파일)
- 스풀/큐 디렉터리(메일 큐, 작업 큐)
컨테이너 환경에서 자주 겪는 다른 “고갈” 이슈로는 메모리 부족이 있습니다. 메모리 누수/리밋 문제는 Kubernetes OOMKilled 진단과 메모리 누수 추적 실전도 함께 참고하면 장애 분류가 빨라집니다.
4) 안전한 복구: “지우기 전에” 영향도부터 확인
inode 고갈은 급하지만, 무작정 rm -rf는 2차 장애를 만들 수 있습니다. 아래 순서로 안전하게 진행합니다.
4-1) 무엇을 지울지 판단하는 체크리스트
- 로그: 최근 N일만 남기고 정리 가능? (규정/감사/보관 정책 확인)
- 캐시: 삭제해도 재생성 가능한가?
- 임시파일: 프로세스가 사용 중인지 확인 필요
- 큐/스풀: 삭제하면 데이터 유실 가능(업무 영향 큼)
4-2) 열린(삭제된) 파일이 공간/inode를 계속 점유하는 경우
파일을 삭제했는데도 inode가 회복되지 않으면, 프로세스가 파일을 열린 채로 잡고 있을 수 있습니다.
# 삭제된 파일을 열고 있는 프로세스 찾기
sudo lsof +L1
+L1은 링크 수가 0(삭제됨)인 파일을 의미합니다.- 이런 경우 프로세스 재시작(또는 로그 핸들 reopen)이 필요합니다.
5) 실전 정리 레시피 (상황별)
5-1) /tmp, /var/tmp 임시파일 정리
임시 디렉터리는 오래된 파일부터 제거하는 방식이 안전합니다.
# /tmp에서 3일 이상 지난 파일 삭제(디렉터리 포함)
sudo find /tmp -xdev -mindepth 1 -mtime +3 -print -delete
# /var/tmp에서 7일 이상 지난 파일 삭제
sudo find /var/tmp -xdev -mindepth 1 -mtime +7 -print -delete
-print를 먼저 넣어 어떤 파일이 지워지는지 로그로 남기면 추적에 도움이 됩니다.
5-2) 로그 폭증: logrotate 점검 + 즉시 정리
logrotate 동작 확인
# 설정 문법 체크 및 디버그
sudo logrotate -d /etc/logrotate.conf
# 강제 실행(실제 수행)
sudo logrotate -f /etc/logrotate.conf
오래된 로그 파일 정리(예: 14일 초과)
sudo find /var/log -xdev -type f \( -name "*.log" -o -name "*.gz" -o -name "*.1" \) \
-mtime +14 -print -delete
주의:
- 애플리케이션이 단일 파일에 계속 쓰는 구조면, 삭제 대신 truncate가 더 안전할 때가 있습니다.
# 파일을 지우지 않고 내용만 비움(파일 핸들은 유지)
sudo truncate -s 0 /var/log/myapp/debug.log
5-3) Docker/Containerd: 이미지·컨테이너 찌꺼기 정리
컨테이너 호스트에서 inode 고갈이 자주 나옵니다.
Docker
# 전체 사용량 확인
sudo docker system df
# 사용하지 않는 리소스 정리(주의: 중지 컨테이너/미사용 이미지 삭제)
sudo docker system prune -af
# 볼륨까지 정리(데이터 볼륨이 날아갈 수 있으니 매우 주의)
# sudo docker system prune -af --volumes
containerd(환경에 따라)
배포판/클러스터 구성에 따라 crictl을 사용합니다.
# 미사용 이미지 정리(환경에 따라 동작 상이)
sudo crictl images
sudo crictl rmi --prune
EKS/쿠버네티스에서 노드 리소스 고갈은 다양한 형태로 표출됩니다. 네트워크/인증 지연이 섞이면 원인 추적이 더 어려워지므로, 예를 들어 NAT/DNS/PrivateLink 병목은 EKS Pod STS AssumeRole 타임아웃 - NAT·PrivateLink·DNS처럼 별도로 분리 진단하는 습관이 도움이 됩니다.
6) 복구 확인: inode가 실제로 돌아왔는지 검증
정리 후에는 반드시 inode 회복을 확인합니다.
sudo df -i
# 특정 마운트만 보고 싶다면
sudo df -i / /var /tmp
또한 애플리케이션이 정상적으로 파일을 생성하는지(로그/업로드/세션/빌드 산출물 등) 기능 확인을 합니다.
7) 재발 방지: inode 고갈은 “설계/운영” 문제다
inode 고갈은 단발성 청소로 끝나지 않습니다. 원인을 제거하지 않으면 반복됩니다.
7-1) 로그/임시파일 정책을 코드와 설정으로 고정
- logrotate 정책 명시(보관 기간, 압축, 크기 기준)
- 애플리케이션 로그 레벨/샘플링(디버그 폭주 방지)
/tmp사용 시 TTL/정리 작업(크론 또는 systemd-tmpfiles)
systemd-tmpfiles로 /tmp 자동 정리 예시
배포판에 따라 /usr/lib/tmpfiles.d/tmp.conf 또는 /etc/tmpfiles.d/*.conf로 관리합니다.
# 예: /etc/tmpfiles.d/mytmp.conf
# 10일 지난 /tmp/myapp 아래 파일/디렉터리 정리
# d: 디렉터리 생성(권한/소유), D: 디렉터리 및 내용 정리 정책
D /tmp/myapp 0755 myuser mygroup 10d
적용:
sudo systemd-tmpfiles --clean
7-2) 모니터링: 디스크(용량)와 inode를 분리해서 알림
많은 모니터링은 df -h 기반이라 inode 고갈을 놓칩니다.
- 노드/서버별
inode 사용률(IUse%)메트릭 수집 - 70%/85%/95% 단계별 알림
/var,/tmp, 컨테이너 런타임 경로는 별도 임계치
간단한 크론 체크 스크립트 예시:
#!/usr/bin/env bash
set -euo pipefail
THRESHOLD=90
while read -r fs inodes iused ifree iuse mnt; do
[[ "$fs" == "Filesystem" ]] && continue
pct=${iuse%%%}
if (( pct >= THRESHOLD )); then
echo "[ALERT] inode usage ${pct}% on ${mnt} (${fs})"
fi
done < <(df -iP)
7-3) 파일시스템 설계: inode가 넉넉한 포맷/마운트 고려
- ext4는 생성 시 inode 수가 결정됩니다(기본 비율). “작은 파일이 매우 많은 워크로드”라면 포맷 옵션을 검토해야 합니다.
- XFS는 inode가 동적으로 관리되어(ext4 대비) inode 고갈 이슈가 덜한 편이지만, 운영 표준/복구 절차와 함께 판단해야 합니다.
- 컨테이너 워크로드는
/var/lib/docker같은 경로를 별도 디스크로 분리하면 장애 격리가 쉬워집니다.
8) 장애 대응 체크리스트(요약)
-
df -h와df -i를 함께 확인했는가? - inode 100%인 마운트 지점이 어디인지 특정했는가?
-
find ... | wc -l로 파일 수가 비정상적인 디렉터리를 찾았는가? -
lsof +L1로 삭제된 파일을 잡고 있는 프로세스가 있는지 확인했는가? - 로그/임시/캐시/컨테이너 찌꺼기 중 무엇이 원인인지 분류했는가?
- 정리 후
df -i로 회복을 검증했는가? - logrotate/systemd-tmpfiles/모니터링으로 재발 방지를 걸었는가?
마무리
inode 고갈은 “디스크 100%”처럼 보이지만, 실제로는 파일 개수 한도에 부딪힌 상태입니다. df -i로 빠르게 판별하고, 파일 수 폭증 지점을 찾아 안전하게 정리한 뒤, 로그·임시파일 정책과 모니터링을 붙이면 재발을 크게 줄일 수 있습니다. 특히 컨테이너/마이크로서비스 환경에서는 캐시·레이어·로그가 분산되어 쌓이기 쉬우니, inode 알림을 기본으로 넣어두는 것이 운영 비용을 줄이는 가장 확실한 방법입니다.