본문 바로가기
Operating System

logrotate로 로그 로테이션 관리하기

매큠·2026년 9월 16일·조회 2

서버를 오래 돌리다 보면 로그가 디스크를 다 먹는 상황을 한 번씩 만난다. 특히 애플리케이션이 직접 파일에 붙여 쓰는 catalina.out 같은 로그는 방치하면 수십 GB까지 커진다. 예전에 웹, WAS 점검하러 들어가면 /logs 파티션만 100% 차 있는 서버가 꼭 하나씩 있었다. logrotate 설정 몇 줄이면 끝나는 일인데, copytruncate와 create를 잘못 고르면 로테이션은 도는데 옛 파일에만 계속 기록되는 함정에 걸린다. 이 글은 그 지점을 정리한다.

결론부터 말하자면, logrotate의 기본 방식인 create는 원본을 .1로 이름만 바꾸고 같은 이름의 빈 파일을 새로 만든다. 이 방식은 애플리케이션이 파일을 다시 열어야 새 파일에 쓴다. 다시 열지 못하는 프로세스(대표적으로 catalina.out에 붙는 Tomcat)라면 copytruncate로 원본을 복사한 뒤 내용만 비워야 한다. 아래에서 두 방식의 동작과 Tomcat, Nginx, 일반 애플리케이션별 설정을 차례로 본다.

1. logrotate가 하는 일

logrotate는 로그 파일을 주기나 크기 기준으로 잘라 보관하고, 오래된 것을 압축하거나 지우는 도구다. 대부분의 배포판에 기본 설치돼 있고 cron 또는 systemd timer가 하루 한 번 실행한다. 실행 시각과 마지막 로테이션 기록은 상태 파일에 남는다.

$ rpm -q logrotate     # RHEL 계열
logrotate-3.20.1-3.el9.x86_64

$ cat /etc/cron.daily/logrotate
#!/bin/sh
/usr/sbin/logrotate /etc/logrotate.conf

$ ls /etc/logrotate.d/
chrony  nginx  sssd-common  syslog

전역 설정은 /etc/logrotate.conf에 있고, 개별 서비스 설정은 /etc/logrotate.d/ 아래에 파일 하나씩 둔다. 상태 파일은 /var/lib/logrotate/logrotate.status(배포판에 따라 /var/lib/logrotate.status)에 기록되며, 여기에 각 로그의 마지막 로테이션 날짜가 저장된다.

2. 핵심 지시어

설정 파일에 쓰는 지시어는 많지만 운영에서 쓰는 건 몇 개로 좁혀진다. 하나씩 정의하고 넘어간다.

주기와 크기: daily, weekly, size, maxsize

daily, weekly, monthly는 시간 기준 로테이션이다. size 100M은 파일이 그 크기를 넘으면 자른다. size는 시간 지시어와 함께 쓸 수 없고, 크기만 본다.

maxsize 100M는 다르다. 주기가 되지 않아도 크기를 넘으면 자르고, 크기에 못 미치면 주기에 맞춰 자른다. "하루 한 번, 단 그 전에 100M를 넘으면 바로" 같은 요구에는 dailymaxsize를 같이 쓴다. 반대로 minsize는 주기가 됐어도 최소 크기에 못 미치면 넘긴다.

보관 개수: rotate

rotate 7은 로테이션된 파일을 7개까지 보관하고 그다음부터 오래된 것을 지운다. 기본값은 0이라 이 값을 안 주면 로테이션하면서 바로 지워버린다. 보관이 목적이라면 rotate를 반드시 명시한다.

압축: compress, delaycompress

compress는 로테이션된 파일을 gzip으로 압축한다. delaycompress는 압축을 한 주기 미룬다. 즉 .1은 압축하지 않고 두었다가 다음 로테이션에서 .2.gz로 만든다. 프로세스가 로테이션 직후에도 .1에 잠깐 더 쓸 수 있는 경우, 압축을 미뤄 내용 손상을 피한다.

안전장치: missingok, notifempty, dateext

missingok은 로그 파일이 없어도 에러 없이 넘어간다. notifempty는 파일이 비어 있으면 로테이션하지 않는다. dateext.1 대신 -20260916 같은 날짜를 붙여, 어느 날짜 로그인지 이름만 봐도 알게 한다.

스크립트: postrotate, sharedscripts

postrotate ... endscript 사이에 로테이션 후 실행할 명령을 넣는다. Nginx에 USR1 신호를 보내 로그 파일을 다시 열게 하는 것이 대표 용도다. 와일드카드로 여러 파일을 잡을 때 sharedscripts를 넣으면 스크립트를 파일마다가 아니라 한 번만 실행한다.

3. copytruncate와 create의 차이

이 둘이 로테이션의 핵심이고, 함정도 여기서 나온다. 리눅스에서 프로세스는 파일 이름이 아니라 열어둔 파일 디스크립터로 쓴다. 로그 파일 이름을 바꿔도, 프로세스가 그 디스크립터를 쥐고 있으면 이름 바뀐 옛 파일에 계속 쓴다.

create (기본 방식)

원본 app.logapp.log.1로 이름을 바꾸고, app.log라는 빈 파일을 새로 만든다. 문제는 프로세스가 아직 app.log.1이 된 옛 디스크립터를 쥐고 있다는 점이다. 프로세스가 파일을 닫고 다시 열어야(로그 프레임워크의 재오픈, 또는 USR1 같은 신호) 새 app.log에 쓰기 시작한다.

# 로테이션은 됐는데 새 파일 크기가 0에서 안 늘어나는 증상
$ ls -l /app/logs/
-rw-r--r-- 1 tomcat tomcat        0 Sep 16 03:20 app.log
-rw-r--r-- 1 tomcat tomcat 524288000 Sep 16 03:20 app.log.1

# 프로세스가 지운/이름 바뀐 파일을 계속 붙들고 있는지 확인
$ ls -l /proc/$(pgrep -f app.jar)/fd | grep app.log
l-wx------ 1 root root 64 Sep 16 03:25 267 -> /app/logs/app.log.1

app.log는 0바이트인데 실제 로그는 app.log.1로 계속 들어간다. 이게 "로테이션 후에도 옛 파일에 계속 기록되는" 함정이다. 애플리케이션이 파일을 다시 열 방법이 있으면 create가 정답이다. Log4j2, Logback 같은 프레임워크는 자체 롤링을 쓰거나 재오픈을 지원하므로 create와 잘 맞는다.

copytruncate

원본을 app.log.1로 복사한 뒤, 원본 app.log를 그 자리에서 내용만 0으로 비운다. 디스크립터와 inode가 그대로라 프로세스는 하던 대로 app.log에 계속 쓴다. 파일을 다시 열지 못하는 프로세스에 쓴다.

대가가 있다. 복사와 절단(truncate) 사이 아주 짧은 순간에 쓰인 로그는 유실될 수 있다. 또 복사본을 만드는 동안 순간적으로 디스크를 원본 크기만큼 더 쓴다. 그래서 copytruncate는 "재오픈이 안 되는 레거시"에 한정하는 것이 맞다.

4. Tomcat catalina.out 설정

Tomcat의 catalina.out은 시작 스크립트가 표준출력을 리다이렉트해 붙이는 파일이라, Tomcat 자체는 이 파일을 다시 열지 못한다. create로 돌리면 위의 함정에 그대로 걸린다. copytruncate를 쓴다.

$ sudo vi /etc/logrotate.d/tomcat
/opt/tomcat/logs/catalina.out {
    copytruncate
    daily
    maxsize 200M
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    dateext
    su tomcat tomcat
}

참고로 catalina.outserver.xml의 접근 로그나 logging.properties의 애플리케이션 로그와 다르다. 후자는 Tomcat이 자체적으로 날짜별 파일을 만들므로 logrotate로 또 자르지 않는다. logrotate 대상은 자체 롤링이 없는 catalina.out으로 좁힌다.

5. Nginx 설정

Nginx는 USR1 신호를 받으면 로그 파일을 다시 연다. 그래서 create로 로테이션하고 postrotate에서 신호를 보내는 것이 정석이다. 배포판 패키지가 /etc/logrotate.d/nginx를 이미 깔아두는데, 형태는 이렇다.

/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 nginx adm
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

sharedscripts가 있어 access.logerror.log를 둘 다 자른 뒤 USR1을 딱 한 번 보낸다. delaycompress가 있는 이유는, 신호를 보내도 워커가 옛 디스크립터를 닫는 데 짧은 시간이 걸려 .1에 잠깐 더 쓸 수 있기 때문이다. 압축을 한 주기 미뤄 그 사이 손상을 막는다.

6. 적용 확인과 디버깅

설정을 바꾼 뒤에는 -d(디버그, 실제로 자르지 않고 계획만 출력)로 먼저 확인한다. 실제 강제 실행은 -f다.

# 실제 변경 없이 무엇을 할지만 출력
$ sudo logrotate -d /etc/logrotate.d/tomcat
reading config file /etc/logrotate.d/tomcat
Rotating pattern: /opt/tomcat/logs/catalina.out  after 1 days (14 rotations)
empty log files are not rotated, old logs are removed
considering log /opt/tomcat/logs/catalina.out
  Now: 2026-09-16 03:30
  Log does not need rotating (log has been already rotated)

# 주기를 무시하고 강제 로테이션
$ sudo logrotate -f /etc/logrotate.d/tomcat

$ ls -l /opt/tomcat/logs/
-rw-r--r-- 1 tomcat tomcat      0 Sep 16 03:31 catalina.out
-rw-r--r-- 1 tomcat tomcat 198M   Sep 16 03:31 catalina.out-20260916

여기서 한 번 걸리는 지점이 있다. su 지시어 없이 root가 아닌 디렉터리를 자르려 하면 최신 logrotate는 권한 경고를 내고 넘어간다. 로그 디렉터리 소유자에 맞춰 su tomcat tomcat처럼 명시하면 풀린다.

error: skipping "/opt/tomcat/logs/catalina.out" because parent directory has
insecure permissions ... Set "su" directive in config file to tell logrotate
which user/group should be used for rotation.

또 하나, 로테이션이 아예 안 도는 것처럼 보이면 상태 파일의 마지막 날짜를 본다. 같은 날 이미 돌았다고 기록돼 있으면 -f 없이는 다시 돌지 않는다.

$ sudo grep catalina /var/lib/logrotate/logrotate.status
"/opt/tomcat/logs/catalina.out" 2026-9-16-3:0:0

7. 정리

파일을 다시 열 수 있는 프로세스(Nginx, 재오픈을 지원하는 로그 프레임워크)는 createpostrotate 재오픈을 붙인다. 다시 열지 못하는 catalina.out 부류는 copytruncate로 간다. 보관은 rotate로 개수를, compressdelaycompress로 용량과 안전을 잡고, 크기 상한이 필요하면 dailymaxsize를 더한다. 설정 후 logrotate -d로 계획을 확인하고, 로테이션이 돌았는데 새 파일이 0바이트에서 멈춰 있으면 /proc/PID/fd로 프로세스가 옛 파일을 붙들고 있는지부터 본다. 버전 번호와 출력은 저장소, 배포판 상태에 따라 다를 수 있다.

자주 묻는 질문

copytruncate와 create 중 무엇을 써야 하나

로그 파일을 스스로 다시 열 수 있는 프로세스면 기본값인 create를 쓰고, postrotate에서 재오픈 신호를 보낸다. Nginx가 여기 해당한다. 파일을 다시 열지 못하는 프로세스, 예를 들어 표준출력을 리다이렉트해 쓰는 Tomcat catalina.out은 copytruncate를 쓴다. copytruncate는 복사와 절단 사이 짧은 순간의 로그가 유실될 수 있으므로 재오픈이 불가능한 레거시에만 한정한다.

로테이션은 됐는데 새 로그 파일이 0바이트에서 안 늘어난다

create 방식에서 프로세스가 이름 바뀐 옛 파일의 디스크립터를 계속 쥐고 있어 새 파일 대신 옛 파일에 쓰는 상황이다. ls -l /proc/PID/fd로 확인하면 프로세스가 app.log.1을 가리키고 있다. 프로세스가 재오픈을 지원하면 postrotate에서 재오픈 신호를 보내고, 지원하지 못하면 copytruncate로 바꾼다.

rotate 값을 안 줬더니 오래된 로그가 남지 않는다

rotate의 기본값은 0이라 이 값을 명시하지 않으면 로테이션하면서 곧바로 지운다. 보관이 목적이면 rotate 7이나 rotate 14처럼 보관할 개수를 반드시 지정한다. -1을 주면 무기한 보관한다.

size와 maxsize는 어떻게 다른가

size는 시간 지시어와 함께 쓸 수 없고 오직 크기만 보고 자른다. maxsize는 시간 지시어와 함께 쓸 수 있어, 주기가 되지 않아도 크기를 넘으면 자르고 크기에 못 미치면 주기에 맞춰 자른다. 하루 한 번을 기본으로 하되 그 전에 특정 크기를 넘으면 바로 자르려면 daily와 maxsize를 같이 쓴다.

설정을 실제로 자르기 전에 확인하는 방법은

logrotate -d 설정파일 로 디버그 모드를 실행하면 실제로 파일을 자르지 않고 무엇을 할지 계획만 출력한다. 강제로 즉시 로테이션하려면 logrotate -f 설정파일 을 쓴다. 같은 날 이미 돌았다면 상태 파일 기록 때문에 -f 없이는 다시 돌지 않으므로, /var/lib/logrotate/logrotate.status의 마지막 날짜를 함께 확인한다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.