Published on

리눅스 디스크 100%인데 삭제해도 안 줄 때

Authors

운영 중인 리눅스 서버에서 디스크가 100%에 도달해 급히 로그를 지우거나 큰 파일을 rm 했는데도 df 상 사용량이 그대로인 경우가 있습니다. 이런 상황은 특히 애플리케이션 로그, 웹서버 접근 로그, 컨테이너 로그, 데이터베이스 로그 등 “계속 쓰이는 파일”에서 자주 발생합니다.

핵심 원인은 단순합니다. 파일은 삭제됐지만(디렉터리 엔트리 제거), 어떤 프로세스가 그 파일 디스크립터를 계속 열고 있어서(inode 참조 유지) 공간이 해제되지 않는 것입니다. 이 글에서는 이 현상을 빠르게 판별하고, lsoffuser 로 범인을 찾은 뒤, 서비스 영향도를 최소화하면서 공간을 회수하는 실전 절차를 정리합니다.

또한 디스크 이슈는 종종 다른 장애(예: 파드 재시작, OOM, 이미지 풀 실패)로 연쇄 전파되므로, 장애 대응 관점에서 함께 읽기 좋은 글로 Kubernetes OOMKilled 진단과 메모리 누수 추적 실전, K8s ImagePullBackOff - registry 인증·캐시 진단법 도 참고할 만합니다.

1) 증상: du 는 줄었는데 df 는 그대로

가장 흔한 패턴은 아래처럼 나타납니다.

  • df -h 는 여전히 100%에 가깝다
  • 큰 디렉터리를 du -sh 로 보면 분명히 용량이 줄었다
  • 그런데도 디스크가 꽉 찬 상태가 지속된다

먼저 파일시스템 사용량을 확인합니다.

df -h

문제 지점을 대략적으로 찾습니다.

du -xh --max-depth=1 /var | sort -h

du현재 디렉터리 트리에 “보이는” 파일만 합산합니다. 반면 df 는 파일시스템 관점에서 아직 해제되지 않은 블록을 포함합니다. 삭제된 파일을 어떤 프로세스가 계속 열고 있으면, du 에서는 사라졌는데 df 에는 남아있는 것처럼 보입니다.

2) 왜 삭제했는데 공간이 안 줄까: 열린 파일 디스크립터

리눅스(정확히는 유닉스 계열)의 파일 삭제는 “데이터 블록을 즉시 제거”가 아니라, 대략 아래 흐름을 따릅니다.

  1. 파일명과 inode를 연결하는 디렉터리 엔트리가 제거됨
  2. inode의 링크 카운트가 0이 됨
  3. 하지만 어떤 프로세스가 해당 inode를 가리키는 파일 디스크립터를 열고 있으면 참조가 남음
  4. 마지막 파일 디스크립터가 닫힐 때 실제 공간이 반환됨

그래서 로그를 rm 해도, 로그를 쓰는 프로세스가 계속 그 파일에 쓰고 있으면 디스크는 안 줄어듭니다.

3) 1차 진단: lsof 로 삭제된 열린 파일 찾기

가장 확실한 방법은 lsof 로 “삭제됐지만 열린 파일”을 찾는 것입니다.

3-1) 전체 시스템에서 deleted 검색

sudo lsof | grep -i deleted

출력에는 보통 다음 정보가 포함됩니다.

  • 프로세스명과 PID
  • 열려있는 파일 디스크립터
  • 파일 경로(종종 (deleted) 로 표시)
  • 파일 크기(오래된 로그가 그대로 남아있는 경우가 많음)

다만 전체 lsof 는 시스템에 따라 무겁습니다. 특정 마운트나 디렉터리로 범위를 줄이는 편이 안전합니다.

3-2) 특정 마운트(예: /var)에 한정

sudo lsof +L1 /var

여기서 +L1 은 링크 카운트가 1 미만인 파일(즉 삭제되어 링크가 0인 파일)을 보여줍니다. 운영에서 가장 유용한 옵션 중 하나입니다.

3-3) 용량이 큰 순서로 보고 싶을 때

lsof 결과를 정렬해 큰 파일부터 처리하면 회수 효과가 빠릅니다.

sudo lsof +L1 /var | awk '{print $7, $2, $1, $9}' | sort -n

환경에 따라 컬럼이 다를 수 있으니, 먼저 원본 출력 포맷을 확인하고 조정하세요.

4) 2차 진단: fuser 로 “누가 쓰는지” 빠르게 확인

fuser 는 특정 파일이나 마운트를 점유 중인 PID를 빠르게 보여줍니다.

4-1) 특정 파일을 점유한 프로세스 확인

sudo fuser -v /var/log/syslog

4-2) 특정 마운트 전체를 점유한 프로세스 확인

예를 들어 /var 파티션이 꽉 찼다면:

sudo fuser -vm /var

-m 은 마운트 포인트 기준, -v 는 상세 출력입니다.

주의할 점은, fuser 가 보여주는 것은 “삭제된 파일”에 한정이 아니라 “해당 파일 또는 마운트를 사용하는 프로세스”라서 해석이 필요합니다. 삭제된 열린 파일을 찾는 용도는 lsof +L1 이 더 직관적이고, fuser 는 PID를 빠르게 잡아내는 보조 도구로 쓰는 편이 좋습니다.

5) 해결 전략: 안전한 순서로 공간 회수하기

삭제된 열린 파일을 확인했다면, 이제 “그 파일 디스크립터를 닫게” 만들어야 합니다. 방법은 크게 3가지입니다.

5-1) 가장 안전: 서비스 재시작(또는 로그 로테이트 트리거)

로그 파일을 계속 쓰는 프로세스(예: nginx, gunicorn, java, node)라면, 프로세스를 재시작하면 열린 파일 디스크립터가 닫히며 공간이 회수됩니다.

systemd 환경이라면:

sudo systemctl restart nginx

컨테이너 환경이라면 해당 파드/컨테이너 재시작이 동일한 효과를 냅니다.

재시작이 부담이라면, 애플리케이션이 로그 재오픈 시그널을 지원하는지 확인하세요. 예를 들어 nginx 는 설정 리로드로 로그를 다시 엽니다.

sudo nginx -s reload

5-2) 운영에서 자주 쓰는 방식: logrotate 강제 실행

로그 로테이션이 제대로 구성되어 있다면, 강제로 돌려서 로그 파일 핸들을 새 파일로 유도할 수 있습니다.

sudo logrotate -f /etc/logrotate.conf

로그 로테이트 설정에 copytruncate 를 쓰는지, postrotate 에서 재오픈 시그널을 보내는지에 따라 동작이 달라집니다. 대용량 로그를 다루는 서비스는 일반적으로 copytruncate 보다 “재오픈 시그널 방식”이 더 안전하고 성능상 유리한 경우가 많습니다.

5-3) 최후의 수단: 프로세스 종료 또는 FD를 직접 비우기

(1) 프로세스 종료

삭제된 열린 파일이 엄청나게 크고, 재시작이 불가피한데 서비스 영향이 허용된다면 종료가 가장 확실합니다.

sudo kill -TERM 12345

정리되지 않으면:

sudo kill -KILL 12345

-KILL 은 최후의 수단입니다. 데이터 손상 가능성이 있는 프로세스(특히 DB)에는 매우 위험합니다.

(2) 파일 디스크립터를 직접 truncate

“프로세스는 살려야 하는데 디스크만 급히 비워야” 하는 상황에서 종종 쓰는 방법입니다. 다만 권장 순위는 낮습니다.

먼저 해당 프로세스가 가진 삭제된 파일 FD를 찾습니다.

sudo lsof -p 12345 | grep -i deleted

리눅스에서는 /proc 아래에서 FD를 파일처럼 다룰 수 있습니다. 예를 들어 FD 번호가 9 라면 아래처럼 크기를 0으로 만들 수 있습니다.

: > /proc/12345/fd/9

위 명령은 셸 내장 : 와 리다이렉션을 이용해 해당 FD로 빈 내용을 써서 truncate 효과를 냅니다.

주의사항:

  • 무엇을 비우는지 정확히 알고 해야 합니다(로그 파일에만 제한 권장).
  • 애플리케이션이 파일 오프셋/쓰기 오류를 어떻게 처리하는지에 따라 부작용이 있을 수 있습니다.

6) 실전 절차 체크리스트(장애 대응용)

디스크 100% 상황에서는 “빨리 비우기”와 “원인 재발 방지”를 동시에 해야 합니다.

6-1) 빠른 복구 플로우

  1. df -h 로 꽉 찬 파일시스템 확인
  2. sudo lsof +L1 해당_마운트 로 삭제된 열린 파일 확인
  3. 큰 파일을 잡고 있는 프로세스 PID 확인
  4. 가능한 경우 systemctl restart 또는 앱 리로드로 FD 닫기
  5. df -h 로 즉시 회수됐는지 확인

6-2) 재발 방지 플로우

  • 로그 로테이트 정책 재점검(주기, 보관 개수, 압축, 최대 크기)
  • 애플리케이션 로그 경로가 의도한 파티션에 있는지 확인
  • 컨테이너 로그가 노드 디스크를 잠식하는지 점검
  • 디스크 알람 임계치 조정(예: 70%, 85%, 95% 단계 알람)

Kubernetes 환경이라면 디스크 압박은 파드 축출, 이미지 풀 실패로 이어질 수 있어, 디스크 문제를 “노드 장애”로 다루는 편이 좋습니다. 이런 연쇄 장애 관점은 K8s ImagePullBackOff - registry 인증·캐시 진단법 글도 함께 보면 도움이 됩니다.

7) 자주 하는 실수와 주의점

7-1) rm -rf 로 지웠는데도 안 줄어 당황

정상입니다. 삭제된 파일을 프로세스가 계속 열고 있으면 공간은 유지됩니다. 이때는 “더 지우기”가 아니라 “열린 핸들 닫기”가 정답입니다.

7-2) DB 파일에 동일한 방법을 적용

데이터베이스의 데이터 파일을 이런 방식으로 건드리면 장애가 훨씬 커질 수 있습니다. DB는 별도의 관리 명령(예: PostgreSQL의 VACUUM 같은)과 스토리지 설계로 접근해야 합니다. DB 성능/용량 이슈는 PostgreSQL VACUUM·AUTOVACUUM 튜닝 - bloat로 느려질 때 같은 관점으로 분리해서 다루는 편이 안전합니다.

7-3) lsof 가 없거나 권한 문제

최소한 아래를 확인하세요.

which lsof
sudo -n true

lsof 가 없다면 배포판에 맞게 설치합니다.

# Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y lsof

# RHEL/CentOS/Amazon Linux
sudo yum install -y lsof

8) 요약

  • df 는 파일시스템 관점, du 는 디렉터리 트리 관점이라 결과가 다를 수 있습니다.
  • 삭제된 파일을 프로세스가 계속 열고 있으면 디스크 공간은 반환되지 않습니다.
  • sudo lsof +L1 마운트포인트 로 삭제된 열린 파일을 찾고, PID를 확인합니다.
  • 해결은 보통 “서비스 재시작” 또는 “로그 재오픈(리로드, logrotate)”로 FD를 닫는 것입니다.
  • 정말 급하면 /proc/PID/fd/FD 를 truncate 하는 방법도 있지만, 로그에 한정해 신중히 사용해야 합니다.