- Published on
logrotate 했는데 디스크 100%? 열린 삭제파일 찾기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 logrotate 를 돌렸는데도 df 는 여전히 100%에 가깝고, 서비스는 느려지고, 새 로그도 못 쓰는 상황이 종종 발생합니다. 이때 많은 경우 “파일은 지웠는데 공간이 안 돌아오는” 리눅스 파일 시스템 동작 방식 때문에 문제가 지속됩니다.
핵심은 간단합니다.
- 파일을 삭제해도(디렉터리 엔트리 제거) 해당 파일을 열고 있는 프로세스가 있으면 inode가 남아 디스크 블록이 유지됩니다.
- 이 상태는
du로는 잘 안 보이고,lsof나/proc로 찾아야 합니다.
이 글에서는 열린 삭제파일을 찾는 실전 커맨드, 즉시 회수하는 방법, 그리고 logrotate 설정에서 재발을 막는 포인트를 정리합니다.
증상: du 는 작은데 df 는 꽉 찼다
가장 흔한 시그널은 아래 조합입니다.
df -h는/var또는/가 90~100%du -sh /var/log/*는 생각보다 작음journalctl이나 애플리케이션이No space left on device를 뱉음
리눅스에서 “삭제”는 파일 내용이 즉시 사라지는 게 아니라, 해당 파일을 가리키는 이름(디렉터리 엔트리)만 제거하는 의미에 가깝습니다. 열려 있는 파일 디스크립터가 남아 있으면 커널은 블록을 해제하지 않습니다.
원인: logrotate가 지운 파일을 프로세스가 계속 쓰는 경우
logrotate 는 보통 다음 중 하나로 로그를 회전합니다.
- rename 방식:
app.log를app.log.1로 이름 변경 후 새 파일 생성 - copytruncate 방식: 내용을 복사해 백업 파일 만들고, 원본 파일을 truncate
rename 방식에서는 “기존 파일 핸들”을 잡고 있던 프로세스가 계속 그 파일에 쓰게 됩니다. 그런데 그 파일이 압축/삭제까지 되면, 프로세스 입장에서는 여전히 열린 파일에 쓰고 있지만 파일 경로는 사라져 “deleted” 상태가 됩니다.
즉,
- 파일 시스템 관점: 경로는 삭제됨
- 커널 관점: 열린 inode는 살아 있음
- 디스크 관점: 블록이 계속 점유됨
1분 진단: 열린 삭제파일 찾기 (lsof)
가장 빠른 방법은 lsof 로 (deleted) 를 찾는 것입니다.
sudo lsof +L1 | head
+L1은 링크 카운트가 1보다 작은(대개 0, 즉 삭제된) 파일을 의미합니다.
좀 더 “로그 파일”에 집중하려면:
sudo lsof +L1 | grep -E "\(deleted\)|/var/log" | head -n 50
특정 마운트(예: /var)에서만 보고 싶다면:
sudo lsof +L1 /var
출력에서 중요한 컬럼은 다음입니다.
COMMAND/PID: 어떤 프로세스가 잡고 있는지FD: 어떤 파일 디스크립터인지(예:1w,2w,3u)SIZE/OFF: 얼마나 커졌는지NAME: 경로 끝에(deleted)표시
디스크를 실제로 먹는 놈만 추리기
SIZE/OFF 기준으로 큰 것만 보고 싶을 때는 정렬을 한 번 거칩니다.
sudo lsof +L1 | awk '{print $7, $2, $1, $9}' | sort -n | tail -n 20
환경에 따라 컬럼 위치가 달라질 수 있으니, 먼저 lsof +L1 원본을 보고 조정하세요.
lsof 가 없다면: /proc 로 직접 찾기
최소 설치 이미지나 컨테이너 환경에서는 lsof 가 없을 수 있습니다. 이때는 /proc 의 파일 디스크립터 심볼릭 링크를 훑으면 됩니다.
for pid in /proc/[0-9]*; do
ls -l "$pid/fd" 2>/dev/null | grep "(deleted)" && echo "PID=${pid##*/}"
done
프로세스 이름까지 같이 보고 싶다면:
for pid in /proc/[0-9]*; do
cmd=$(tr -d '\0' < "$pid/cmdline" 2>/dev/null | cut -c1-120)
if ls -l "$pid/fd" 2>/dev/null | grep -q "(deleted)"; then
echo "PID=${pid##*/} CMD=$cmd"
ls -l "$pid/fd" 2>/dev/null | grep "(deleted)" | head
fi
done
즉시 해결: 공간을 회수하는 3가지 방법
찾았으면 이제 “열린 파일 핸들”을 정리해야 디스크 블록이 반환됩니다.
1) 가장 안전: 프로세스 재시작(또는 로그 재오픈)
정석은 해당 프로세스가 로그 파일을 다시 열도록 만드는 것입니다.
- systemd 서비스라면:
sudo systemctl restart your-service
- nginx 같이 재오픈 시그널을 지원하면(재시작보다 안전):
sudo kill -HUP $(cat /run/nginx.pid)
애플리케이션/데몬마다 “로그 재오픈” 시그널이 다릅니다. 웹 서버나 일부 데몬은 HUP 로 재오픈을 지원합니다.
2) 긴급 처치: 열린 FD에 truncate 를 걸어 0으로 만들기
재시작이 당장 어렵고 “디스크부터 살려야” 한다면, 열린 파일 디스크립터가 가리키는 실제 파일에 대해 크기를 0으로 만들 수 있습니다.
예를 들어 PID가 1234 이고, lsof 에서 FD가 5w 라면 /proc/1234/fd/5 가 대상입니다.
sudo truncate -s 0 /proc/1234/fd/5
또는 셸 리다이렉션으로도 가능합니다.
sudo sh -c 'echo -n > /proc/1234/fd/5'
주의점:
- 이 방법은 “로그 내용이 사라집니다”. 디스크 회수 목적의 응급처치로만 쓰세요.
- 잘못된 FD를 건드리면 예상치 못한 부작용이 있을 수 있으니, 반드시
lsof -p 1234로 재확인 후 진행하세요.
3) 프로세스 종료(최후의 수단)
서비스 영향이 있더라도 디스크가 꽉 차서 더 큰 장애가 나기 직전이라면 프로세스를 종료해 inode를 해제할 수도 있습니다.
sudo kill 1234
정상 종료가 안 되면:
sudo kill -9 1234
다만 kill -9 는 정리 루틴을 건너뛰므로 가능한 피하세요.
logrotate 설정에서 재발 방지 포인트
문제를 해결했으면, 다음 회전에서 같은 일이 반복되지 않게 해야 합니다.
1) postrotate 에서 재오픈 신호/재시작을 보장
대표적으로 nginx는 postrotate 에서 HUP 을 보내는 패턴을 씁니다.
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
[ -s /run/nginx.pid ] && kill -HUP $(cat /run/nginx.pid)
endscript
}
sharedscripts는 여러 파일이 매칭돼도postrotate를 한 번만 실행합니다.delaycompress는 방금 회전한 파일을 즉시 압축하지 않고 다음 회전에서 압축합니다. 일부 프로세스/툴링과 충돌을 줄이는 데 도움이 됩니다.
2) copytruncate 는 만능이 아니다
copytruncate 는 프로세스 재시작 없이도 “같은 파일 경로”를 유지하게 만들어 편해 보이지만, 단점이 있습니다.
- 복사와 truncate 사이에 로그가 유실될 수 있음
- 큰 파일이면 I/O 부담이 큼
가능하면 애플리케이션이 로그 재오픈을 지원하도록 구성하고, postrotate 로 처리하는 게 더 안정적입니다.
3) systemd/journald 사용 시 별도 확인
애플리케이션이 파일 로그가 아니라 journald로만 남긴다면 logrotate 와 무관할 수 있습니다. 디스크가 journald로 찬 경우는 다음을 확인하세요.
journalctl --disk-usage
용량 제한은 예를 들어 이렇게 설정합니다.
sudo mkdir -p /etc/systemd/journald.conf.d
cat <<'EOF' | sudo tee /etc/systemd/journald.conf.d/size.conf
[Journal]
SystemMaxUse=1G
SystemKeepFree=500M
EOF
sudo systemctl restart systemd-journald
실전 트러블슈팅 플로우(체크리스트)
운영 중에는 “원인 찾기” 자체가 시간을 잡아먹습니다. 아래 순서로 보면 빠르게 수렴합니다.
df -h로 꽉 찬 마운트 확인du -sh /var/log/*로 대략적인 로그 사용량 확인sudo lsof +L1로(deleted)파일 확인- 큰
SIZE/OFF를 잡고 있는 PID를 식별 - 가능하면 “재오픈 신호” 또는 서비스 재시작으로 해결
- 긴급하면
/proc/PID/fd/FD에truncate로 공간 회수 logrotatepostrotate와 애플리케이션 로깅 정책 개선
컨테이너 환경에서도 동일한 문제가 발생할 수 있는데, 특히 노드 디스크가 꽉 차면 파드가 연쇄적으로 죽거나 스케줄링이 실패합니다. 장애 상황에서 원인을 빠르게 좁히는 접근은 다른 운영 이슈에서도 동일하게 중요합니다. 예를 들어 쿠버네티스에서 재시작 루프를 빠르게 진단하는 방법은 K8s CrashLoopBackOff 원인 10분내 찾는 법 도 함께 참고하면 좋습니다.
자주 하는 실수와 주의사항
rm 했는데 왜 용량이 안 줄죠?
rm 은 경로를 지우는 것이고, 열린 파일 디스크립터가 남아 있으면 공간은 유지됩니다. “열린 삭제파일”이 대표 케이스입니다.
du 로는 왜 안 보이나요?
du 는 디렉터리 트리를 따라가며 “경로가 존재하는 파일”만 합산합니다. 경로가 사라진 (deleted) 파일은 합산 대상에서 빠집니다.
이미 압축된 로그도 같은 문제를 만들 수 있나요?
압축 파일 자체가 아니라, 압축/삭제되기 전 원본 로그를 프로세스가 계속 쓰는 상황이 문제입니다. 회전 직후에도 프로세스가 새 파일로 전환하지 않으면 계속 누적됩니다.
마무리
logrotate 이후에도 디스크가 100%로 남는 문제는 대부분 “삭제된 파일을 프로세스가 계속 열고 있는” 전형적인 리눅스 동작에서 시작합니다. lsof +L1 또는 /proc 로 열린 삭제파일을 찾아 PID를 특정하고, 재시작/재오픈 또는 긴급 truncate 로 공간을 회수하면 빠르게 정상화할 수 있습니다.
다음 회전에서 재발하지 않도록 logrotate 의 postrotate 에 재오픈 신호를 넣고, 애플리케이션 로깅 전략(파일 로그 vs journald)까지 함께 점검하는 것을 권장합니다.