Published on

리눅스 df 100%인데 용량이 안 줄 때 핫픽스

Authors

서버에서 df -h가 100%를 찍으면 보통은 파일을 지우고 끝날 것 같지만, 실제로는 rm 이후에도 사용량이 그대로인 경우가 자주 발생합니다. 이때 무작정 더 지우다 보면 서비스 로그나 데이터까지 날릴 수 있어, 정확한 원인 분기가 중요합니다.

이 글은 “지웠는데도 용량이 안 줄어드는” 상황을 세 가지 축으로 정리합니다.

  • 삭제했지만 프로세스가 파일을 잡고 있어 공간이 반환되지 않는 케이스
  • 블록 용량은 남았는데 inode가 고갈된 케이스
  • dudf가 서로 다른 값을 보여주는(숨은 파일/마운트/오버레이) 케이스

또한 디스크가 꽉 차면 애플리케이션이 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바이트로 줄일 수 있습니다.

  1. lsof로 PID와 FD 확인
sudo lsof +L1 | grep deleted | head -n 5
  1. /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) dudf가 다르다: 숨은 파일/마운트/오버레이 함정

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 -hdf -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/tmptmpfiles.d 정책 적용
  • 디스크 알람: node_exporter 등으로 사용량과 inode를 모두 모니터링
  • 장애 시 “재시도 폭주” 제어: 타임아웃/백오프/서킷 브레이커로 로그 폭증을 막기

특히 재시도 폭주가 로그를 폭발시키는 패턴은 애플리케이션/인프라 장애에서 매우 흔합니다. 비슷한 맥락의 운영 트러블슈팅은 다음 글도 함께 보면 도움이 됩니다: Python httpx ReadTimeout·ConnectError 재시도 설계

8) 요약: 원인별로 “정확히” 다른 처방이 필요

  • rm 했는데 df가 그대로면, 대부분 삭제된 파일이 열린 상태입니다. lsof +L1로 확인하고 재시작 또는 /proc/PID/fd/FDtruncate가 핫픽스입니다.
  • df -i가 100%면 inode 고갈이므로 용량이 아니라 “파일 개수”를 줄여야 합니다.
  • dudf가 다르면 마운트/오버레이/바인드 또는 **숨은 소비(삭제-open, 메타데이터, 예약 블록)**를 의심하고, du -x와 도커 경로를 함께 점검하세요.

이 세 갈래만 정확히 분기해도 “df 100%인데 왜 안 줄지?”로 허비하는 시간을 크게 줄일 수 있습니다.