Published on

리눅스 디스크 100%? lsof로 삭제파일 점유 해결

Authors

서버에서 갑자기 디스크가 100%가 되면 보통 du로 큰 디렉터리를 찾아 지우는 방식으로 접근합니다. 그런데 df는 꽉 찼다고 하는데 du로는 큰 파일이 보이지 않거나, 지웠다고 생각했는데도 용량이 줄지 않는 경우가 있습니다. 이때 가장 흔한 원인 중 하나가 삭제된 파일을 어떤 프로세스가 계속 열고 있는 상태입니다.

리눅스에서는 파일을 삭제(rm)해도, 그 파일을 열고 있는 파일 디스크립터가 남아 있으면 실제 데이터 블록은 즉시 회수되지 않습니다. 즉, 파일은 디렉터리 엔트리에서는 사라졌지만(그래서 du에 안 잡힘), 커널은 프로세스가 닫을 때까지 데이터를 유지합니다. 이 글에서는 lsof로 그 상황을 진단하고, 안전하게 공간을 회수하는 방법을 단계별로 정리합니다.

운영 환경에서 디스크 100%는 장애로 직결됩니다. 특히 DB, 로그, 컨테이너 런타임, 에이전트(모니터링/보안)가 원인이 되는 경우가 많습니다. 쿠버네티스/EKS 환경이라면 노드 디스크 압박이 Pod Pending으로 이어질 수 있으니, 연관 이슈는 K8s Pod가 Pending? 스케줄링 실패 12가지도 함께 참고하면 좋습니다.

1) 증상 패턴: df는 100%인데 du로는 안 보인다

대표적인 증상은 다음과 같습니다.

  • df -h에서 특정 마운트(/, /var, /data 등)가 100%
  • du -sh /var/* 같은 탐색으로는 큰 파일이 안 보이거나, 지운 후에도 df가 줄지 않음
  • 서비스 재시작/로그 로테이션 이후에도 회복이 안 됨

먼저 기본 확인부터 합니다.

# 파일시스템 사용량(커널 관점)
df -h

# inode 고갈인지도 같이 확인 (inode 100%면 파일 생성 자체가 막힙니다)
df -ih

# 디렉터리 기준 사용량(디렉터리 엔트리 기반)
du -xh /var | sort -h | tail -n 20

dfdu가 크게 어긋난다면, 삭제된 파일 점유(혹은 마운트/바인드, 스냅샷, overlay 계층 등)를 의심할 타이밍입니다.

2) 원리: 삭제했는데 왜 용량이 안 돌아오나

유닉스 계열 파일시스템에서 파일은 크게 두 요소로 관리됩니다.

  • 디렉터리 엔트리(파일명과 inode를 연결)
  • inode와 데이터 블록(실제 내용)

rm은 디렉터리 엔트리 링크를 제거합니다. 하지만 어떤 프로세스가 이미 그 파일을 열어서 파일 디스크립터를 가지고 있다면, 커널은 참조 카운트가 0이 될 때까지(모든 FD가 닫힐 때까지) 데이터 블록을 유지합니다.

그래서

  • du는 디렉터리 트리를 따라가며 계산하므로 삭제된 파일은 잡히지 않고
  • df는 파일시스템의 실제 블록 사용량을 보여주므로 여전히 꽉 찬 것으로 보입니다.

이 간극을 메우는 도구가 lsof입니다.

3) lsof로 삭제된 파일을 잡아내기

3-1) 가장 많이 쓰는 한 줄

# 삭제(deleted)된 파일을 열고 있는 프로세스 찾기
sudo lsof +L1
  • +L1은 링크 수가 1보다 작은(즉, 디렉터리 엔트리가 없는) 열린 파일을 의미합니다.
  • 출력에 (deleted)가 붙는 항목이 핵심입니다.

3-2) 특정 마운트/경로에서만 찾기

디스크가 꽉 찬 마운트가 /var라면 범위를 좁히는 게 좋습니다.

sudo lsof +L1 /var

또는 파일시스템 단위로 잡고 싶다면 df에서 문제 파티션의 마운트 포인트를 확인한 뒤 그 경로를 넣습니다.

3-3) 출력에서 봐야 할 컬럼

lsof 출력에서 주로 확인하는 값은 다음입니다.

  • COMMAND: 어떤 프로세스인지
  • PID: 프로세스 ID
  • USER: 실행 사용자
  • FD: 파일 디스크립터 번호 (예: 1w, 2w, 3u)
  • TYPE: REG(일반 파일) 등
  • SIZE/OFF: 파일 크기
  • NAME: 경로 및 (deleted) 표시

예시 상황(개념 예시):

  • java 프로세스가 /var/log/app.log (deleted)를 수십 GB로 잡고 있음
  • nginx가 오래된 access log를 계속 쓰고 있음
  • dockerd 또는 containerd가 컨테이너 로그를 잡고 있음

4) 해결 전략: “프로세스가 잡고 있는 FD”를 안전하게 놓게 하기

삭제된 파일 점유를 해제하는 방법은 크게 3가지입니다.

  1. 프로세스를 재시작해서 FD를 닫게 한다 (가장 흔하고 안전)
  2. 해당 FD를 직접 truncate해서 공간만 회수한다 (응급 처치)
  3. 로그 로테이션/설정을 수정해 재발을 막는다 (근본 해결)

4-1) 가장 권장: 서비스 재시작

점유 주체가 명확하고 재시작이 가능한 서비스라면 재시작이 가장 깔끔합니다.

# systemd 서비스 예시
sudo systemctl restart nginx
sudo systemctl restart myapp

# 프로세스 직접 종료(권장도는 낮음)
sudo kill -TERM 12345

재시작 후 다시 확인합니다.

sudo lsof +L1

df -h

디스크가 즉시 회수되는 것이 정상입니다.

4-2) 재시작이 어렵다면: FD에 대해 truncate(공간만 회수)

장애 상황에서 “지금 당장 공간만 확보”해야 하는 경우가 있습니다. 이때 프로세스를 죽이지 않고도 열린 FD가 가리키는 파일 내용을 0바이트로 줄여 공간을 회수할 수 있습니다.

핵심은 /proc/[pid]/fd/[fd]를 이용하는 것입니다.

  1. lsof에서 PID와 FD를 확인합니다. 예를 들어 PID가 12345, FD가 4w라면 숫자 4가 FD 번호입니다.
  2. 아래 중 하나를 실행합니다.
# 방법 A: shell 리다이렉션으로 0바이트로 만들기
sudo sh -c 'true > /proc/12345/fd/4'

# 방법 B: truncate 사용
sudo truncate -s 0 /proc/12345/fd/4

주의점:

  • 이 방식은 파일 내용을 날리는 것이므로, 로그 파일이라면 괜찮지만 데이터 파일(DB 파일 등)에 하면 치명적일 수 있습니다.
  • 반드시 lsof에서 NAME이 로그/캐시/임시 파일인지 확인하고 진행하세요.

4-3) 로그 로테이션 문제라면: 설정 점검

삭제된 로그를 계속 점유하는 케이스는 보통 로테이션 정책과 프로세스의 로그 reopen 동작이 어긋날 때 발생합니다.

  • logrotate를 쓰는 경우, copytruncate를 쓸지 postrotate에서 서비스에 reopen 신호를 보낼지 선택이 필요합니다.
  • nginx는 보통 USR1 신호로 로그 파일을 다시 엽니다.

예시(nginx):

# nginx가 로그를 다시 열도록 유도
sudo kill -USR1 $(cat /run/nginx.pid)

앱이 자체적으로 로그 파일을 열어두는 방식이라면, 로거 설정(예: logback, log4j2)에서 rolling 정책을 점검해야 합니다.

5) 실전 점검 플로우(복붙용)

장애 대응 시 순서대로 실행하기 좋은 플로우입니다.

# 1) 어디가 찼는지 확인
df -h

# 2) inode 문제인지 확인
df -ih

# 3) du로 큰 디렉터리 확인(일반적인 대용량 원인)
du -xh /var | sort -h | tail -n 30

# 4) df는 큰데 du가 애매하면, 삭제 파일 점유 확인
sudo lsof +L1 | sort -k7 -h | tail -n 30

# 5) 특정 마운트만 보고 싶으면 경로 제한
sudo lsof +L1 /var | sort -k7 -h | tail -n 30

sort -k7 -h는 환경에 따라 컬럼 위치가 달라질 수 있습니다. 정렬이 기대대로 안 되면 정렬은 빼고 SIZE/OFF 큰 항목을 눈으로 확인하는 게 더 안전합니다.

6) 컨테이너/쿠버네티스 환경에서의 추가 포인트

쿠버네티스 노드에서 디스크가 100%가 되는 흔한 원인 중 하나는 컨테이너 로그/이미지/레이어입니다. 다만 이 글의 주제처럼 “삭제된 파일 점유”도 컨테이너 런타임이나 로깅 에이전트가 관련될 수 있습니다.

  • 노드에서 lsof +L1 결과에 containerd, dockerd, fluent-bit, filebeat 등이 보이면 로그 파이프라인을 의심합니다.
  • 노드 디스크 압박은 스케줄링 실패로 이어질 수 있으니, 장애가 Pending으로 확산되는 상황이라면 K8s Pod가 Pending? 스케줄링 실패 12가지에서 DiskPressure 관련 항목도 같이 확인하세요.

7) 자주 하는 실수와 안전 가이드

7-1) “파일을 지웠는데 왜 안 줄지?”만 반복

rm은 디렉터리 엔트리만 제거합니다. 열린 FD가 남아 있으면 공간은 그대로입니다. 이때 du만 계속 보는 건 시간 낭비가 되기 쉽습니다.

7-2) 무작정 kill -9

SIGKILL은 즉시 종료라서 FD는 닫히며 공간은 회수되겠지만, 데이터 정합성/정리 작업을 건너뛸 수 있습니다. 가능한 systemctl restart 또는 kill -TERM 같은 정상 종료 경로를 우선하세요.

7-3) /proc/[pid]/fd/[fd] truncate를 아무 파일에나 적용

로그 파일이면 대체로 안전하지만, DB나 큐, 스토리지 파일에 적용하면 장애가 더 커질 수 있습니다. lsof 출력의 NAME과 프로세스 역할을 확인하고, 확신이 없으면 재시작/롤백 가능한 방향을 선택하세요.

8) 마무리: dfdu가 다르면 lsof +L1부터

정리하면,

  • df는 파일시스템 실제 사용량
  • du는 디렉터리 트리 기준 사용량
  • 둘이 어긋나고 삭제했는데도 용량이 안 줄면, 프로세스가 삭제 파일을 점유 중일 가능성이 큼

가장 빠른 진단은 sudo lsof +L1이고, 해결은 보통 “해당 프로세스 재시작”입니다. 재시작이 어려운 상황에서는 /proc 경로의 FD를 truncate하는 응급 처치가 가능하지만, 적용 대상이 로그/임시 파일인지 반드시 확인한 뒤 진행하세요.