- Published on
리눅스 logrotate가 안 돎? 권한·SELinux 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 디스크가 갑자기 꽉 차거나, /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 permissionserror: ... Permission deniedSELinux 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/myapp가 0777이거나 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) 빠른 체크리스트(현장에서 그대로 따라하기)
- 트리거 확인:
systemctl list-timers | grep logrotate/ cron daily 실행 여부 - 최근 실패 로그:
journalctl -u logrotate.service또는/var/log/cron - 수동 실행:
logrotate -vf /etc/logrotate.conf - 상태 파일:
/var/lib/logrotate/logrotate.status에 마지막 회전 기록 확인 - 권한: 디렉터리/파일 퍼미션, “insecure permissions” 경고 여부
- 소유자/생성:
su,create정합성 (앱이 쓰는 계정과 일치?) - SELinux:
ausearch -m avc,ls -Z,restorecon, 필요 시semanage fcontext - 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 컨텍스트를 정리하는 순서로 접근하면 대부분 빠르게 복구할 수 있습니다.