큰 로그 파일을 지웠고 디렉터리에서도 사라졌는데 df의 여유 공간은 거의 늘지 않는다. 이때 확인할 후보 중 하나는 삭제한 파일을 서비스가 계속 열고 있는 경우다. 이름이 사라졌다는 사실과 파일을 사용하는 프로그램이 손을 놓았다는 사실은 다르다. 이 글에서는 작은 임시 로그로 그 차이를 재현하고, 실제 서버에서 어떤 대상을 확인해야 하는지 정리한다.
8 MiB 파일을 연 채로 이름만 지웠다

Linux의 임시 디렉터리에 X 바이트를 실제로 써서 8 MiB 파일을 만들었다. 파일 크기만 늘리는 희소 파일 방식은 쓰지 않았다. Python 버퍼를 거치지 않는 모드로 열고 fsync 후 관찰했다. 이 실험에서 fsync는 쓰기를 반영한 뒤 할당량을 읽기 위한 절차이며, 삭제 문제를 해결하는 명령으로 사용한 것이 아니다.
열려 있는 파일 디스크립터를 유지한 상태에서 unlink로 경로를 지웠다. 파일 디스크립터(FD)는 프로그램이 열린 파일을 가리킬 때 쓰는 번호다. 아래 표의 크기와 링크 수는 경로 조회가 아니라 os.fstat로 기존 FD에서 읽었다. 할당량은 Linux가 보고한 st_blocks에 512를 곱한 값이다. 경로 존재 여부는 그 시점에 같은 이름이 있는지를 별도로 확인했다.
| 시점 | 경로 존재 | 기존 파일 링크 수 | 기존 FD의 논리 크기(바이트) | 할당량(바이트) |
|---|---|---|---|---|
| 삭제 전 | 있음 | 1 | 8388608 | 8388608 |
| unlink 직후 | 없음 | 0 | 8388608 | 8388608 |
| 삭제 후 1 MiB 추가 | 없음 | 0 | 9437184 | 9437184 |
| 같은 이름 재생성·기존 FD에 8바이트 추가 | 있음 | 0 | 9437192 | 9441280 |
| 첫 FD 닫기·복제 FD 유지 | 있음 | 0 | 9437192 | 9441280 |
unlink 직후 경로는 없어졌고 링크 수는 0이 됐지만, 기존 FD에서 읽은 논리 크기와 할당량은 모두 8,388,608바이트였다. /proc/self/fd의 해당 링크에는 (deleted)가 붙었다. 그 상태에서 1 MiB를 더 쓰자 논리 크기와 할당량이 모두 9,437,184바이트로 늘었다. 삭제된 이름으로 다시 열지 않아도, 이미 열어 둔 파일에는 쓰기가 계속됐다.
같은 이름으로 새 파일을 만들어도 기존 FD는 남았다
다음으로 같은 sample.log 경로에 new-log와 줄바꿈을 담은 8바이트 파일을 만들었다. 새 경로의 inode와 기존 FD의 inode가 다른지 확인했다. inode는 파일시스템 안에서 파일을 식별하는 정보다. 이번에는 같은 디렉터리에 두 파일을 두었으므로 inode 번호가 다르다는 비교로 서로 다른 파일임을 확인할 수 있었다.
기존 FD에 old-log와 줄바꿈을 추가하자 기존 파일은 9,437,192바이트가 됐고, 새 경로를 읽은 결과는 여전히 new-log였다. 표에서 경로는 ‘있음’인데 기존 파일의 링크 수는 0인 이유가 이것이다. 빈 로그를 다시 만들어 놓는 것만으로 실행 중인 프로그램의 기록 대상이 새 파일로 바뀐다고 볼 수 없다.
파일 하나를 닫았다는 사실도 충분하지 않을 수 있다

os.dup으로 기존 FD를 복제한 다음 첫 FD를 닫았다. 복제 FD에서는 여전히 같은 크기와 할당량이 관찰됐고, 마지막에 쓴 old-log도 읽혔다. 마지막 복제 FD까지 닫자 그 번호에 대한 fstat는 EBADF 오류를 냈고 /proc/self/fd의 해당 항목도 없어졌다. 이 확인 사이에는 새 파일을 열지 않아 FD 번호 재사용이 끼어들지 않게 했다.
Linux unlink 문서는 마지막 이름이 삭제돼도 파일을 열고 있는 프로세스가 있으면 마지막 관련 FD가 닫힐 때까지 파일이 유지된다고 설명한다. 이번 실험은 mmap 같은 별도 참조 없이 파일 열기와 FD 복제만 사용했다. 실제 서비스는 여러 worker가 같은 로그를 열 수 있으므로 한 프로세스의 FD 하나를 닫은 사실만으로 정리가 끝났다고 판단해서는 안 된다.
서버에서는 파일시스템과 로그 소유 프로세스를 좁혀 본다
GNU 문서에서 df는 지정 경로가 속한 파일시스템의 사용량을 보고하고, du는 지정한 파일·디렉터리의 사용 공간을 계산한다. 삭제된 이름은 일반적인 디렉터리 순회에서 빠져도 열린 파일의 할당 공간은 남을 수 있다. 다만 df와 특정 로그 디렉터리의 du는 처음부터 집계 범위가 다르므로 두 숫자가 같아야 한다는 기준을 세우지는 않는다.
# /var/log/myapp을 문제가 생긴 실제 로그 디렉터리로 바꾼다.
df -h -- /var/log/myapp
df -i -- /var/log/myapp
# 로그를 쓰는 것으로 확인한 실제 프로세스 PID로 바꾼다.
service_pid=12345
ps -p "$service_pid" -o pid,comm
timeout 10s lsof -nP -a -p "$service_pid" +L1위 명령은 운영 적용 시 사용할 조회 예시이며 이번 원자료에 있는 실행 결과는 아니다. lsof가 설치돼 있어야 한다. +L1은 링크 수가 1보다 작은 열린 파일을 고르고, -a는 PID 조건과 링크 수 조건을 동시에 적용한다. 이 옵션을 빼면 선택 조건이 합쳐지는 방식이 달라질 수 있다. PID가 여러 개라면 실제 로그를 쓰는 worker까지 확인 범위에 넣어야 한다.
출력에서는 PID·FD·DEVICE·NODE·NAME을 함께 확인한다. 같은 파일이 여러 FD나 여러 프로세스에 보일 수 있으므로 행마다 SIZE/OFF를 더해 회수할 공간으로 계산하지 않는다. SIZE/OFF도 파일의 실제 할당량과 같다고 단정할 수 없다. 접근 권한 부족이나 명령 시간 초과로 결과가 비었을 때는 삭제된 열린 파일이 없다는 결론을 내리지 말고, 검사 범위와 오류 메시지부터 확인한다.
- 파일시스템 확인
df에서 부족한 볼륨과 조사 중인 경로가 같은지 본다.
- 소유 프로세스 확인
삭제된 열린 파일의 PID·FD·크기와 서비스 소유 관계를 좁힌다.
- 서비스 절차로 조치
그 서비스가 지원하는 로그 재열기 또는 회전 절차를 따른다.
- 공간과 새 기록 확인
이전 참조가 사라지고 새 로그에 기록되는지 함께 확인한다.
조치는 서비스를 확인한 뒤 로그 재열기 절차로 이어간다
| 확인한 상태 | 다음 확인 또는 조치 |
|---|---|
| 서비스가 삭제된 로그를 계속 열고 있음 | 그 서비스의 공식 로그 재열기·회전 절차와 권한 확인 |
| 새 경로는 생겼지만 기존 FD도 남음 | 새 파일에 기록이 도착하는지와 기존 FD 해제 여부를 각각 확인 |
| 바이트 여유는 있는데 df -i가 가득 참 | 작은 파일 증가 등 inode 고갈 원인을 별도로 조사 |
| 해당 PID에서 삭제된 파일이 안 보임 | 다른 worker·권한·마운트 범위를 확인하고 다른 공간 점유 원인으로 범위 확대 |
예를 들어 Nginx 공식 문서는 로그 회전 시 먼저 로그 이름을 바꾸고 master 프로세스에 USR1을 보내 로그를 다시 여는 절차를 설명한다. 이는 Nginx에 정해진 동작이며 모든 프로그램이 같은 신호를 처리한다는 뜻은 아니다. 이 글에서는 운영 Nginx에 신호를 보내거나 로그를 회전하지 않았고, 문서에 있는 재열기 절차와 임시 파일 실험을 구분했다.
운영 조치를 선택할 때는 해당 서비스가 지원하는 재열기 기능부터 확인한다. 재시작이 필요하다면 그 서비스의 요청 처리 영향을 검토하고 대상으로 확인된 서비스에 한정한다. 임의 PID 종료나 /proc의 FD 경로를 통한 파일 잘라내기를 일반적인 정리법으로 삼지 않는다. 조치 후에는 새 로그에 기록이 들어오는지, 이전 파일 참조가 해제됐는지, 같은 파일시스템의 여유 공간이 어떻게 바뀌는지 순서대로 살핀다.
이 결과로 설명하지 못하는 용량 차이도 있다
이 글의 실험은 한 Linux 프로세스와 작은 일반 파일에 한정된다. 파일시스템 스냅샷, 압축, 네트워크 파일시스템, 컨테이너의 마운트 차이, mmap으로 유지되는 참조는 시험하지 않았다. 삭제된 열린 로그가 없는데도 공간이 부족하다면 그 조건들을 별도로 확인해야 한다. 여기서 가져갈 판단은 하나다. 로그 경로를 지웠다는 사실 다음에는, 그 파일을 계속 사용하는 프로그램이 있는지를 확인한다.
편집자 의견
디스크 공간이 돌아오지 않을 때는 같은 이름의 파일을 다시 지우기보다 누가 이전 파일을 열고 있는지 먼저 확인하겠습니다. 실험에서 새 파일의 이름과 기존 열린 파일의 참조는 별도로 남았기 때문입니다.
실제 운영에서는 해당 로그를 보유한 프로세스와 정상적인 로그 재열기 방법을 확인한 뒤 조치하겠습니다. 공간 회수만을 목적으로 관련 없는 세션이나 프로세스를 종료하는 방식은 선택하지 않겠습니다.
판단 근거: 같은 이름으로 새 파일을 만들어도 기존 FD는 남았다
의견 추가 2026-10-23 · 본문 자료를 바탕으로 한 편집 판단
실험 환경과 원자료
관찰 시각: 2026-09-15T02:04:19.163553+00:00
os: Linux · kernel: 6.8.0-1063-aws · python: 3.10.12
임시 합성 로그 8 MiB에 unlink, 1 MiB 추가 쓰기, 같은 이름 재생성, 복제 FD 순차 닫기를 수행했다. 운영 로그와 다른 프로세스는 조작하지 않았다.
확인하지 않은 범위: 단일 Linux 프로세스의 일반 파일 실험. df의 여유 공간 회복량, 실제 서비스 로그 재열기, mmap, 스냅샷·압축·네트워크 파일시스템은 검증하지 않았다.
Python 3과 /proc가 있는 Linux에서 실행합니다. 약 10 MiB 여유 공간이 있는 임시 디렉터리를 사용하며 관리자 권한은 필요하지 않습니다. 임시 파일과 스크립트가 만든 자식 프로세스만 사용하며, 기존 결과 파일은 덮어쓰지 않습니다.
python3 lab-deleted-open-file.py --output new-observations.json참고한 공식 문서
- Linux unlink(2): 이름 삭제와 열린 파일의 수명
- Linux proc_pid_fd(5): 프로세스의 열린 FD
- Linux stat(3type): 파일 크기·링크 수·할당 블록
- lsof 매뉴얼: +L과 -a 선택 조건
- GNU df 매뉴얼
- GNU du 매뉴얼
- Nginx 공식 문서: Rotating Log-files
원고 수정: 2026-10-24 · 정정 요청
이 글의 보완 기록
- · 파일시스템·소유 프로세스·서비스 조치·사후 확인의 흐름을 추가했다.
- · 본문 근거에 대한 편집자의 판단과 결론이 달라지는 조건을 추가했습니다. 기존 관찰일·공식 자료 확인일은 유지했습니다.
- · 글별 적용 사례와 반례를 보강했습니다. 기존 관찰일과 원자료 확인일은 유지했습니다.