- Published on
리눅스 logrotate 실패? 권한·SELinux 7분 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버 운영 중 디스크가 갑자기 차오르거나, 특정 로그 파일이 며칠치씩 쌓여 있는 걸 발견했는데 logrotate가 조용히 실패하고 있는 경우가 많습니다. 특히 RHEL/CentOS/Rocky/Alma 계열처럼 SELinux가 활성화된 환경에서는 “권한은 맞는데도” 회전이 안 되는 일이 자주 발생합니다.
이 글은 logrotate 실패를 7분 내로 진단하는 흐름으로 구성했습니다.
- 1분: 실패 여부와 에러 메시지 확보
- 3분: 권한/소유자/디렉터리 권한 점검
- 3분: SELinux 컨텍스트/정책 점검 및 복구
로그가 쌓여 디스크가 가득 차는 상황이라면, 원인 진단과 함께 inode까지 같이 확인하는 게 좋습니다. 디스크 용량은 남았는데 로그 생성이 실패하는 케이스도 있어요. 필요하면 이 글도 같이 보세요: No space left on device인데 용량 남을 때 - inode 0% 해결
1) 먼저: logrotate가 실제로 실패했는지 확인
systemd 타이머/크론 확인
배포판에 따라 logrotate는 systemd timer 또는 cron으로 실행됩니다.
systemctl list-timers | grep -i logrotate || true
systemctl status logrotate.timer logrotate.service 2>/dev/null || true
# cron 기반일 수도 있음
ls -al /etc/cron.daily/ | grep -i logrotate || true
직접 실행해서 에러를 “보이게” 만들기
-d는 디버그(실제 변경 없음), -v는 상세 로그입니다. 실제로 돌려야 하면 -f를 씁니다.
# 설정 파싱/대상 확인 (실제 회전 없음)
logrotate -d -v /etc/logrotate.conf
# 지금 당장 강제 회전 (주의: 실제로 파일 변경)
logrotate -f -v /etc/logrotate.conf
상태 파일(lock 비슷한 역할) 확인
logrotate는 상태 파일에 마지막 회전 시점을 기록합니다.
cat /var/lib/logrotate/logrotate.status 2>/dev/null || true
상태 파일이 손상되거나 권한이 꼬이면 회전이 건너뛰어지거나 실패할 수 있습니다.
2) 에러 메시지별 “가장 흔한” 원인 맵
logrotate -v 출력이나 /var/log/messages, journalctl에 아래 같은 메시지가 자주 보입니다.
error: skipping ... because parent directory has insecure permissionsPermission deniedfailed to renameerror opening ...: Permission deniedSELinux is preventing ...혹은 AVC denials
이 중 상위 80%는 아래 두 갈래로 정리됩니다.
- 권한/소유자/디렉터리 권한 문제: logrotate는 보통 root로 실행되지만, “회전된 파일을 특정 유저로 생성”하거나 “특정 디렉터리에 쓸 수 있어야” 합니다.
- SELinux 컨텍스트 문제: 권한이 맞아도 라벨이 틀리면 차단됩니다.
이제부터는 가장 빠른 순서(권한 → SELinux)로 점검합니다.
3) 권한 이슈 3분 컷 체크리스트
3-1) insecure permissions로 스킵될 때
대표적으로 Nginx/Apache 로그에서 자주 보입니다.
예시 메시지(형태는 환경마다 조금 다름):
skipping "/var/log/nginx/access.log" because parent directory has insecure permissions
원인: 로그 디렉터리(예: /var/log/nginx)가 group 또는 other에 쓰기 가능(w)이면, logrotate가 안전하지 않다고 판단해 스킵할 수 있습니다.
# 디렉터리 권한 확인
stat -c '%A %U:%G %n' /var/log/nginx
# 권장: 0755 또는 더 엄격하게
chmod 0755 /var/log/nginx
chown root:root /var/log/nginx
만약 해당 디렉터리가 특정 서비스 유저가 써야 한다면, “디렉터리는 안전하게 유지”하면서 “파일 생성만 서비스 유저로” 하도록 create를 올바르게 설정하는 편이 안전합니다.
3-2) create/su 설정 불일치
logrotate는 회전 후 새 로그 파일을 만들 때 create 지시어를 사용합니다. 이때 소유자/그룹/퍼미션이 실제 서비스가 기대하는 값과 다르면 서비스가 새 파일에 쓰지 못해 장애가 납니다.
Nginx 예시:
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
# systemd 환경에서 권장: logrotate가 어떤 유저/그룹으로 동작할지 명시
su root adm
# 회전 후 새 파일 생성 권한/소유자
create 0640 nginx adm
sharedscripts
postrotate
# nginx reopen
/bin/systemctl kill -s USR1 nginx.service >/dev/null 2>&1 || true
endscript
}
포인트:
su는 해당 블록에서 logrotate가 어떤 권한으로 파일 작업을 수행할지 지정합니다.create는 새로 생성될 로그 파일의 모드/소유자/그룹을 지정합니다.- 서비스가
nginx유저로 로그를 쓰는데 새 파일이root:root로 만들어지면, 다음 로그 쓰기에서Permission denied가 납니다.
3-3) rename/copytruncate 선택 실수
회전 방식은 크게 두 가지가 많습니다.
rename기반: 기존 파일을 이름 변경하고 새 파일 생성. 일반적으로 더 안전/권장.copytruncate: 파일을 복사한 뒤 원본을 truncate. 로그를 열어둔 프로세스를 재시작/시그널로 reopen하기 어려울 때 사용.
failed to rename가 뜬다면 다음을 점검하세요.
- 로그 파일이 다른 파일시스템으로 마운트된 경로인지
- 권한/SELinux로 rename이 막힌 건 아닌지
- 컨테이너/볼륨 환경에서 atomic rename이 제약되는지
copytruncate는 편하지만, 회전 순간에 로그 유실/중복이 생길 수 있어 가급적 서비스의 reopen(예: USR1) 방식과 함께 rename을 쓰는 편이 좋습니다.
3-4) 실제 서비스 유저가 새 로그에 쓸 수 있는지 즉시 검증
# 새 파일 소유자/권한 확인
ls -al /var/log/nginx | head
# 서비스 유저로 쓰기 테스트 (운영에서는 주의)
sudo -u nginx sh -c 'echo test >> /var/log/nginx/access.log'
여기서 막히면 logrotate 설정(create) 또는 디렉터리 권한이 원인입니다.
4) SELinux 이슈 3분 컷 체크리스트
권한이 “완벽해 보이는데” 계속 실패하면 SELinux를 의심해야 합니다. 특히 /var/log 밖(예: /data/logs, /srv/logs)에 로그를 두는 경우 SELinux 라벨이 기본값이라 회전/생성이 막힐 수 있습니다.
4-1) SELinux 모드 확인
getenforce
sestatus
Enforcing면 차단이 실제로 발생할 수 있습니다.
4-2) AVC 거부 로그 확인
# 최근 AVC 확인
ausearch -m avc -ts recent 2>/dev/null | tail -n 50
# systemd journal에서 selinux 관련 필터링
journalctl -t setroubleshoot --no-pager | tail -n 200
로그에 특정 경로(/data/logs/app.log 등)와 denied가 보이면 거의 확정입니다.
4-3) 파일 컨텍스트(라벨) 확인
ls -Z /var/log 2>/dev/null | head
ls -Z /data/logs 2>/dev/null | head
# 특정 파일만
ls -Z /data/logs/app.log 2>/dev/null || true
/var/log 아래는 보통 var_log_t 계열 라벨이 붙습니다. 반면 커스텀 경로는 default_t 같은 애매한 라벨로 남아 있는 경우가 많습니다.
4-4) 가장 안전한 해결: 올바른 파일 컨텍스트 지정
임시로 chcon만 하면 재부팅/복구 작업에서 원복될 수 있습니다. 영구적으로는 semanage fcontext 후 restorecon이 정석입니다.
/data/logs를 로그 디렉터리로 쓰는 예시:
# semanage가 없다면 설치
# RHEL 계열
sudo dnf install -y policycoreutils-python-utils || sudo yum install -y policycoreutils-python
# /data/logs 및 하위 파일을 var_log_t로 지정
sudo semanage fcontext -a -t var_log_t '/data/logs(/.*)?'
# 실제 라벨 적용
sudo restorecon -Rv /data/logs
# 확인
ls -Zd /data/logs
ls -Z /data/logs | head
이후 다시 실행:
logrotate -f -v /etc/logrotate.conf
4-5) 정말 급할 때만: Enforcing 임시 해제
운영 환경에서 권장하진 않지만, 원인이 SELinux인지 10초 내 확인하려면 다음이 빠릅니다.
sudo setenforce 0
logrotate -f -v /etc/logrotate.conf
sudo setenforce 1
setenforce 0에서만 성공한다면 SELinux 라벨/정책 문제입니다. 이 경우 위의 semanage fcontext 방식으로 해결하세요.
5) 자주 놓치는 설정 실수 5가지
5-1) postrotate 스크립트 실패로 전체가 실패하는 경우
postrotate에서 서비스 reload/reopen이 실패하면 회전 자체가 실패로 기록될 수 있습니다.
postrotate
/bin/systemctl kill -s USR1 nginx.service >/dev/null 2>&1 || true
endscript
|| true로 “로그 reopen 실패가 회전 실패로 번지지 않게” 만드는 게 실무에서 유용합니다.
5-2) sharedscripts 유무
와일드카드(/var/log/nginx/*.log)로 여러 파일을 회전할 때 postrotate가 파일마다 실행되면 불필요하게 여러 번 reload될 수 있습니다.
sharedscripts없음: 파일마다postrotate실행sharedscripts있음: 블록당 한 번 실행
5-3) dateext와 외부 도구의 파일 패턴 충돌
dateext를 켜면 회전 파일명이 access.log-20260224 같은 형태로 바뀝니다. 백업/수집 도구가 *.log.1만 기대하면 누락이 생길 수 있어요.
5-4) compress와 애플리케이션의 tail/파서
압축된 로그(.gz)를 애플리케이션이 직접 읽는 구조라면, 압축 시점(delaycompress)을 고려해야 합니다.
5-5) 컨테이너 환경에서 logrotate를 “호스트에서” 돌리는 문제
쿠버네티스에서는 보통 stdout/stderr 기반 수집을 권장합니다. 그래도 파일 로그를 회전해야 한다면, 볼륨 마운트 권한/SELinux(노드 정책)/SecurityContext까지 함께 봐야 합니다. 쿠버네티스 트러블슈팅 흐름이 필요하면 이 글도 도움이 됩니다: EKS에서 kubectl exec·logs가 안 될 때 진단법
6) “바로 복붙” 가능한 진단 스크립트
아래는 특정 로그 파일 하나를 대상으로 권한과 SELinux를 빠르게 훑는 커맨드 묶음입니다. 경로만 바꿔 실행하세요.
LOG=/var/log/nginx/access.log
DIR=$(dirname "$LOG")
echo "[1] logrotate dry-run"
logrotate -d -v /etc/logrotate.conf 2>&1 | tail -n 50
echo "[2] file/dir permissions"
stat -c '%A %a %U:%G %n' "$DIR" "$LOG" 2>/dev/null || true
echo "[3] selinux mode"
getenforce 2>/dev/null || true
echo "[4] selinux context"
ls -Zd "$DIR" 2>/dev/null || true
ls -Z "$LOG" 2>/dev/null || true
echo "[5] recent AVC denials"
ausearch -m avc -ts recent 2>/dev/null | tail -n 20 || true
7) 결론: 해결 우선순위는 “디렉터리 권한 → create/su → SELinux 라벨”
logrotate 실패는 겉으로는 단순히 “회전이 안 됨”이지만, 실제 원인은 대부분 다음 셋 중 하나입니다.
- 로그 디렉터리 권한이 안전하지 않아
insecure permissions로 스킵 create/su설정 불일치로 새 로그 파일에 서비스가 쓰지 못함- SELinux 컨텍스트가 맞지 않아 rename/create가 차단됨
이 순서대로 보면 시간 낭비 없이 빠르게 복구할 수 있습니다. 특히 SELinux는 끄는 게 아니라, semanage fcontext와 restorecon으로 “의도한 라벨”을 부여하는 게 정석입니다.