2026년 7월 2일 | 개발 깨알 상식_Tips
디스크 사용률이 갑자기 90%를 넘었는데, 막상 큰 디렉터리를 지워도 공간이 돌아오지 않을 때가 있다. 처음 겪으면 꽤 당황스럽다. 분명히 오래된 로그 파일을 삭제했고, du로 보면 디렉터리 크기도 줄었는데, df는 여전히 파일시스템이 가득 찼다고 말한다. 이럴 때 나는 먼저 “삭제가 실패했나?”보다 아직 열린 파일이 있는지를 본다.
df와 du는 같은 질문을 보지 않는다
df와 du는 둘 다 디스크 용량을 보여 주지만, 실제로는 다른 질문에 답한다. df는 파일시스템 전체의 할당 상태를 본다. 블록이 아직 누구에게 잡혀 있으면 경로가 보이지 않아도 사용 중으로 계산된다. 반면 du는 디렉터리 트리를 걸으며 현재 이름으로 접근 가능한 파일의 크기를 더한다. 그래서 파일 이름은 삭제됐지만 어떤 프로세스가 파일 디스크립터를 계속 들고 있으면, du에서는 빠지고 df에는 남는다.
운영 중인 서비스 로그에서 이 장면이 자주 나온다. 큰 로그 파일을 급하게 지웠는데, 웹 서버나 워커 프로세스가 그 파일을 계속 열어 둔 상태라면 커널 입장에서는 아직 블록을 반환할 수 없다. 파일의 디렉터리 엔트리는 사라졌지만, 열린 핸들이 살아 있어서 실제 데이터 블록은 붙잡힌 상태다. 그래서 삭제했다와 공간이 반환됐다를 같은 말로 보면 안 된다.
df -h /var
sudo du -sh /var/log
삭제했는데 공간이 안 돌아오는 경우
내가 보는 첫 번째 신호는 두 숫자의 차이다. 예를 들어 df -h /var에서는 92% 사용 중인데, du -sh /var/log와 주변 큰 디렉터리를 더해도 설명이 안 되는 빈틈이 있다면 열린 삭제 파일을 의심한다. 이때 바로 재부팅으로 가면 원인은 사라질 수 있지만, 다음에도 같은 상황을 반복한다. 어떤 프로세스가 어떤 파일을 붙잡았는지 한 줄이라도 남겨 두는 편이 낫다.
리눅스에서는 lsof +L1이 꽤 직접적인 힌트를 준다. 링크 수가 1보다 작은, 즉 디렉터리 이름으로는 더 이상 접근하기 어려운 열린 파일을 찾는다. 권한 때문에 안 보이는 항목이 있을 수 있으니 운영 서버에서는 보통 sudo를 붙여 본다. 출력에서 중요한 건 파일명보다 프로세스 이름, PID, 파일 크기, 마운트 위치다. 이 네 가지가 있어야 어떤 서비스를 재시작해야 하는지, 아니면 더 안전한 정리 방법이 있는지 판단할 수 있다.
sudo lsof -nP +L1
확인할 때 남기는 세 줄
나는 이런 상황에서 로그를 길게 남기지 않는다. 대신 세 줄만 고정한다. 첫째, df 기준으로 어느 마운트가 몇 퍼센트까지 찼는지다. 둘째, du 기준으로 가장 큰 디렉터리 합계가 얼마인지다. 셋째, lsof +L1에서 확인된 PID와 삭제된 파일 크기다. 이 정도만 있어도 “디스크가 찼다”라는 넓은 문장이 “프로세스 A가 삭제된 로그 B를 38GB 들고 있다”로 좁아진다.
이 차이가 중요하다. 디스크 이슈는 흔히 청소 문제처럼 보이지만, 실제로는 로그 회전 정책, 프로세스 재시작 방식, 컨테이너 볼륨 경계, 파일 핸들 수명 문제가 섞여 있다. 원인을 좁히지 않고 오래된 파일만 더 지우면 잠깐 버틴 것처럼 보여도, 파일시스템 사용률은 그대로일 수 있다. 반대로 열린 삭제 파일 하나를 확인하면 조치가 훨씬 작아진다.
mount: /var
filesystem_usage: 92%
directory_sum: 54G
open_deleted_file: worker PID 1842, 38G
바로 죽이기보다 재시작 경계를 본다
주의할 점도 있다. lsof +L1에서 큰 파일을 잡은 PID가 보인다고 해서 바로 kill -9로 끝내면 안 된다. 그 프로세스가 요청 처리 중인지, 큐 작업을 들고 있는지, supervisor가 안전하게 다시 띄우는지 먼저 봐야 한다. 웹 서버라면 graceful reload가 맞을 수 있고, 배치 워커라면 큐 ack 정책을 확인해야 한다. 디스크 공간을 돌려받는 조치가 데이터 중복 처리나 요청 실패로 바뀌면 더 비싸다.
다음부터 같은 일을 줄이려면 로그 회전 방식도 같이 본다. 프로세스가 파일을 계속 들고 있는 구조라면 단순 삭제보다 rotate 후 reload가 맞을 때가 많다. 어떤 서비스는 copytruncate가 필요하고, 어떤 서비스는 신호를 받아 새 로그 파일을 다시 여는 편이 더 낫다. 정답은 서비스마다 다르지만, 최소한 df와 du 차이, 열린 삭제 파일, 안전한 재시작 경계를 한 번에 보게 되면 문제를 훨씬 덜 크게 만든다.
결국 이 팁은 용량을 아끼는 명령어 하나가 아니라, 디스크 장애를 읽는 순서에 가깝다. 이름 있는 파일을 지웠는지보다 커널이 블록을 돌려줄 수 있는 상태인지 먼저 확인한다. 특히 배포 직전이나 야간 배치 중에는 이 차이가 불필요한 알림 피로를 조금 줄인다. 그 차이를 알고 있으면 “분명히 지웠는데 왜 아직 가득 차 있지?”라는 시간을 꽤 줄일 수 있다.
'[개발 깨알 상식_Tips]' 카테고리의 다른 글
| 빈 환경변수가 덮어쓴 API 기본 URL (0) | 2026.07.03 |
|---|---|
| 릴리스 목록에서 1.10이 1.2보다 앞서는 문제 (0) | 2026.07.03 |
| 체크섬 manifest로 잡는 깨진 다운로드 (0) | 2026.07.01 |
| 첫 실패 기준으로 줄인 pytest 로그 (0) | 2026.06.30 |
| 서버 동기화 직전의 삭제 파일 경고 (0) | 2026.06.26 |