Published on

리눅스 logrotate가 안 돎? 권한·SELinux 점검

Authors

서버에서 디스크가 갑자기 꽉 차거나, /var/log 아래 로그가 며칠째 그대로라면 대부분 logrotate가 “안 돈다”기보다 돌았는데 실패했거나, 아예 트리거(타이머/cron)가 실행되지 않았거나, 권한/SELinux로 파일 조작이 막힌 경우가 많습니다. 특히 운영 환경에서는 서비스가 root가 아닌 계정으로 뜨는 경우가 많아, create, su, copytruncate, postrotate 스크립트까지 얽히면 원인 파악이 까다로워집니다.

이 글은 “왜 logrotate가 회전하지 않는가?”를 재현 → 로그 확인 → 트리거 확인 → 권한/SELinux 확인 → 설정 교정 순서로 정리한 실전 점검 가이드입니다.

> 로그가 쌓이면 비용도 같이 쌓입니다. 클라우드에서 로그 적재/보관 비용이 폭증했다면 원인과 절감법은 CloudWatch Logs 비용 폭증 원인과 절감 10가지도 함께 참고하세요.

1) 먼저 “정말로 logrotate가 실행됐는지” 확인

systemd timer 기반인지(대부분의 최신 배포판)

Ubuntu/Debian, RHEL/CentOS/Rocky 등은 보통 logrotate.timer가 매일 실행됩니다.

systemctl status logrotate.timer
systemctl list-timers --all | grep -i logrotate

# 마지막 실행 결과(서비스 유닛)
systemctl status logrotate.service
journalctl -u logrotate.service --since "7 days ago" --no-pager
  • timer가 비활성/마스킹되어 있으면 회전이 안 됩니다.
  • logrotate.service가 실패(Exit code)했다면, journal에 원인이 남습니다.

cron 기반인지(구형/일부 환경)

ls -l /etc/cron.daily/logrotate

# cron 실행 흔적(배포판마다 경로가 다름)
grep -R "CRON" /var/log/syslog /var/log/cron 2>/dev/null | tail -n 50

cron이 죽어 있거나(특히 최소 설치 이미지), /etc/cron.daily가 실행되지 않는 환경(컨테이너 등)에서는 logrotate가 “설치만 되어 있고 실행이 안 되는” 상태가 됩니다.

2) logrotate 자체를 “수동 디버그 모드”로 돌려보기

실패 원인은 대개 표준 출력/에러에 바로 드러납니다.

# 설정 문법/대상 파일 검사
logrotate -d /etc/logrotate.conf

# 실제로 실행(강제)
logrotate -vf /etc/logrotate.conf
  • -d(debug)는 실제 변경 없이 시뮬레이션합니다.
  • -v는 verbose, -f는 force(조건 무시하고 회전 시도)입니다.

자주 나오는 메시지:

  • error: skipping ... because parent directory has insecure permissions
  • error: ... Permission denied
  • SELinux is preventing ...(audit에 남는 경우가 더 흔함)
  • state file ... is not an absolute path / bad rotation count 등 설정 오류

3) 상태 파일(logrotate.status) 때문에 “안 도는 것처럼” 보이는 경우

logrotate는 마지막 회전 시점을 상태 파일에 기록합니다.

# 일반적으로 이 파일을 씁니다
ls -l /var/lib/logrotate/logrotate.status 2>/dev/null || true
ls -l /var/lib/logrotate/status 2>/dev/null || true

# 상태 내용 확인
sudo cat /var/lib/logrotate/logrotate.status 2>/dev/null | tail -n 50

다음 상황이면 회전이 안 된 것처럼 보일 수 있습니다.

  • daily인데 마지막 회전이 “오늘”로 기록되어 있음
  • size 조건이 아직 충족되지 않음
  • dateext로 파일명이 바뀌어 있는데 기대한 패턴과 다름

강제로 한 번 회전해보려면:

sudo logrotate -vf /etc/logrotate.conf

운영 중에 상태 파일을 삭제하는 방법도 있지만, 예상치 못한 “대량 회전”이 발생할 수 있어 권장하지 않습니다.

4) 가장 흔한 원인 1: 디렉터리/파일 권한(특히 “insecure permissions”)

logrotate는 보안상 이유로 로그 디렉터리의 퍼미션이 너무 열려 있으면 회전을 스킵합니다. 예를 들어 /var/log/myapp0777이거나 group/world writable인데 sticky bit가 없으면 문제가 됩니다.

권한 점검

# 디렉터리와 파일 권한 확인
namei -l /var/log/myapp/myapp.log
ls -ld /var/log/myapp
ls -l /var/log/myapp | head
  • 디렉터리 권한은 보통 0755 또는 0750을 권장합니다.
  • group write가 필요하다면, 접근 모델을 명확히(그룹 지정) 하고 최소 권한으로 유지하세요.

해결 예시

# 예: myapp 그룹만 쓰게 하고 싶을 때
sudo chown -R root:myapp /var/log/myapp
sudo chmod 0750 /var/log/myapp
sudo chmod 0640 /var/log/myapp/*.log

5) 가장 흔한 원인 2: create/su 설정 불일치(권한은 맞는데 새 로그 생성이 실패)

회전 후 새 로그 파일을 만들 때 create 지시어가 관여합니다. 서비스가 root가 아닌 계정으로 실행된다면, 회전 후 새 파일 소유자가 root로 생성되어 앱이 로그를 더 이상 못 쓰는 상황이 생깁니다.

권장: su 지시어 사용

logrotate는 기본적으로 root로 실행되지만, 특정 블록에서 파일 소유자/그룹으로 동작하도록 su를 지정할 수 있습니다.

/var/log/myapp/myapp.log {
  daily
  rotate 14
  missingok
  notifempty
  compress
  delaycompress

  su myapp myapp
  create 0640 myapp myapp
}
  • su myapp myapp: 해당 로그 처리(회전/생성)를 myapp 권한으로 수행
  • create 0640 myapp myapp: 새 로그 파일 소유자/권한 지정

흔한 실수

  • create는 있는데 su가 없어 root로 파일이 생성됨
  • su는 있는데, 대상 디렉터리에 myapp이 쓸 권한이 없음

6) copytruncate 사용 시 주의(권한 문제처럼 보이는 “로그 계속 커짐”)

서비스를 재시작할 수 없거나 SIGUSR1 같은 reopen 신호가 없을 때 copytruncate를 쓰기도 합니다.

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

하지만 copytruncate는 다음 문제가 있습니다.

  • 회전 시점에 로그가 일부 유실될 수 있음(레이스)
  • 매우 큰 파일 복사로 I/O 스파이크
  • SELinux/권한이 엄격한 환경에서 truncate가 막히면 회전이 실패

가능하면 서비스가 로그 파일을 reopen하도록 postrotate에서 신호를 보내는 방식이 더 안전합니다.

7) SELinux 때문에 회전/생성이 막히는 경우(“권한은 맞는데 Permission denied”)

RHEL 계열에서 SELinux가 Enforcing이면, 유닉스 퍼미션이 맞아도 컨텍스트가 틀리면 rename, create, truncate가 거부될 수 있습니다.

SELinux 상태 확인

getenforce
sestatus

거부 로그 확인(audit)

# 최근 AVC 거부 확인
sudo ausearch -m avc -ts recent | tail -n 50

# 또는
sudo grep -i "avc:  denied" /var/log/audit/audit.log | tail -n 50

파일 컨텍스트 확인/복구

# 컨텍스트 확인
ls -Z /var/log/myapp
ls -Z /var/log/myapp/myapp.log

# 기본 정책에 맞게 복구(권장)
sudo restorecon -Rv /var/log/myapp

만약 커스텀 경로(/data/logs/myapp)에 로그를 쓴다면, 해당 경로가 var_log_t 같은 적절한 타입이 아니어서 막힐 수 있습니다. 이때는 semanage fcontext로 영구 반영 후 restorecon을 적용합니다.

# semanage가 없다면 설치: policycoreutils-python-utils(배포판별 상이)

sudo semanage fcontext -a -t var_log_t "/data/logs/myapp(/.*)?"
sudo restorecon -Rv /data/logs/myapp

임시로 setenforce 0로 확인하는 방법이 있지만, 운영에서는 원인 확인 후 즉시 원복하고 정책/컨텍스트로 해결하는 게 안전합니다.

8) postrotate 스크립트 실패로 전체 회전이 실패하는 경우

postrotate/endscript 안에서 서비스 reload가 실패하면 logrotate가 에러로 끝날 수 있습니다.

/var/log/nginx/*.log {
  daily
  rotate 7
  compress
  missingok
  notifempty

  postrotate
    /usr/sbin/nginx -s reopen
  endscript
}

점검 포인트:

  • postrotate 명령 경로가 맞는지
  • 실행 권한/환경(PATH)이 타이머 실행 환경과 다른지
  • systemd 서비스 이름이 다른데 systemctl reload xxx를 호출하는지

수동으로 동일 명령을 실행해 확인하세요.

sudo /usr/sbin/nginx -s reopen
# 또는
sudo systemctl reload nginx

9) 설정 파일 우선순위/중복 정의로 “내 설정이 안 먹는” 경우

대부분 /etc/logrotate.conf가 공통 설정을 포함하고, /etc/logrotate.d/*를 include 합니다.

grep -n "include" /etc/logrotate.conf
ls -l /etc/logrotate.d/

중복 정의가 있으면, 기대와 다른 블록이 적용될 수 있습니다.

  • 같은 로그 파일을 여러 블록에서 매칭
  • 와일드카드(/var/log/myapp/*.log)가 다른 블록에 포함

이럴 때는 logrotate -d 출력에서 “어떤 설정이 어떤 파일에 적용되는지”를 확인하는 게 가장 빠릅니다.

10) 컨테이너/쿠버네티스에서는 logrotate가 “원래 안 도는” 구조일 수 있음

컨테이너는 cron/systemd가 없고, 로그는 stdout/stderr로 흘려서 노드/런타임이 수집하는 형태가 일반적입니다. 이 경우 컨테이너 내부 logrotate를 기대하면 안 맞는 설계가 됩니다.

  • 노드의 /var/log/containers, /var/log/pods는 kubelet/런타임 정책에 따름
  • 애플리케이션 파일 로그를 꼭 써야 한다면 sidecar로 logrotate를 돌리거나, 애초에 로그 수집/보관 정책을 재설계하는 편이 낫습니다.

쿠버네티스 운영 트러블슈팅 관점은 Kubernetes HPA가 안 늘 때 metrics-server 0값 해결처럼 “컨트롤 플레인/에이전트가 실제로 동작하는지”부터 확인하는 접근이 유사합니다.

11) 빠른 체크리스트(현장에서 그대로 따라하기)

  1. 트리거 확인: systemctl list-timers | grep logrotate / cron daily 실행 여부
  2. 최근 실패 로그: journalctl -u logrotate.service 또는 /var/log/cron
  3. 수동 실행: logrotate -vf /etc/logrotate.conf
  4. 상태 파일: /var/lib/logrotate/logrotate.status에 마지막 회전 기록 확인
  5. 권한: 디렉터리/파일 퍼미션, “insecure permissions” 경고 여부
  6. 소유자/생성: su, create 정합성 (앱이 쓰는 계정과 일치?)
  7. SELinux: ausearch -m avc, ls -Z, restorecon, 필요 시 semanage fcontext
  8. postrotate: reload/reopen 명령이 실제로 성공하는지

12) 예시: 커스텀 앱 로그 회전 설정(권한+SELinux까지 안전하게)

/data/logs/myapp/myapp.log에 myapp 유저가 쓰는 파일 로그를 회전한다고 가정합니다.

1) logrotate 설정

# /etc/logrotate.d/myapp
/data/logs/myapp/myapp.log {
  daily
  rotate 14
  missingok
  notifempty

  compress
  delaycompress
  dateext
  dateformat -%Y%m%d

  su myapp myapp
  create 0640 myapp myapp

  postrotate
    # myapp이 SIGHUP으로 로그 reopen을 지원한다는 가정
    /bin/kill -HUP $(cat /run/myapp.pid) 2>/dev/null || true
  endscript
}

2) 디렉터리 권한

sudo mkdir -p /data/logs/myapp
sudo chown -R myapp:myapp /data/logs/myapp
sudo chmod 0750 /data/logs/myapp

3) SELinux 컨텍스트(Enforcing 환경)

sudo semanage fcontext -a -t var_log_t "/data/logs/myapp(/.*)?"
sudo restorecon -Rv /data/logs/myapp

4) 검증

sudo logrotate -vf /etc/logrotate.conf
ls -l /data/logs/myapp
ls -Z /data/logs/myapp

여기까지 했는데도 회전이 안 되면, 대부분은 (1) 실제 트리거가 하루에 한 번도 실행되지 않거나 (2) postrotate가 실패하거나 (3) 동일 파일을 다른 블록이 잡아먹는 설정 충돌입니다.

마무리

logrotate 문제는 “설정 한 줄”보다 실행 경로(타이머/cron) + 파일 시스템 권한 + SELinux 정책 + 서비스의 로그 reopen 방식이 함께 맞아야 해결됩니다. 특히 SELinux Enforcing 환경에서는 유닉스 퍼미션만 보고 접근하면 시간을 많이 쓰게 됩니다.

운영에서 로그 회전이 멈추면 결국 디스크 고갈 → 서비스 장애로 이어지기 쉬우니, 위 체크리스트대로 journal/audit를 먼저 보고, logrotate -vf로 재현하고, su/create와 SELinux 컨텍스트를 정리하는 순서로 접근하면 대부분 빠르게 복구할 수 있습니다.