df와 du 결과가 다른 이유: 리눅스에서 숨겨진 기가바이트 찾기
df -h 명령어는 디스크 사용량이 95%라고 표시하는 반면, du -sh /*는 단지 20%만 합산합니다. 나머지 75%는 du에는 보이지 않지만 디스크 블록을 차지하고 있습니다. 그 이유는 삭제된 파일을 가리키는 프로세스의 열린 파일 디스크립터 때문입니다. df는 파일 시스템 슈퍼블록을 직접 읽고, du는 디렉토리 트리를 순회하며 그 안에 항목이 없는 파일은 무시합니다.
inode는 데이터 블록 목록을 포함한 파일 메타데이터를 저장합니다. rm은 디렉토리 항목(하드 링크)만 제거하며, inode 링크 수는 감소하지만 프로세스가 디스크립터를 보유하고 있다면 블록은 계속 할당된 상태로 남습니다.
다이어그램:
rm 실행 전:
디렉토리 inode 블록
/var/log/ --> app.log --> [block1][block2]
^
nginx 디스크립터 (pid 1234, fd 7)
rm 실행 후:
디렉토리 inode 블록
/var/log/ --> (삭제됨) --> [block1][block2]
^
nginx 디스크립터
du는 /var/log/의 파일을 볼 수 없지만, df는 점유된 블록을 기록합니다.
문제의 전형적인 사례
로그 로테이션: logrotate가 오래된 파일에 rm을 수행하지만, nginx나 java가 시작 시점의 디스크립터를 보유하고 있습니다. 프로세스는 삭제된 파일에 기록하고, df는 증가된 사용량을 등록하지만 du는 그렇지 않습니다.
다른 시나리오:
- 열린 로그를 가진 장기 실행 데몬.
- 디스크립터 닫힘보다 rm이 먼저 실행되는 정리 스크립트 버그.
- 내부 프로세스가 호스트 파일을 보유하는 Docker 컨테이너.
문제는 누적됩니다: 디스크가 가득 찰 때까지 기가바이트가 눈에 띄지 않게 사라집니다.
삭제된 파일을 가진 프로세스 찾기
NLINK < 1(파일 시스템에서 삭제되었지만 열려 있음)인 파일에 lsof +L1 사용:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 1234 www 7w REG 8,1 2147483648 0 12345 /var/log/app.log (deleted)
java 5678 app 3w REG 8,1 536870912 0 67890 /var/log/service.log (deleted)
주요 필드:
- COMMAND/PID: 프로세스와 ID.
- FD: 디스크립터 (7w — 쓰기, 번호 7).
- SIZE/OFF: 크기 — 손실된 GB의 원천.
- NLINK=0: 삭제 확인.
- NAME (deleted): rm 실행 전 경로.
크기 필터: lsof +L1 | awk '$7 > 104857600 {print}' (100MB 이상 파일).
재시작 없이 공간 확보
프로세스 PID 1234, FD 7의 경우:
$ > /proc/1234/fd/7
/proc는 메모리의 procfs입니다. 이 명령어는 디스크립터를 통해 파일을 제로화하고, 커널은 즉시 블록을 해제합니다. df가 업데이트되며, 프로세스는 fd 7에 계속 기록합니다.
확인: lsof +L1 | grep 1234 — SIZE/OFF=0 또는 비어 있음.
스크립트 자동화:
#!/bin/bash
lsof +L1 | awk '$7 > 104857600 && $9 ~ /deleted/ {print $2, $8}' | while read pid fd; do
echo "Zeroing $pid/$fd"
> /proc/$pid/fd/$fd
echo "Freed"
done
logrotate에서 문제 방지
로테이션에서 rm을 피하세요. /etc/logrotate.d/app에서:
/var/log/app.log {
daily
rotate 7
compress
copytruncate
}
copytruncate는 로그를 복사한 후 원본을 잘라냅니다(> 메커니즘). 디스크립터는 변경되지 않습니다.
단점: 복사 시 미세 지연 — 일부 줄 손실 가능성.
무손실 대안:
- logrotate: mv old → new file.
- kill -HUP <pid> (로그 재개를 위한 SIGHUP).
nginx: nginx -s reopen. systemd: systemctl reload nginx.
모니터링 및 경고
확인 스크립트:
lsof +L1 -F s | awk '/^s/ && substr($0,2)+0 > 104857600 {count++} END {print count+0}'
100MB 이상 삭제된 파일 수 출력.
Prometheus 통합:
node_filefd_allocated— 열린 fd 증가.- df/disk_used 대 du 합계 비교.
5분마다 cron: 불일치 >10% 시 경고.
핵심 요점
- df는 슈퍼블록의 할당된 블록을 보여주고, du는 디렉토리에서 보이는 파일을 보여줍니다.
lsof +L1는 NLINK=0으로 (삭제된) 파일을 가진 프로세스를 찾습니다.- 공간 확보:
> /proc/PID/fd/FD— 재시작 없이 제로화. - logrotate copytruncate는 HUP 없이 문제를 방지합니다.
- 모니터링: lsof 또는 node_exporter 메트릭을 사용한 스크립트.
— Editorial Team
아직 댓글이 없습니다.