Published on

리눅스 디스크 100%? 열린 삭제파일 lsof로 찾기

Authors

서버에서 갑자기 df -h가 100%를 찍는데, du -sh /*로는 큰 파일이 안 보이는 상황을 한 번쯤 겪습니다. 이때 가장 흔한 원인은 삭제됐지만 프로세스가 아직 열고 있는(open) 파일입니다. 리눅스는 파일을 지워도(디렉터리 엔트리 제거) 그 파일을 참조하는 파일 디스크립터가 남아 있으면 실제 블록은 해제하지 않습니다. 그래서 디스크는 가득 찼는데, 경로 기반으로 합산하는 du에는 안 잡히는 “유령 용량”이 생깁니다.

이 글에서는 lsof를 중심으로 열린 삭제파일을 찾고, 서비스 중단을 최소화하면서 디스크 공간을 회수하는 실전 절차를 다룹니다.

증상: df는 100%인데 du는 멀쩡한 이유

  • df: 파일시스템의 할당된 블록 사용량을 봅니다. 삭제된 파일이라도 블록이 남아 있으면 사용량에 포함됩니다.
  • du: 디렉터리 트리를 순회하며 경로로 접근 가능한 파일 크기를 합산합니다. 삭제되어 경로가 사라진 파일은 집계되지 않습니다.

따라서 아래와 같은 전형적인 불일치가 발생합니다.

df -h
# /dev/nvme0n1p1  100G  100G   0G 100% /

du -sh /var/log
# 2.1G  /var/log

이 상황에서 가장 먼저 의심할 것은 (1) 열린 삭제파일, (2) 마운트 포인트 착시(다른 파일시스템 아래에 쌓인 데이터), (3) inode 고갈입니다. 이 글은 그중 (1)을 해결하는 방법입니다.

열린 삭제파일이 생기는 대표 케이스

  • 로그 로테이션 설정이 잘못되어 copytruncate 없이 파일을 rm로 날려버림
  • 애플리케이션이 대용량 임시파일을 만들고 삭제했지만 프로세스가 계속 쓰는 중
  • 컨테이너/노드에서 stdout 로그 파일이 커졌는데 런타임이 파일 디스크립터를 계속 유지
  • Java, Python, Go 등 장수 프로세스가 파일 핸들을 오래 쥠

특히 logrotate 이후에도 디스크가 줄지 않는다면, 프로세스가 예전 inode를 계속 잡고 있을 가능성이 큽니다.

1단계: lsof로 (deleted) 파일 찾기

lsof는 “어떤 프로세스가 어떤 파일을 열고 있는지”를 보여줍니다. 삭제된 파일은 보통 경로 끝에 (deleted)로 표시됩니다.

가장 기본적인 탐색

sudo lsof | grep -E "\(deleted\)" | head

다만 전체 lsof는 출력이 매우 크고 느릴 수 있습니다. 파일시스템/디렉터리 범위를 좁히는 습관이 좋습니다.

특정 마운트(/)에서 삭제된 파일만 빠르게

sudo lsof +L1 /
  • +L1: 링크 수가 1보다 작은(즉 0인) 파일, 흔히 삭제된 열린 파일을 찾는 옵션입니다.
  • 뒤의 /는 스캔 범위를 루트 파일시스템으로 제한합니다.

용량 큰 순으로 보고 싶다면

lsof 출력만으로는 크기 정렬이 애매할 때가 많습니다. awk로 size 컬럼이 있는 경우를 활용해 정렬합니다(환경에 따라 컬럼이 다를 수 있어 참고용입니다).

sudo lsof +L1 -nP | awk '{print $7, $2, $1, $9}' | sort -nr | head -20
  • -nP: DNS 역조회/포트 변환을 끄고 빠르게
  • $7이 SIZE/OFF로 나오지 않는 시스템도 있으니, 결과가 이상하면 다음 섹션의 /proc 방식으로 확인합니다.

2단계: df로 “숨은 사용량” 여부 확인

삭제파일이 원인이라면, 보통 df는 100%인데 du 합계가 낮습니다. 간단히 비교합니다.

sudo du -xhd1 / | sort -h

df -h /
  • -x: 다른 파일시스템(mount)을 넘어가지 않음

이때 du로 보이는 합계가 df보다 한참 낮으면 열린 삭제파일 가능성이 더 커집니다.

3단계: 어떤 프로세스가 잡고 있는지 식별

lsof +L1 결과에서 중요한 것은 PID, 프로세스명, FD, 파일 경로(삭제 표시) 입니다.

예시(형태는 시스템마다 다름):

nginx   1234 www-data  12w  REG  8,1  2147483648  1234567 /var/log/nginx/access.log (deleted)

여기서 핵심은:

  • PID 1234가 FD 12w(쓰기 모드)로 삭제된 로그 파일을 계속 쓰고 있음
  • 이 파일의 블록 때문에 디스크가 회수되지 않음

4단계: 안전하게 공간 회수하는 3가지 방법

운영 환경에서는 “무조건 kill”이 답이 아닙니다. 상황에 맞는 방법을 선택합니다.

방법 A) 프로세스 재시작(가장 확실)

삭제된 열린 파일을 잡고 있는 프로세스를 재시작하면 FD가 닫히고 블록이 해제됩니다.

sudo systemctl restart nginx
# 또는
sudo kill -HUP 1234
  • 데몬/웹서버는 HUP으로 로그 재오픈을 지원하는 경우가 많습니다.
  • 재시작이 가능한 서비스라면 가장 깔끔합니다.

방법 B) /proc의 fd를 이용해 “파일을 0바이트로 truncate”

서비스 중단이 어렵고, 지금 당장 공간이 급하면 열린 FD가 가리키는 파일을 0으로 줄여 블록을 회수할 수 있습니다.

중요: 경로가 삭제되어도 FD는 /proc에 남아 있습니다.

  1. 어떤 FD인지 확인:
sudo lsof -p 1234 | grep deleted
  1. 해당 FD를 truncate:
sudo sh -c ':
> /proc/1234/fd/12'
  • 12는 FD 번호입니다.
  • :는 no-op이고, > 리다이렉션으로 길이를 0으로 만듭니다.
  • 이 작업은 “프로세스가 계속 쓰는 파일”이면 다시 커질 수 있으니, 근본적으로는 로그 재오픈/로테이션을 바로잡아야 합니다.

방법 C) 로그 로테이션/애플리케이션 설정 수정(재발 방지)

대표적으로 logrotate에서 애플리케이션이 로그 파일을 다시 열도록 신호를 보내야 합니다.

예시 logrotate 설정(nginx):

/var/log/nginx/*.log {
  daily
  rotate 14
  compress
  missingok
  notifempty
  sharedscripts
  postrotate
    /bin/systemctl kill -s HUP nginx.service >/dev/null 2>&1 || true
  endscript
}

이렇게 하면 삭제/교체된 로그 파일을 nginx가 다시 열어, 오래된 inode를 붙잡는 문제를 줄일 수 있습니다.

5단계: 컨테이너/쿠버네티스 환경에서의 포인트

  • 노드 디스크 100%인데 파드 내부 du로는 안 보일 때, 노드의 프로세스(컨테이너 런타임, 로깅 에이전트) 가 삭제파일을 잡고 있을 수 있습니다.
  • lsof는 보통 노드에서 실행해야 합니다.

컨테이너 런타임이 잡고 있는지 확인:

sudo lsof +L1 | grep -E "containerd|dockerd|cri-o" | head

로그가 원인이라면 로깅 드라이버 설정, 로그 파일 제한, 로테이션 정책도 같이 점검해야 합니다.

6단계: 디스크가 꽉 찼을 때의 “응급 처치” 체크리스트

  1. 현재 사용량 확인
df -h
  1. inode 고갈 여부(파일이 너무 많아도 100%처럼 보일 수 있음)
df -ih
  1. 열린 삭제파일 확인
sudo lsof +L1 /
  1. 상위 1~2개 원인 PID를 잡아 재시작 또는 truncate
sudo systemctl restart <service>
# 또는
sudo sh -c '>
/proc/1234/fd/12'
  1. 재발 방지: logrotate postrotate 신호, 애플리케이션 로그 설정 수정

운영에서 이런 작업을 자동화할 때는 스크립트 안전장치가 중요합니다. 예를 들어 bash 스크립트에서 변수 누락이나 파이프 실패를 조기에 터뜨리려면 [bash set -euo pipefail로 스크립트 폭발 막기](https://beautifulsoup.dev/blog/bash-set-euo-pipefail-prevent-script-explosions)도 함께 참고하면 좋습니다.

자주 하는 실수와 주의점

  • rm -rf로 지웠는데 용량이 안 줄어든다고 “더 지우는” 행동: 원인이 열린 FD면 효과 없습니다.
  • 무작정 kill -9: 파일은 닫히지만 서비스 장애가 날 수 있습니다. 먼저 HUP/재시작 가능 여부를 확인하세요.
  • /proc/PID/fd/FD truncate는 응급 처치: 애플리케이션이 계속 쓰면 다시 차오릅니다.
  • lsof가 느릴 때: -nP로 속도를 올리고, 특정 마운트나 디렉터리로 범위를 제한하세요.

마무리

df 100% + du로 원인이 안 보이면, 높은 확률로 삭제됐지만 열린 파일이 디스크를 점유하고 있습니다. lsof +L1로 (deleted) 파일을 찾고, 서비스 특성에 맞게 HUP/재시작 또는 /proc FD truncate로 공간을 회수하세요. 그 다음에는 logrotate와 애플리케이션 로그 정책을 손봐 재발을 막는 것이 핵심입니다.