Published on

logrotate 후 디스크 100% - 삭제 로그 공간 회수

Authors

서버에서 logrotate를 돌렸고 분명 오래된 로그 파일도 지웠는데, df -h는 여전히 100%를 가리키는 상황이 종종 발생합니다. 이때 많은 분들이 "삭제가 안 됐나?"를 먼저 의심하지만, 리눅스/유닉스 파일 시스템의 동작 방식 때문에 파일은 삭제됐어도 디스크 공간이 즉시 회수되지 않을 수 있습니다.

핵심은 간단합니다.

  • 프로세스가 로그 파일을 열어둔 상태로 계속 쓰고 있으면
  • 파일을 rm로 삭제해도 inode는 남아 있고(디렉터리 엔트리만 제거)
  • 마지막 파일 디스크립터가 닫힐 때까지 블록이 해제되지 않습니다.

이 글에서는 원인 진단부터 안전한 공간 회수, 재발 방지용 logrotate 설정까지 실무 관점에서 정리합니다.

증상: rm 했는데도 df가 줄지 않는다

대표적인 증상은 아래 조합입니다.

  • df -h 는 100%에 가깝다
  • du -sh /var/log 같은 합계는 생각보다 작다(또는 삭제했는데도 비슷하다)
  • logrotate는 정상 수행 로그를 남긴다

이때 가장 먼저 확인할 것은 삭제된 파일을 아직 잡고 있는 프로세스입니다.

원리: “삭제된 파일”과 열린 FD

리눅스에서 파일은 대략 두 가지로 연결됩니다.

  • 디렉터리 엔트리(경로 이름)
  • inode(실제 데이터와 메타데이터)

rm은 경로 이름을 지우는 것이고, 프로세스가 파일을 열면 해당 inode에 대한 참조(파일 디스크립터)가 생깁니다. 따라서 경로가 없어져도 FD가 살아 있으면 데이터 블록은 유지됩니다.

이 상황에서 du는 경로 기반으로 크기를 합산하므로 “안 보이는” 공간을 놓치고, df는 실제 파일 시스템 사용량을 보여주므로 100%가 유지됩니다.

1단계: 삭제됐지만 열린 파일 찾기

가장 확실한 방법은 lsof입니다.

sudo lsof +L1
  • +L1은 링크 수가 1 미만(즉, 디렉터리 엔트리가 제거된) 파일을 보여줍니다.
  • 출력에서 deleted 표시가 나오면 거의 정답입니다.

로그 관련 프로세스만 좁혀서 보고 싶다면:

sudo lsof +L1 | grep -E '/var/log|deleted'

특정 파일 시스템에서만 보고 싶다면 마운트 포인트 기준으로:

sudo lsof +L1 /var

systemd 환경에서 빠르게 매핑하기

lsof에서 PID를 얻었으면 어떤 서비스인지 확인합니다.

ps -p 1234 -o pid,comm,args
sudo systemctl status --pid 1234

서비스가 비정상적으로 재시작을 반복하는 상황이면 로그가 폭증하면서 디스크를 빠르게 채울 수 있습니다. 이 경우 서비스 자체 안정화도 병행해야 합니다. 관련해서는 systemd 서비스 무한 재시작 - Exit code 203 해결도 함께 참고하면 좋습니다.

2단계: 공간 회수 방법 3가지(안전성 순)

삭제된 로그 공간을 회수하는 방법은 크게 3가지가 있습니다. 운영 환경에서는 리스크를 최소화하는 순서로 선택하는 것이 중요합니다.

방법 A: 프로세스/서비스 재시작(가장 권장)

가장 정상적인 해결은 해당 프로세스가 열린 FD를 닫게 만드는 것입니다.

sudo systemctl restart nginx
# 또는
sudo systemctl restart rsyslog

재시작 후 df -h를 다시 확인하면 공간이 즉시 회수되는 경우가 많습니다.

  • 장점: 파일/로그 일관성이 좋고, 표준적인 운영 방식
  • 단점: 재시작에 따른 짧은 영향(무중단 구성이라면 영향 최소)

방법 B: 열린 FD에 대해 truncate(재시작이 어렵다면)

재시작이 곤란한 프로세스(예: 장시간 작업, 영향 큰 데몬)라면, 열려 있는 파일 디스크립터를 직접 0바이트로 줄여 공간을 회수할 수 있습니다.

  1. lsof로 FD 경로를 잡습니다. 보통 아래처럼 보입니다.
  • /var/log/app.log (deleted)
  1. /proc의 FD로 접근해 truncate 합니다.
# PID=1234, FD=5 예시
sudo truncate -s 0 /proc/1234/fd/5
  • 장점: 프로세스 재시작 없이 즉시 공간 회수 가능
  • 단점: 운영자가 잘못된 FD를 건드리면 예상치 못한 영향 가능

truncate가 없다면 리다이렉션도 가능하지만, 반드시 백틱으로 감싸 인라인 코드처럼 다루는 것이 안전합니다.

# 동일 효과(주의해서 사용)
: > /proc/1234/fd/5

방법 C: 프로세스 종료(최후 수단)

서비스 재시작이 불가능하고, truncate도 위험하거나 실패한다면 종료가 최후 수단입니다.

sudo kill -TERM 1234
# 안 되면
sudo kill -KILL 1234
  • 장점: 확실하게 FD가 닫히며 공간 회수
  • 단점: 서비스 중단/데이터 손실 위험

3단계: logrotate 설정 점검 포인트

공간 회수는 “응급처치”이고, 재발 방지는 logrotate 정책이 좌우합니다.

핵심: rotate 후 로그 작성 프로세스가 새 파일을 열게 해야 함

대부분의 데몬은 로그 파일을 열어둔 채로 쓰기 때문에, rotate 시점에 아래 중 하나가 필요합니다.

  • 서비스에 로그 재오픈 신호(HUP)를 보내기
  • 서비스 재시작
  • copytruncate 사용(부작용 이해 필요)

예시 1) postrotate에서 HUP 보내기(권장)

예: nginx

/var/log/nginx/*.log {
  daily
  rotate 14
  compress
  delaycompress
  missingok
  notifempty
  sharedscripts
  postrotate
    # nginx가 로그 파일을 다시 열도록 유도
    /bin/kill -HUP `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
  endscript
}
  • sharedscripts: 여러 파일을 rotate 해도 postrotate를 한 번만 실행
  • delaycompress: 방금 rotate한 파일은 다음 주기에 압축(프로세스/도구 호환성 개선)

예시 2) copytruncate 사용(신중히)

/var/log/myapp.log {
  size 200M
  rotate 10
  compress
  missingok
  notifempty
  copytruncate
}

copytruncate는 원본 파일을 복사한 뒤 원본을 truncate 하므로, 프로세스가 파일을 재오픈하지 않아도 됩니다.

  • 장점: 재시작/HUP 없이도 동작
  • 단점: 복사와 truncate 사이에 로그 유실/중복 가능, 대용량이면 I/O 부담

즉, 트래픽이 높거나 로그 정합성이 중요한 시스템에서는 postrotate로 재오픈 유도 방식이 더 안전한 편입니다.

4단계: dfdu 불일치 빠르게 확인하는 루틴

장애 대응 시 아래 순서가 빠릅니다.

  1. 파일 시스템 사용량 확인
df -h
  1. 디렉터리 기준 사용량 확인
sudo du -xhd1 /var | sort -h
  1. 불일치가 크면 열린 deleted 파일 확인
sudo lsof +L1
  1. PID 확인 후 재시작 또는 truncate
sudo systemctl restart <service>
# 또는
sudo truncate -s 0 /proc/<pid>/fd/<fd>

이 루틴은 쿠버네티스 노드에서도 유효합니다. 노드 디스크가 꽉 차면 DiskPressure로 파드가 축출되거나 스케줄링이 꼬일 수 있으니, 관련 대응은 EKS DiskPressure로 Pod Evicted 폭주 해결 10가지도 함께 참고할 만합니다.

5단계: 자주 놓치는 원인들

journald가 디스크를 잡아먹는 경우

/var/log/journal이 커지는 케이스도 흔합니다.

journalctl --disk-usage
sudo du -sh /var/log/journal

필요 시 보존 정책을 조정합니다.

sudo journalctl --vacuum-time=7d
sudo journalctl --vacuum-size=500M

지속 설정은 /etc/systemd/journald.conf에서 SystemMaxUse 등을 조정합니다.

컨테이너 로그(예: Docker json-file)

도커 기본 로깅 드라이버가 json-file이면 /var/lib/docker/containers 아래가 커질 수 있습니다.

sudo du -sh /var/lib/docker/containers

이 경우 로깅 드라이버 변경 또는 로그 옵션(max-size, max-file) 설정이 필요합니다.

애플리케이션이 자체적으로 파일 핸들을 고정하는 경우

특정 런타임/로거는 HUP에 반응하지 않거나, 파일 재오픈을 지원하지 않을 수 있습니다. 이때는 애플리케이션 로깅 설정(예: logback의 rolling policy, Python logging RotatingFileHandler 등)을 애초에 “파일 롤링을 애플리케이션이 직접” 하도록 바꾸는 것이 더 안정적일 수 있습니다.

운영 팁: 장애 중에는 “즉시 회수”와 “원인 차단”을 분리

  • 즉시 회수: lsof +L1로 PID 및 FD 확인 후 재시작 또는 truncate
  • 원인 차단: logrotatepostrotate/copytruncate 재검토, 로그 폭증 원인(에러 루프, 디버그 로그) 제거, journald/컨테이너 로그 정책 정리

디스크가 100%에 도달하면 로그뿐 아니라 DB, 패키지 업데이트, 소켓 생성 등 여러 기능이 연쇄적으로 실패할 수 있습니다. 특히 부팅/파일시스템 레벨 이슈로 번질 경우에는 복구 난이도가 급상승하므로, 징후가 보이면 빠르게 대응하는 것이 중요합니다. 파일 시스템 관련 장애 복구 관점에서는 Azure VM 부팅 실패(XFS 로그) 즉시복구 가이드도 참고가 됩니다.

체크리스트 요약

  • df는 높은데 du는 낮다: 삭제됐지만 열린 파일 의심
  • sudo lsof +L1deleted 파일과 PID 확인
  • 1순위: systemctl restart 또는 HUP로 로그 재오픈 유도
  • 재시작이 어렵다면: /proc/<pid>/fd/<fd>truncate로 즉시 회수
  • 재발 방지: logrotatepostrotate 또는 신중한 copytruncate, journald/컨테이너 로그 정책 정리

위 절차대로 하면 “logrotate 했는데 디스크가 안 줄어드는” 전형적인 케이스는 대부분 빠르게 해결됩니다.