- Published on
리눅스 디스크 100%인데 용량이 안 보일 때 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버 운영 중 가장 당황스러운 순간 중 하나가 df -h는 디스크 100%를 가리키는데, 정작 du -sh /*로 훑어보면 “그만한 용량이 어디에도 없다”는 상황입니다. 이 현상은 리눅스의 파일 삭제/링크(inode) 동작, 파일시스템 예약 블록, 컨테이너 오버레이, 마운트 포인트 착시 등이 겹치며 자주 발생합니다.
이 글에서는 (1) df vs du의 관점 차이를 먼저 정리하고, 이어서 **lsof로 ‘삭제됐지만 열려있는 파일’**을 찾는 방법, inode 고갈 진단, 마운트/오버레이 착시까지 포함해 실전 트러블슈팅 루틴을 제공합니다.
> 운영 환경에서 디스크 이슈는 장애 전파가 빠릅니다. 관련해서 “타임아웃/데드라인 전파”처럼 연쇄 장애를 줄이는 설계 관점은 gRPC 타임아웃 지옥 탈출 - 데드라인 전파 설계도 참고할 만합니다.
1) 먼저 확인: df와 du가 왜 다르게 보이나
df: 파일시스템 전체 관점(블록 단위)에서 사용량을 봅니다. 삭제된 파일이라도 어떤 프로세스가 파일 디스크립터로 잡고 있으면 블록은 계속 점유합니다.du: 디렉터리 트리 기준으로 파일을 따라가며 합산합니다. 이미 unlink(삭제)되어 디렉터리 엔트리에서 사라진 파일은 du로는 보이지 않습니다.
즉, df=100%인데 du가 작게 나오면 1순위 의심은:
- 삭제된 파일을 프로세스가 계속 열고 있음 (가장 흔함)
- inode 고갈 (파일 수가 너무 많음)
- 다른 마운트 포인트/오버레이로 가려짐 (컨테이너/바인드 마운트)
- reserved blocks(예약 블록), 스냅샷, CoW 등 파일시스템 특성
이제부터는 “가장 빨리 원인을 좁히는 순서”로 진행합니다.
2) 5분 진단 루틴(명령어 체크리스트)
아래 순서대로 실행하면 대부분의 케이스를 빠르게 잡습니다.
2.1 어떤 파티션이 100%인지 확정
df -hT
# inode도 함께
df -ih
Use% 100%인 대상이/,/var,/home, 특정 데이터 볼륨인지 확인df -ih에서IUse% 100%면 용량이 아니라 inode 문제일 가능성이 큽니다(뒤에서 다룸).
2.2 du는 “같은 파일시스템만” 보도록 제한
du가 다른 마운트까지 따라가면 합산이 왜곡됩니다. 대상 파일시스템 내부만 보려면 -x를 사용하세요.
# 루트 파티션 기준(다른 마운트는 제외)
sudo du -xhd1 / 2>/dev/null | sort -h
# /var가 별도 파티션이면 /var에서 수행
sudo du -xhd1 /var 2>/dev/null | sort -h
여기서도 큰 범인이 안 보이면 다음 단계로.
3) 가장 흔한 원인: 삭제됐지만 열려있는 파일(lsof)
리눅스에서 파일을 rm하면 디렉터리 엔트리만 제거(unlink)됩니다. 하지만 어떤 프로세스가 그 파일을 열고 있으면 실제 데이터 블록은 마지막 핸들이 닫힐 때까지 유지됩니다.
이 경우 du는 파일을 못 찾지만 df는 블록 사용량을 계속 포함합니다.
3.1 lsof로 (deleted) 파일 찾기
sudo lsof +L1
# 또는
sudo lsof | grep -E "\(deleted\)"
+L1은 링크 수(link count)가 1 미만인(즉, 디렉터리에서 사라진) 열린 파일을 보여줍니다.- 출력에서
SIZE/OFF가 큰 항목이 범인일 확률이 높습니다.
예시로 이런 형태를 보게 됩니다:
- 프로세스:
java,python,nginx,dockerd,containerd,rsyslogd - 파일:
/var/log/app.log (deleted)
3.2 해결: 프로세스를 재시작하거나 FD를 닫기
가장 안전한 해결은 해당 프로세스 재시작입니다.
# systemd 서비스라면
sudo systemctl restart <service>
# 프로세스만 확인
ps -fp <PID>
서비스 재시작이 어려운 경우(예: 대형 JVM), 임시로 파일 디스크립터를 직접 비우는 방식도 있지만 운영 리스크가 큽니다. 그래도 불가피하다면(원리 이해 후) 다음처럼 해당 FD에 대해 truncate를 수행할 수 있습니다.
# PID가 1234이고, 열린 파일 디스크립터가 5라면
sudo sh -c ': > /proc/1234/fd/5'
- 이 방법은 “열려있는 파일”의 내용을 0으로 만들어 블록을 반환시킬 수 있습니다.
- 단, 애플리케이션이 로그/데이터 파일로 사용 중이면 예상치 못한 동작(로그 손실, 데이터 손상)을 유발할 수 있으니 최후의 수단으로만 고려하세요.
3.3 로그 로테이션 설정 점검(logrotate)
이 문제가 반복된다면 대개 로그 로테이션이 잘못되어 있습니다.
- 애플리케이션이
copytruncate가 필요한 방식인데 rename만 하고 프로세스가 파일 핸들을 계속 잡는 경우 - 로테이트 후
postrotate에서kill -HUP/reload를 안 해서 파일 핸들이 갱신되지 않는 경우
/etc/logrotate.d/*를 점검하고, 서비스별 권장 로테이션 방식을 따르세요.
4) inode 고갈: “용량은 남았는데 디스크가 가득 찼다”
df -h는 여유가 있어 보이는데도 파일 생성이 실패하거나(“No space left on device”), 반대로 df가 100%로 보이는데 du가 애매한 경우도 있습니다. 이때 df -ih로 inode를 봐야 합니다.
4.1 inode 사용량 확인
df -ih
IUse%가 100%면 파일을 더 만들 수 없습니다. 작은 파일이 수백만 개 쌓이면 이런 일이 생깁니다.
4.2 inode를 많이 쓰는 디렉터리 찾기
용량이 아니라 “파일 개수”를 세어야 합니다.
# 상위 1레벨 디렉터리별 파일 수(대략)
for d in /var/*; do
echo -n "$d: "
sudo find "$d" -xdev -type f 2>/dev/null | wc -l
done | sort -n -t: -k2
또는 특정 경로에서 상위 디렉터리별 inode 소비를 빠르게 보고 싶다면:
sudo find /var -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -n | tail
4.3 흔한 원인과 조치
/tmp,/var/tmp에 임시 파일 폭증/var/lib/docker또는 컨테이너 런타임 레이어/캐시(작은 파일 다량)- 앱의 캐시/세션 디렉터리(파일 기반)
- CI 작업 디렉터리(빌드 산출물 누적)
조치는 “삭제”가 기본이지만, inode가 자주 고갈되면:
- 캐시를 파일이 아닌 DB/오브젝트 스토리지로 전환
- 주기적 정리(cron/systemd timer)
- 파일시스템 재구성 시 inode 밀도 고려(ext4 mkfs 옵션)
5) 마운트/오버레이 착시: du가 못 보는 공간
5.1 다른 파일시스템이 덮어쓴 경우
예를 들어 /var/log 아래에 별도 마운트가 올라가면, 원래 /var/log에 있던 데이터가 “가려져” 보이지 않을 수 있습니다.
mount | column -t
findmnt -T /var/log
- 마운트 전 원래 경로에 쌓인 데이터는 마운트를 내리기 전까지 접근이 어렵습니다.
du를/에서 돌릴 때-x를 안 주면 다른 파일시스템까지 합산되어 혼란이 커집니다.
5.2 컨테이너 환경(overlay2)에서의 함정
도커/컨테이너d는 보통 /var/lib/docker 또는 /var/lib/containerd 아래에 overlay 스토리지를 둡니다. 이미지 레이어, 로그, 빌드 캐시가 쌓이면 df가 먼저 터집니다.
# Docker 사용 시
sudo docker system df
sudo docker system prune -af --volumes
# containerd(환경별 상이)
sudo du -sh /var/lib/containerd 2>/dev/null
운영 클러스터에서 무작정 prune는 위험합니다. 노드 역할/워크로드 특성에 맞춰 정리 정책을 설계해야 합니다. (쿠버네티스 관련 장애 진단 글로는 AKS ImagePullBackOff - ACR 권한·토큰 만료 진단처럼 “원인-증상 분리” 접근이 도움이 됩니다.)
6) 파일시스템 예약 블록(reserved blocks)로 100%처럼 보이는 경우
ext 계열 파일시스템은 기본적으로 root용으로 일부 블록을 예약합니다(보통 5%). 작은 볼륨에서는 이게 체감상 크게 보일 수 있습니다.
# ext4 예시: 예약 블록 확인
sudo tune2fs -l /dev/sdXN | grep -E 'Reserved block count|Block count|Reserved block percentage'
예약 비율을 줄일 수도 있지만(예: 데이터 볼륨), 시스템 파티션에서는 무리하게 줄이면 장애 시 복구 여지가 줄어듭니다.
# 예약 비율을 1%로(데이터 볼륨에서만 신중히)
sudo tune2fs -m 1 /dev/sdXN
7) 그래도 안 잡히면: “df와 du의 차이”를 수치로 대조
아래는 한 파일시스템에서 df 사용량과 du 합계를 비교해 갭이 큰지 확인하는 패턴입니다.
# 대상 마운트가 /var라고 가정
DF_USED=$(df -B1 /var | awk 'NR==2{print $3}')
DU_USED=$(sudo du -sxB1 /var 2>/dev/null | awk '{print $1}')
echo "df used: $DF_USED"
echo "du used: $DU_USED"
echo "gap : $((DF_USED - DU_USED)) bytes"
- gap이 크면: (deleted) 열린 파일, 스냅샷/CoW, 메타데이터 오버헤드 등을 더 의심
- gap이 작으면: 단순히
du를 잘못 돌렸거나(다른 마운트 포함/제외), 특정 디렉터리 권한 때문에 누락되었을 가능성
권한 누락을 줄이려면 sudo로 실행하고, 에러를 숨기지 말고 확인하세요.
sudo du -xhd1 /var | sort -h
8) 운영에서의 재발 방지 체크리스트
- 로그 로테이션이 “프로세스 핸들 갱신”까지 포함하는지 점검(
postrotate, reload/HUP) /var/log,/tmp, 앱 캐시 디렉터리에 상한(보관일/용량)을 정책화- 컨테이너 노드는 이미지/빌드 캐시 정리 전략 수립(무작정 prune 금지)
- inode 모니터링 추가:
node_filesystem_files_free,node_filesystem_files(node-exporter) - 디스크 90% 경보 시 즉시
lsof +L1자동 수집(사후 분석에 결정적)
9) 정리: 원인별 “가장 빠른 한 방”
df=100%,du로 큰 파일이 안 보임 →sudo lsof +L1로 (deleted) 열린 파일 찾기 → 서비스 재시작- “No space left on device”인데 용량은 남음 →
df -ih로 inode 확인 → 파일 수 폭증 디렉터리 정리 - 컨테이너 노드 디스크 급증 →
/var/lib/docker|containerd점검, 이미지/로그/볼륨 정책화 - 작은 볼륨에서 100%처럼 보임 → ext 예약 블록 확인(
tune2fs)
이 루틴만 몸에 익혀도 “디스크 100%인데 용량이 안 보이는” 대부분의 케이스를 10분 안에 해결할 수 있습니다.