- Published on
리눅스 df 100%인데 용량이 안 줄 때 핫픽스
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 df -h가 100%를 찍으면 보통은 파일을 지우고 끝날 것 같지만, 실제로는 rm 이후에도 사용량이 그대로인 경우가 자주 발생합니다. 이때 무작정 더 지우다 보면 서비스 로그나 데이터까지 날릴 수 있어, 정확한 원인 분기가 중요합니다.
이 글은 “지웠는데도 용량이 안 줄어드는” 상황을 세 가지 축으로 정리합니다.
- 삭제했지만 프로세스가 파일을 잡고 있어 공간이 반환되지 않는 케이스
- 블록 용량은 남았는데 inode가 고갈된 케이스
du와df가 서로 다른 값을 보여주는(숨은 파일/마운트/오버레이) 케이스
또한 디스크가 꽉 차면 애플리케이션이 CrashLoopBackOff로 튀거나(로그 쓰기 실패) 컨테이너가 기동 자체를 못 하는 상황으로 이어지기도 합니다. 운영 환경이라면 이런 장애 패턴도 함께 참고해 두면 좋습니다: Cloud Run 503·컨테이너 미기동 원인 7가지, K8s Pod CrashLoopBackOff 원인 7가지와 해결
1) 30초 진단 체크리스트 (가장 흔한 순)
아래 순서대로 보면 대부분 1~2분 내에 원인이 갈립니다.
1-1. df는 100%인데 du 합계는 작다
- 삭제된 파일이 열린 상태이거나
- 다른 마운트/오버레이/바인드 마운트 때문에
du가 못 보는 영역이 존재할 확률이 큽니다.
1-2. df -i가 100%
- inode 고갈입니다. 작은 파일이 너무 많거나, 캐시/스풀 디렉터리에 파일이 폭증한 상황입니다.
1-3. root reserved block(예약 블록) 때문에 100%처럼 보인다
- ext 계열에서 root용 예약 공간이 커서 일반 유저 관점에서 “꽉 참”처럼 보일 수 있습니다.
2) 삭제했는데도 용량이 안 줄 때: 열린 파일(Deleted but open)
리눅스는 파일을 삭제해도 해당 파일을 열고 있는 프로세스가 있으면 디스크 블록을 즉시 회수하지 않습니다. rm은 디렉터리 엔트리만 지우고, 마지막 FD가 닫힐 때 실제 공간이 반환됩니다.
2-1. 증상
rm -rf로 로그를 지웠는데df -h가 그대로- 재부팅하면 갑자기 용량이 생김
- 특정 프로세스(nginx, java, python, postgres 등)가 큰 파일을 계속 쓰고 있었음
2-2. 확인 명령
가장 확실한 방법은 lsof로 (deleted)를 찾는 것입니다.
sudo lsof +L1
+L1은 link count가 1보다 작은(즉, 디렉터리 엔트리가 사라진) 열린 파일을 보여줍니다.- 출력에서
SIZE/OFF가 큰 항목이 범인인 경우가 많습니다.
특정 마운트/경로 기준으로 좁히려면:
sudo lsof +L1 /var
lsof가 없거나 더 간단히 보고 싶다면 /proc로도 확인 가능합니다.
sudo find /proc/*/fd -lname '* (deleted)' 2>/dev/null | head
2-3. 즉시 해결(핫픽스)
방법 A: 프로세스 재시작(가장 안전)
서비스가 파일을 계속 잡고 있다면 해당 프로세스를 재시작하는 게 정석입니다.
sudo systemctl restart nginx
# 또는
sudo systemctl restart your-service
컨테이너 환경이면 해당 파드/컨테이너 롤링 재시작이 보통 더 안전합니다.
방법 B: 열린 FD를 truncate해서 즉시 회수(재시작이 어려울 때)
프로세스를 죽이기 어렵고, “삭제된 파일”의 FD만 비우고 싶다면 해당 FD를 0바이트로 줄일 수 있습니다.
lsof로 PID와 FD 확인
sudo lsof +L1 | grep deleted | head -n 5
/proc/PID/fd/FD에 대해 truncate
# 예: PID=1234, FD=7
sudo truncate -s 0 /proc/1234/fd/7
- 이 방식은 파일 내용을 날리므로, 대상이 로그 파일 같은 “버려도 되는 데이터”인지 반드시 확인하세요.
방법 C: 로그 파일은 logrotate/재오픈 신호 사용
nginx 같은 경우 USR1로 로그를 재오픈하도록 유도할 수 있습니다.
sudo kill -USR1 $(cat /run/nginx.pid)
애플리케이션별로 로그 재오픈 시그널이 다르니, 운영 표준에 맞춰 적용합니다.
3) df -i 100%: inode 고갈로 파일을 못 만든다
디스크 블록은 남아도 inode가 없으면 파일 생성이 실패합니다. 이때 df -h는 여유가 있어 보이는데도 touch가 실패하거나, 애플리케이션이 “No space left on device”를 냅니다.
3-1. 확인
df -i
IUse%가 100%면 inode 고갈입니다.
어디에서 inode를 많이 쓰는지 찾으려면 “파일 개수” 기준으로 봐야 합니다.
# 디렉터리별 파일 개수 대략 파악
sudo find /var -xdev -type f | wc -l
# 상위 디렉터리별로 파일 수 랭킹
for d in /var/*; do echo $(sudo find "$d" -xdev -type f 2>/dev/null | wc -l) "$d"; done | sort -nr | head
특히 아래 경로가 자주 범인입니다.
/var/log(로그 폭증)/var/lib/docker(컨테이너 레이어/로그)/var/tmp,/tmp(임시 파일 누수)/var/spool(큐/스풀 적체)
3-2. 해결
- 작은 파일이 쌓이는 디렉터리를 정리(캐시/임시/오래된 로그)
- 애플리케이션이 파일을 무한 생성하는 버그/설정 수정
- 근본적으로는 inode가 더 많은 파일시스템 설계(재포맷 시 옵션) 또는 파티션 분리
로그가 원인일 때는 “용량”보다 “파일 수”를 제한하는 정책이 필요합니다.
- logrotate에서
rotate,maxage,size를 함께 설정 - 애플리케이션 로그를 stdout로 보내고 수집기에서 보관(컨테이너 환경 권장)
4) du와 df가 다르다: 숨은 파일/마운트/오버레이 함정
4-1. du는 파일 합계, df는 파일시스템 사용량
du는 디렉터리 트리를 따라가며 보이는 파일만 합산합니다.df는 파일시스템 메타데이터/예약 블록/삭제되었지만 열린 파일 등까지 포함한 “실제 사용량”을 보여줍니다.
따라서 du가 작아도 df가 큰 상황이 가능합니다.
4-2. 다른 파일시스템이 마운트된 경로를 du가 건너뛰는 문제
원인 디렉터리 탐색 시에는 -xdev 옵션을 적절히 써야 합니다.
- 특정 파일시스템만 보려면
du -x
sudo du -xhd1 / | sort -h
- 반대로, 마운트 지점까지 포함해 “전체”를 보고 싶다면
-x를 빼야 합니다.
4-3. 바인드 마운트/컨테이너 오버레이
도커/컨테이너 환경에서는 실제 데이터가 /var/lib/docker 아래 overlay2로 쌓이는데, 앱 컨테이너 내부에서 du를 보면 호스트 사용량과 다르게 보일 수 있습니다.
호스트에서 아래를 우선 확인하세요.
sudo du -hd1 /var/lib/docker 2>/dev/null | sort -h
컨테이너 로그(json-file 드라이버)가 비대해지는 케이스도 흔합니다.
sudo find /var/lib/docker/containers -name '*-json.log' -type f -printf '%s %p\n' 2>/dev/null | sort -n | tail
필요 시(로그만 비우는 핫픽스):
# 특정 컨테이너 로그 파일 truncate
sudo truncate -s 0 /var/lib/docker/containers/CONTAINER_ID/CONTAINER_ID-json.log
근본 해결은 로그 드라이버/로테이션 옵션을 설정하는 것입니다.
5) ext 예약 블록 때문에 “100%처럼 보이는” 경우
ext2/3/4는 기본적으로 root를 위해 일부 블록을 예약합니다. 일반 사용자/일반 프로세스는 그 공간을 못 써서 df가 꽉 찬 것처럼 보일 수 있습니다.
5-1. 확인
sudo tune2fs -l /dev/sdXN | grep -E 'Reserved block count|Block count|Reserved block percentage'
5-2. 조정(주의해서)
데이터 파티션(예: /data)이라면 예약 비율을 낮춰 응급 공간을 확보할 수 있습니다.
# 예약 블록 비율을 1%로
sudo tune2fs -m 1 /dev/sdXN
- 루트 파일시스템(
/)에 대해서는 운영 정책에 따라 신중히 적용하세요.
6) 실전 복구 절차: “지금 당장 서비스 살리기” 루틴
디스크 100%는 로그 기록 실패, 소켓 생성 실패, DB 임시파일 생성 실패로 빠르게 장애로 번집니다. 아래 순서로 진행하면 안전합니다.
6-1. 쓰기 폭주를 먼저 멈춘다
- 과도한 로그를 뿜는 프로세스/배치 중지
- 장애 상황에서 무한 재시도 루프가 로그를 폭증시키는 경우가 많습니다
6-2. df -h와 df -i를 함께 본다
df -h
df -i
- inode 100%면 “작은 파일 정리”로 방향을 전환
6-3. 열린 삭제 파일 확인 후 재시작 또는 truncate
sudo lsof +L1 | sort -k7 -h | tail
- 큰
(deleted)가 보이면 해당 프로세스 재시작이 1순위
6-4. du로 상위 디렉터리부터 범인 찾기
sudo du -xhd1 / | sort -h
sudo du -xhd1 /var | sort -h
6-5. 컨테이너라면 /var/lib/docker 및 로그 파일 체크
sudo du -hd1 /var/lib/docker | sort -h
7) 재발 방지 체크(운영에서 효과 큰 것들)
- 로그 로테이션: 파일 크기/보관 기간/압축/삭제 정책을 명시
- 컨테이너 로그 제한: json 로그 로테이션 또는 외부 로깅으로 전환
- 임시 디렉터리 정리:
/tmp,/var/tmp에tmpfiles.d정책 적용 - 디스크 알람:
node_exporter등으로 사용량과 inode를 모두 모니터링 - 장애 시 “재시도 폭주” 제어: 타임아웃/백오프/서킷 브레이커로 로그 폭증을 막기
특히 재시도 폭주가 로그를 폭발시키는 패턴은 애플리케이션/인프라 장애에서 매우 흔합니다. 비슷한 맥락의 운영 트러블슈팅은 다음 글도 함께 보면 도움이 됩니다: Python httpx ReadTimeout·ConnectError 재시도 설계
8) 요약: 원인별로 “정확히” 다른 처방이 필요
rm했는데df가 그대로면, 대부분 삭제된 파일이 열린 상태입니다.lsof +L1로 확인하고 재시작 또는/proc/PID/fd/FD에truncate가 핫픽스입니다.df -i가 100%면 inode 고갈이므로 용량이 아니라 “파일 개수”를 줄여야 합니다.du와df가 다르면 마운트/오버레이/바인드 또는 **숨은 소비(삭제-open, 메타데이터, 예약 블록)**를 의심하고,du -x와 도커 경로를 함께 점검하세요.
이 세 갈래만 정확히 분기해도 “df 100%인데 왜 안 줄지?”로 허비하는 시간을 크게 줄일 수 있습니다.