infra
Platform

모듈 맵

[Linux] logrotate로 서버 용량 갉아먹는 로그 파일 자동 압축/분할

0 / 37 완료

펼치기
0 / 37 완료0%

Linux · 27 / 37

[Linux] logrotate로 서버 용량 갉아먹는 로그 파일 자동 압축/분할

로그가 디스크를 갉아먹기 전에 — logrotate, journald 보존 정책 완전 설정

🚨INCIDENT ALERT
HIGH

오전에 출근해 모니터링을 보니 서버 디스크가 98%입니다. df -h로 확인하니 /var/log가 40GB를 차지하고 있었습니다. nginx 액세스 로그 파일 하나가 30GB였습니다. 지난 6개월치 로그가 압축도 안 된 채 그대로 쌓여 있었던 겁니다. logrotate를 설정했다고 생각했는데, 커스텀 애플리케이션 로그는 설정에서 빠져 있었습니다. 로그는 사고 분석에 반드시 필요하지만, 관리되지 않으면 디스크를 갉아먹어 서비스 장애를 유발합니다.

로그 관리 & logrotate

운영 서버에서 가장 조용하게 장애를 유발하는 원인 중 하나는 디스크 풀(Disk Full) 입니다. 그리고 그 주범은 대부분 통제되지 않은 로그 파일입니다. 로그는 필수적이지만, 관리되지 않으면 수 GB를 순식간에 차지합니다. 이 챕터에서는 Linux 로그 시스템의 구조를 이해하고, logrotatejournald 보존 정책을 통해 로그를 완전히 통제하는 방법을 배웁니다.


1. Linux 로그 체계 전체 그림

이번 챕터에서 배울 것
  • 1Linux 로그 아키텍처: rsyslog, journald, /var/log 구조와 흐름
  • 2journalctl 핵심 옵션으로 서비스 로그 조회 및 필터링
  • 3logrotate 설정 파일 계층과 rotate/compress/delaycompress 디렉티브
  • 4Node.js 등 애플리케이션별 커스텀 logrotate 설정 작성
  • 5logrotate -d/-f 옵션으로 설정 테스트 및 강제 실행
  • 6journald.conf 보존 정책(SystemMaxUse, MaxRetentionSec) 구성
실습 환경 준비
logrotate 설치 여부 및 버전 확인
logrotate --version && ls /etc/logrotate.d/
journald 상태 및 디스크 사용량 확인
journalctl --disk-usage && systemctl status systemd-journald
현재 로그 디렉터리 디스크 점유 확인
du -sh /var/log/* 2>/dev/null | sort -rh | head -15
실습 환경 참고

logrotate 설정 변경 및 journald.conf 수정은 root 권한이 필요합니다. 테스트는 반드시 -d(dry-run) 옵션으로 먼저 검증하세요.

💡개념

Linux 로그 아키텍처: rsyslog, journald, /var/log

Linux 로그 아키텍처 — 생성에서 로테이션까지 전체 흐름

서비스 장애가 났을 때 journalctl -u nginx로 확인하면 로그가 나오는데, /var/log/nginx/error.log에서 찾으면 없는 경우가 있습니다. 반대로 /var/log/syslog에는 있는데 journalctl에는 없기도 합니다. 두 시스템이 따로 동작하고 있어서 어디를 봐야 하는지 혼란스럽습니다. 현대 Linux에는 systemd-journaldrsyslog 두 개의 로그 시스템이 병렬로 돌고 있으며, 앱에 따라 둘 중 하나 또는 둘 다 로그를 씁니다. 어떤 서비스가 어디에 로그를 남기는지, 두 시스템의 관계가 어떻게 되는지를 알면 장애 때 찾는 시간을 크게 줄일 수 있습니다.

현대 Linux 배포판은 두 가지 로그 시스템이 병렬로 동작합니다.

커널 & 애플리케이션
        │
        ├──► systemd-journald  (바이너리 저널, /run/log/journal/ 또는 /var/log/journal/)
        │           │
        │           └──► rsyslog / syslog-ng  (텍스트 파일, /var/log/)
        │
        └──► 직접 파일 쓰기  (Nginx, MySQL 등 자체 로그)

systemd-journald

  • systemd 생태계의 기본 로그 수집기
  • 모든 서비스의 stdout/stderr를 자동으로 캡처
  • 바이너리 포맷으로 저장 — 구조화 필드, 빠른 검색, 변조 감지
  • journalctl 명령으로 조회

rsyslog

  • 전통적인 syslog 데몬. 텍스트 파일로 /var/log/ 에 저장
  • journald와 소켓을 통해 연동하여 동일 로그를 텍스트로 변환 저장
  • UDP/TCP 514 포트로 원격 로그 서버로 전송 가능
  • RHEL/CentOS 계열에서 기본, Ubuntu는 rsyslog 또는 syslog 둘 다 지원

핵심 /var/log 디렉터리 구조 — 주요 로그 파일의 위치와 역할을 파악하는 것이 로그 관리의 시작입니다:

/var/log/
├── messages          # RHEL 계열: 일반 시스템 메시지 (auth 제외)
├── syslog            # Debian/Ubuntu 계열: messages와 동일 역할
├── auth.log          # Debian/Ubuntu: 인증 로그 (SSH, sudo, su)
├── secure            # RHEL 계열: 인증 로그
├── dmesg             # 커널 링 버퍼 (부팅 시 하드웨어 감지 등)
├── boot.log          # 부팅 과정 서비스 시작/실패 기록
├── cron              # cron 작업 실행 기록
├── kern.log          # 커널 메시지만 별도 보관 (Debian 계열)
├── maillog           # 메일 서버 로그 (RHEL 계열)
├── dpkg.log          # 패키지 설치/제거 이력 (Debian 계열)
├── yum.log / dnf.log # 패키지 이력 (RHEL 계열)
├── nginx/            # Nginx access.log, error.log
├── mysql/ or mariadb/# DB 로그
└── journal/          # journald 바이너리 저널 (영구 보관 시)

각 로그 파일이 중요한 이유 — 파일별 주요 용도와 현장에서 어떻게 활용하는지 정리했습니다:

파일주요 용도실무 활용
messages / syslog시스템 전반 이벤트장애 첫 진단 시작점
secure / auth.logSSH 로그인, sudo 명령보안 감사, 침입 탐지
dmesg하드웨어 오류, OOM Killer디스크/메모리 장애 진단
cron배치 작업 실행 여부배치 미실행 원인 파악
boot.log부팅 시 서비스 실패부팅 불가 원인 추적

2. journalctl 심화 사용법

실습 전 디렉토리와 예제 파일을 먼저 준비합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part5/exam_24/logs && cd /tmp/linux/part5/exam_24

# 실습용 로그 파일 생성 (크기 있는 파일)
for i in $(seq 1 100); do
  echo "$(date): INFO [app] Request processed id=$i status=200" >> /tmp/linux/part5/exam_24/logs/app.log
done
wc -l /tmp/linux/part5/exam_24/logs/app.log

이제 실습을 진행합니다.

journalctl 핵심 옵션 마스터하기

기본 조회 — journalctl 기본 옵션으로 시스템 전체 로그를 조회합니다:

로컬 터미널
# 전체 저널 조회 (최신 항목이 마지막)
journalctl

# 최근 50줄만 보기 (-n 기본값은 10)
journalctl -n 50

# 실시간 스트리밍 (tail -f 와 동일)
journalctl -f

# 실시간 스트리밍 + 최근 20줄 먼저 표시
journalctl -fn 20

서비스별 필터링 (-u 옵션) — 특정 서비스의 로그만 골라서 확인합니다:

서버 터미널
# 특정 서비스 로그만 보기
journalctl -u nginx.service

# 복수 서비스 동시 조회
journalctl -u nginx.service -u php-fpm.service

# 특정 서비스 실시간 모니터링
journalctl -fu sshd.service

우선순위 필터링 (-p 옵션) — err, warning, crit 등 심각도 기준으로 로그를 걸러냅니다:

서버 터미널
# syslog 우선순위 레벨:
# 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug

# warning 이상만 보기 (0~4 포함)
journalctl -p warning

# error 이상만 보기
journalctl -p err

# 특정 범위: info부터 warning까지
journalctl -p info..warning

시간 범위 필터링 (--since / --until) — 장애 발생 시각 전후 구간을 지정해 로그를 추적합니다:

서버 터미널
# 오늘 자정부터 현재까지
journalctl --since today

# 어제 하루
journalctl --since yesterday --until today

# 특정 시간 범위
journalctl --since "2026-03-25 09:00:00" --until "2026-03-25 18:00:00"

# 30분 전부터 현재까지
journalctl --since "30 minutes ago"

# 특정 서비스 + 시간 범위 조합
journalctl -u postgresql.service --since "1 hour ago"

출력 포맷 옵션 — JSON, 짧은 형식 등 상황에 맞는 출력 형식을 선택합니다:

서버 터미널
# JSON 포맷 출력 (로그 수집 파이프라인용)
journalctl -u myapp.service -o json-pretty

# 간결한 출력 (타임스탬프 + 메시지만)
journalctl -o short

# 카탈로그 메시지 포함 (설명 추가)
journalctl -x

# 현재 부팅 세션만 (-b 0 = 현재, -b -1 = 이전 부팅)
journalctl -b
journalctl -b -1

# 저널 디스크 사용량 확인
journalctl --disk-usage

실전 활용 패턴 — 현장에서 자주 쓰는 journalctl 조합 명령어 모음입니다:

서버 터미널
# 최근 에러 빠르게 확인
journalctl -p err --since "1 hour ago" --no-pager

# 배포 직후 모니터링
journalctl -fu myapp.service

# 특정 PID 로그 추적
journalctl _PID=12345

# 커널 메시지만
journalctl -k

# grep과 조합
journalctl -u nginx.service | grep "502"
🔍실행 후 확인할 것
  • journalctl -u nginx --since '1 hour ago' 실행 시 지난 1시간의 nginx 로그가 출력된다
  • journalctl --disk-usage 에서 현재 저널이 차지하는 용량이 표시된다
  • journalctl --list-boots 에서 이전 부팅 회차 목록이 나타난다 (영구 저장 설정 후)
  • journalctl -p err -b 실행 시 현재 부팅 이후 에러 수준 이상의 로그만 필터링되어 출력된다

3. logrotate 설정 파일 구조

💡개념

logrotate 아키텍처와 설정 파일 계층

logrotate 설정 계층 구조 — 전역 defaults와 per-app override

Nginx 로그가 단일 파일에 6개월치가 쌓여 있어서 vi /var/log/nginx/access.log를 열면 10GB짜리 파일이 뜹니다. > access.log로 비워버리면 Nginx가 새 파일을 인식하지 못하고 계속 삭제된 inode에 씁니다. 올바른 방법은 logrotate가 주기적으로 파일을 잘라내고 서비스에게 "새 파일로 다시 써라" 신호를 보내는 것입니다. 그런데 logrotate를 설정해도 작동이 안 된다면 설정 파일이 어디에 있는지, 어떤 계층 구조로 읽히는지 모르는 경우가 많습니다.

logrotate는 cron에 의해 주기적으로 실행되어 로그 파일을 회전(rotate)시키는 도구입니다.

실행 방식 — logrotate는 cron이 자동 호출하거나 수동으로 직접 실행할 수 있습니다:

/etc/cron.daily/logrotate
        │
        └──► /usr/sbin/logrotate /etc/logrotate.conf
                        │
                        ├── 전역 기본값 읽기
                        └── include /etc/logrotate.d/*
                                ├── nginx
                                ├── rsyslog
                                ├── mysql
                                └── myapp  ← 직접 작성

메인 설정 파일: /etc/logrotate.conf — 시스템 전체 로테이션의 기본값을 정의합니다:

CONF
# /etc/logrotate.conf — 전역 기본값

# 기본 주기: 주 1회
weekly

# 4주치 보관
rotate 4

# 로테이션 후 새 빈 파일 생성
create

# 날짜를 파일명에 포함 (예: access.log.2026-03-25)
dateext

# gzip 압축
compress

# 개별 서비스 설정 포함
include /etc/logrotate.d

개별 서비스 설정: /etc/logrotate.d/ 디렉터리

각 파일은 하나 이상의 블록으로 구성됩니다:

파일_패턴 {
    옵션1
    옵션2
    ...
    스크립트_블록
}

핵심 옵션 전체 참조 — logrotate 설정에서 자주 쓰는 옵션과 사용 예시입니다:

옵션설명예시
daily / weekly / monthly로테이션 주기daily
rotate N보관할 오래된 파일 수rotate 7
size N지정 크기 초과 시 로테이션size 100M
maxsize N주기 전이라도 크기 초과 시 로테이션maxsize 500M
minsize N주기가 됐어도 이 크기 미만이면 건너뜀minsize 10M
compressgzip 압축compress
delaycompress이번 로테이션 파일은 다음 번에 압축delaycompress
copytruncate복사 후 원본 파일을 0바이트로 truncatecopytruncate
nocopytruncatecopytruncate 비활성화 (기본값)
create MODE OWNER GROUP새 로그 파일 생성 시 권한 설정create 0640 www-data adm
nocreate새 파일 생성 안 함
missingok로그 파일 없어도 에러 무시missingok
nomissingok없으면 에러 (기본값)
notifempty비어 있으면 로테이션 건너뜀notifempty
ifempty비어 있어도 로테이션 (기본값)
sharedscripts여러 파일 매칭 시 스크립트를 한 번만 실행sharedscripts
dateext파일명에 날짜 접미사 추가dateext
dateformat날짜 형식 지정dateformat -%Y%m%d
su USER GROUP다른 권한으로 실행su nginx nginx

4. 기본 logrotate 설정 파일 분석

시스템 기본 logrotate 설정 읽기

실제 배포판에 설치된 설정을 읽으며 각 옵션의 의미를 파악합니다.

Nginx 기본 설정 (/etc/logrotate.d/nginx) — Nginx 로그를 매일 로테이션하고 compress로 공간을 절약합니다:

CONF
/var/log/nginx/*.log {
    # 매일 로테이션
    daily

    # nginx 프로세스가 root가 아닐 때 권한 문제 방지
    su root adm

    # 파일 없어도 에러 무시 (서비스 시작 전 로그 없을 수 있음)
    missingok

    # 52주(약 1년)치 보관
    rotate 52

    # gzip 압축 활성화
    compress

    # 직전 로테이션 파일은 아직 압축하지 않음
    # (rsyslog 등이 아직 읽을 수 있도록)
    delaycompress

    # 비어 있으면 건너뜀
    notifempty

    # 여러 *.log 파일이 있어도 postrotate 스크립트는 한 번만 실행
    sharedscripts

    postrotate
        # nginx에게 로그 파일 재오픈 신호 전송
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

MySQL 기본 설정 (/etc/logrotate.d/mysql-server) — MySQL slow 쿼리 로그도 logrotate로 관리합니다:

CONF
/var/log/mysql/mysql.log /var/log/mysql/mysql-slow.log /var/log/mysql/error.log {
    # 매일
    daily

    # 7일치 보관
    rotate 7

    # 없어도 오류 무시
    missingok

    # 새 파일 생성 (660 권한, mysql 사용자)
    create 660 mysql adm

    compress
    delaycompress
    notifempty

    sharedscripts
    postrotate
        # MySQL에 FLUSH LOGS 명령으로 파일 재오픈
        test -x /usr/bin/mysqladmin && \
          /usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf \
          flush-logs 2>/dev/null || true
    endscript
}

rsyslog 설정 확인 — rsyslog와 logrotate가 협력해 로그를 기록하고 정리하는 구조를 파악합니다:

로컬 터미널
# 현재 시스템의 logrotate 설정 목록
ls -la /etc/logrotate.d/

# 특정 설정 파일 내용 확인
cat /etc/logrotate.d/rsyslog

# logrotate 상태 파일 (마지막 실행 기록)
cat /var/lib/logrotate/logrotate.status
# 또는
cat /var/lib/logrotate.status

logrotate 상태 파일 예시/var/lib/logrotate/status에 마지막 로테이션 시각이 기록됩니다:

logrotate state -- version 2
"/var/log/nginx/access.log" 2026-3-25-6:0:0
"/var/log/nginx/error.log" 2026-3-25-6:0:0
"/var/log/syslog" 2026-3-25-6:0:1
"/var/log/auth.log" 2026-3-25-6:0:1
"/var/log/dpkg.log" 2026-3-25-6:0:1

5. 커스텀 앱 logrotate 설정 작성

Node.js 웹 애플리케이션 로그 logrotate 설정

/opt/myapp에 배포된 Node.js 애플리케이션이 /var/log/myapp/ 에 로그를 남기는 상황을 가정합니다.

애플리케이션 로그 구조 — 커스텀 앱 로그 경로와 권한 구조를 설정합니다:

/var/log/myapp/
├── app.log       # 일반 애플리케이션 로그
├── error.log     # 에러 전용
└── access.log    # HTTP 요청 로그

설정 파일 작성 — 앱 전용 logrotate 설정 파일을 /etc/logrotate.d에 추가합니다:

로컬 터미널
sudo tee /etc/logrotate.d/myapp << 'EOF'
/var/log/myapp/*.log {
    # 매일 로테이션
    daily

    # 30일치 보관
    rotate 30

    # 압축
    compress

    # 직전 파일은 다음 번에 압축 (진행 중인 분석 툴이 읽을 수 있도록)
    delaycompress

    # 파일 없어도 무시
    missingok

    # 비어 있으면 건너뜀
    notifempty

    # 파일명에 날짜 추가: app.log-20260325.gz
    dateext
    dateformat -%Y%m%d

    # 새 로그 파일을 640 권한으로 생성
    create 0640 myapp myapp

    # 여러 파일 있어도 스크립트는 한 번만 실행
    sharedscripts

    postrotate
        # systemd 서비스에 SIGUSR1 신호 → 로그 파일 재오픈
        /bin/systemctl kill -s USR1 myapp.service 2>/dev/null || true
    endscript
}
EOF

파일 크기 기반 로테이션 (고트래픽 환경) — 날짜보다 파일 크기를 기준으로 로테이션해 디스크 초과를 방지합니다:

CONF
/var/log/myapp/access.log {
    # 크기 기반: 100MB 초과 시 즉시 로테이션
    size 100M

    # 주기와 크기 병행: 주기가 됐어도 10MB 미만이면 건너뜀
    # size 대신 아래 조합을 사용할 수도 있음
    # daily
    # minsize 10M
    # maxsize 500M

    # 90개 파일 보관 (size 기반이면 날짜 무관하게 개수 제한)
    rotate 90

    compress
    delaycompress
    missingok
    notifempty
    dateext
    create 0640 myapp myapp

    sharedscripts
    postrotate
        /bin/systemctl kill -s USR1 myapp.service 2>/dev/null || true
    endscript
}

설정 검증 — 실제 로테이션 전에 설정 파일 오류를 검사합니다:

로컬 터미널
# 문법 오류 확인 (실제 실행하지 않음)
sudo logrotate -d /etc/logrotate.d/myapp

# 강제 실행 (날짜 조건 무시하고 즉시 로테이션)
sudo logrotate -f /etc/logrotate.d/myapp

# 결과 확인
ls -la /var/log/myapp/

6. logrotate 강제 실행 및 디버그

logrotate -d와 -f로 테스트하기

실제 운영 환경에서 logrotate 설정을 배포하기 전에 반드시 디버그 모드로 검증해야 합니다.

디버그 모드 (-d): 실제 실행 없이 시뮬레이션 — 실제로 실행되지 않고 어떤 동작을 할지 미리 보여줍니다:

로컬 터미널
# 특정 설정 파일 디버그
sudo logrotate -d /etc/logrotate.d/myapp

# 예상 출력:
# reading config file /etc/logrotate.d/myapp
# Allocating hash table for state file, size 64 entries
#
# Handling 1 logs
#
# rotating pattern: /var/log/myapp/*.log  after 1 days (30 rotations)
# empty log files are not rotated, old logs are removed
# considering log /var/log/myapp/app.log
#   log does not need rotating (log has been already rotated)

강제 실행 (-f): 날짜 조건 무시하고 즉시 로테이션 — 로테이션 조건이 충족되지 않아도 지금 당장 실행합니다:

로컬 터미널
# 특정 설정만 강제 실행
sudo logrotate -f /etc/logrotate.d/myapp

# 전체 설정 강제 실행
sudo logrotate -f /etc/logrotate.conf

# 상세 출력과 함께 강제 실행
sudo logrotate -vf /etc/logrotate.d/myapp

# 상세 출력 예:
# reading config file /etc/logrotate.d/myapp
# rotating log /var/log/myapp/app.log, log->rotateCount is 30
# dateext suffix '-20260325'
# glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
# renaming /var/log/myapp/app.log.1 to /var/log/myapp/app.log.2 (rotatecount 1, logstart 1, i 1),
# old log /var/log/myapp/app.log.30 doesn't exist
# rotating log /var/log/myapp/app.log, log->rotateCount is 30
# renamed /var/log/myapp/app.log to /var/log/myapp/app.log-20260325
# running postrotate script

로테이션 결과 확인 — 로테이션 후 파일이 제대로 압축·보관됐는지 확인합니다:

로컬 터미널
# 로테이션 후 파일 목록
ls -lh /var/log/myapp/

# 예상 결과:
# -rw-r----- 1 myapp myapp    0 Mar 25 10:30 app.log         ← 새 빈 파일
# -rw-r----- 1 myapp myapp 2.3M Mar 25 10:29 app.log-20260325  ← 로테이션된 파일
# -rw-r----- 1 myapp myapp 4.1M Mar 24 06:00 app.log-20260324.gz ← 압축된 파일

# 상태 파일에서 마지막 실행 시각 확인
grep myapp /var/lib/logrotate/logrotate.status

cron 설정 확인 — logrotate를 자동 실행하는 cron 스케줄을 확인합니다:

로컬 터미널
# logrotate가 cron.daily에 등록되어 있는지 확인
ls -la /etc/cron.daily/logrotate

# cron.daily 실행 시각 확인 (아나크론 설정)
cat /etc/anacrontab | grep daily

# 수동으로 cron.daily 전체 실행
sudo run-parts /etc/cron.daily

7. journald 보존 정책 설정

journald.conf로 저널 보존 정책 구성하기

journald의 로그 보존 정책은 /etc/systemd/journald.conf에서 관리합니다.

기본 설정 파일 구조 — journald 전역 설정 파일(/etc/systemd/journald.conf)의 핵심 항목입니다:

INI
# /etc/systemd/journald.conf

[Journal]
# 저장 위치: "persistent" = /var/log/journal/, "volatile" = /run/log/journal/
# "auto" = /var/log/journal/이 있으면 persistent, 없으면 volatile
Storage=auto

# 바이너리 압축 여부
Compress=yes

# 디스크 사용량 제한
SystemMaxUse=1G

# 최솟값 보장 (다른 항목이 SystemMaxUse를 넘어도 이 공간은 확보)
SystemKeepFree=100M

# 개별 저널 파일 최대 크기
SystemMaxFileSize=128M

# 최대 파일 개수
SystemMaxFiles=100

# 런타임(RAM) 저널 제한 (Storage=volatile 시)
RuntimeMaxUse=200M
RuntimeKeepFree=50M

# 보존 기간 (0 = 제한 없음)
MaxRetentionSec=3month

# 동기화 주기 (로그 유실 방지 vs 성능 tradeoff)
SyncIntervalSec=5m

# 속도 제한 (서비스당 초당 메시지 수)
RateLimitIntervalSec=30s
RateLimitBurst=10000

실전 권장 설정 (중소규모 서버) — 중소 규모 서버에서 저널 크기와 보존 기간을 실무 기준으로 설정합니다:

로컬 터미널
sudo tee /etc/systemd/journald.conf << 'EOF'
[Journal]
Storage=persistent
Compress=yes

# 전체 저널 최대 2GB
SystemMaxUse=2G

# 항상 500MB 여유 공간 유지
SystemKeepFree=500M

# 개별 파일 최대 256MB
SystemMaxFileSize=256M

# 3개월치 보관
MaxRetentionSec=3month

# 속도 제한 완화 (기본값이 너무 엄격한 경우)
RateLimitIntervalSec=30s
RateLimitBurst=10000
EOF

# 설정 적용 (journald 재시작)
sudo systemctl restart systemd-journald

# 적용 확인
journalctl --disk-usage

영구 저널 활성화 (재부팅 후에도 로그 유지)/var/log/journal 디렉터리를 만들면 재부팅 후에도 저널이 유지됩니다:

로컬 터미널
# /var/log/journal/ 디렉터리 생성 (없으면 휘발성으로 동작)
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal

# journald 재시작
sudo systemctl restart systemd-journald

# 확인
ls -la /var/log/journal/
journalctl -b -1  # 이전 부팅 로그가 보이면 영구 저널 활성화됨

저널 수동 정리 — 오래된 저널을 디스크에서 직접 삭제해 공간을 확보합니다:

서버 터미널
# 디스크 사용량 확인
journalctl --disk-usage

# 2주 이상 된 로그 삭제
sudo journalctl --vacuum-time=2weeks

# 총 용량을 500MB로 축소
sudo journalctl --vacuum-size=500M

# 파일 수를 10개로 제한
sudo journalctl --vacuum-files=10

# 정리 후 확인
journalctl --disk-usage

8. 실무 트러블슈팅

증상 — 로테이션 후 서비스가 기존 파일 디스크립터를 그대로 사용해 로그가 유실되는 상황입니다:

로컬 터미널
# logrotate 실행 후 파일 확인
ls -la /var/log/myapp/
# -rw-r----- 1 myapp myapp 4.5M Mar 25 10:00 app.log            ← 새 파일인데 계속 커짐?
# -rw-r----- 1 myapp myapp   0B Mar 25 06:00 app.log-20260325   ← 로테이션된 파일이 비어있음

# 어디에 쓰고 있는지 확인
sudo lsof /var/log/myapp/app.log-20260325
# myapp   1234 myapp    3w   REG  8,1  4718592  ... /var/log/myapp/app.log-20260325

원인 분석:

Linux에서 파일 이름은 inode에 대한 포인터일 뿐입니다. 프로세스가 파일 디스크립터(fd)로 파일을 열면, 파일 이름이 바뀌어도 같은 inode를 계속 참조합니다. logrotate가 app.logapp.log-20260325로 이름을 바꾸면, 데몬은 여전히 이름이 바뀐 파일(구 inode)에 계속 씁니다.

before rotate:
  app.log ──► inode 12345 (데몬 fd 3이 열고 있음)

after rename:
  app.log-20260325 ──► inode 12345 (데몬는 fd 3으로 계속 씀!)
  app.log          ──► inode 67890 (새로 생성된 빈 파일)

해결 방법 1: postrotate로 서비스 재시작 / 시그널 — 로테이션 직후 서비스에 HUP 신호를 보내 새 파일로 전환합니다:

CONF
/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress

    sharedscripts
    postrotate
        # SIGUSR1: 많은 데몬이 이 신호를 받으면 로그 파일을 재오픈
        /bin/systemctl kill -s USR1 myapp.service 2>/dev/null || true

        # 또는 HUP 신호 (일부 데몬은 설정 재로드 + 로그 재오픈)
        # /bin/systemctl reload myapp.service 2>/dev/null || true

        # 또는 완전 재시작 (서비스 중단 발생 가능 — 마지막 수단)
        # /bin/systemctl restart myapp.service 2>/dev/null || true
    endscript
}

해결 방법 2: copytruncate (재시작 불가 데몬에 한해) — 파일을 복사한 뒤 원본을 비워 서비스 재시작 없이 로테이션합니다:

CONF
/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress

    # 파일을 복사한 후 원본을 0바이트로 truncate
    # 데몬은 같은 fd(같은 inode)를 계속 사용하지만, 파일 내용이 비워짐
    copytruncate
}

copytruncate의 주의사항: 복사와 truncate 사이에 짧은 시간 간격이 있어 그 사이에 쓴 로그가 유실될 수 있습니다. 가능하면 postrotate + 시그널 방식을 권장합니다.

어떤 신호를 써야 하는지 확인 — 서비스마다 로그 재오픈에 사용하는 신호가 다르므로 먼저 확인합니다:

로컬 터미널
# 해당 데몬의 man 페이지나 공식 문서 확인
man myapp

# 또는 실행 중 프로세스가 처리하는 신호 확인
cat /proc/$(pgrep myapp)/status | grep -i sig

# Nginx: USR1으로 로그 재오픈
kill -USR1 $(cat /var/run/nginx.pid)

# Apache: SIGUSR1 또는 graceful
apachectl graceful

증상 — 디스크 사용량이 임계치를 초과하기 직전의 전형적인 징후입니다:

로컬 터미널
df -h
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   50G     0  100% /

# 로그 쓰기 실패 메시지
tail -f /var/log/syslog
# Mar 25 10:30:01 server kernel: [12345.678] EXT4-fs error: No space left on device

즉각 조치 — 로그 공간 확보 순서 — 디스크 풀 상황에서 빠르게 공간을 만드는 우선순위 순서입니다:

로컬 터미널
# 1단계: 어디서 공간을 먹는지 확인
df -h
du -sh /var/log/* | sort -rh | head -20

# 2단계: 가장 큰 로그 파일 식별
find /var/log -name "*.log" -size +100M -ls 2>/dev/null | sort -k7 -rn

# 3단계: journald 즉시 정리 (빠르고 안전)
sudo journalctl --vacuum-size=100M

# 4단계: 압축되지 않은 오래된 로그 즉시 압축
sudo find /var/log -name "*.log.[0-9]" -exec gzip {} \;

# 5단계: 너무 오래된 로테이션 파일 삭제 (7일 이상)
sudo find /var/log -name "*.gz" -mtime +7 -delete

# 6단계: logrotate 강제 실행
sudo logrotate -f /etc/logrotate.conf

# 7단계: 특정 서비스 로그 직접 비우기 (서비스 중단 없이)
# 주의: 이미 열려있는 파일은 truncate로만 비울 것, rm 하면 안 됨
sudo truncate -s 0 /var/log/myapp/app.log

대용량 로그 파일 긴급 처리 — 서비스를 중단하지 않고 대용량 로그 파일을 안전하게 정리합니다:

로컬 터미널
# 현재 열려있는 파일인지 확인
sudo lsof /var/log/myapp/app.log

# 열려있는 파일: truncate 사용 (데몬 중단 없음)
sudo truncate -s 0 /var/log/myapp/app.log

# 열려있지 않은 파일: 안전하게 삭제 가능
sudo rm /var/log/myapp/app.log.3.gz

# 공간 확보 확인
df -h

재발 방지 — logrotate 설정 즉시 강화 — 디스크 풀 이후 로테이션 주기와 압축 설정을 강화합니다:

로컬 터미널
# maxsize 옵션 추가하여 크기 기반 로테이션 활성화
sudo tee /etc/logrotate.d/myapp << 'EOF'
/var/log/myapp/*.log {
    daily
    maxsize 200M    # 200MB 초과 시 즉시 로테이션
    rotate 7        # 7일치만 보관으로 줄임
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        /bin/systemctl kill -s USR1 myapp.service 2>/dev/null || true
    endscript
}
EOF

모니터링 설정 — 사전 경보 — 디스크 사용량이 임계치를 넘기 전에 알림을 받도록 설정합니다:

로컬 터미널
# 디스크 사용률 80% 초과 시 경보하는 스크립트
sudo tee /usr/local/bin/disk-alert.sh << 'EOF'
#!/bin/bash
THRESHOLD=80
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "ALERT: Disk usage is ${USAGE}% on $(hostname)" | \
        mail -s "Disk Alert" admin@example.com
fi
EOF
sudo chmod +x /usr/local/bin/disk-alert.sh

# cron에 등록 (매 시간 체크)
echo "0 * * * * root /usr/local/bin/disk-alert.sh" | \
    sudo tee /etc/cron.d/disk-alert

증상 — logrotate가 예상 시각에 실행되지 않아 로그 파일이 로테이션되지 않는 상황입니다:

로컬 터미널
# 로그가 수 GB인데 logrotate가 전혀 실행된 흔적이 없음
cat /var/lib/logrotate/logrotate.status
# (비어있거나 오래된 날짜)

# 수동 실행은 잘 됨
sudo logrotate -f /etc/logrotate.conf  # ← 이건 됨

원인 1: cron 데몬이 실행 중이지 않음 — crond/cron 서비스가 중단되면 logrotate 자동 실행도 멈춥니다:

서버 터미널
# cron 상태 확인
sudo systemctl status cron       # Debian/Ubuntu
sudo systemctl status crond      # RHEL/CentOS

# 실행 중이지 않으면 시작
sudo systemctl start cron
sudo systemctl enable cron

원인 2: /etc/cron.daily 실행 시각 문제 — 서버 다운 시각과 cron.daily 실행 시각이 겹치면 skip됩니다:

로컬 터미널
# cron.daily는 기본적으로 매일 특정 시각(보통 06:25)에 실행됨
# 해당 시각에 서버가 꺼져있거나 부하가 높으면 건너뜀
grep daily /etc/crontab
# 25 6    * * * root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )

# anacron 사용 여부 확인 (서버가 꺼져있어도 재부팅 후 실행)
which anacron
sudo systemctl status anacron

# anacron 설정 확인
cat /etc/anacrontab

원인 3: 권한 문제 — logrotate 스크립트 실행 불가 — 스크립트 권한이 잘못되면 cron이 실행하지 못합니다:

로컬 터미널
# /etc/cron.daily/logrotate 권한 확인
ls -la /etc/cron.daily/logrotate
# 반드시 실행 권한이 있어야 하고, 소유자는 root여야 함
# -rwxr-xr-x 1 root root ... /etc/cron.daily/logrotate

# 권한 수정
sudo chmod 755 /etc/cron.daily/logrotate
sudo chown root:root /etc/cron.daily/logrotate

원인 4: 설정 파일 권한이 너무 넓음 — 그룹/타인이 쓸 수 있는 설정 파일은 logrotate가 보안 이유로 거부합니다:

로컬 터미널
# logrotate는 보안상 world-writable 설정 파일을 거부함
ls -la /etc/logrotate.d/myapp
# -rw-rw-rw- 1 root root ... ← world-writable이면 logrotate가 무시!

# 올바른 권한으로 수정
sudo chmod 644 /etc/logrotate.d/myapp
sudo chown root:root /etc/logrotate.d/myapp

원인 5: SELinux / AppArmor 차단 — SELinux가 logrotate의 파일 접근이나 postrotate 실행을 막을 수 있습니다:

로컬 터미널
# SELinux 관련 거부 로그 확인 (RHEL 계열)
sudo ausearch -m avc -ts recent | grep logrotate

# SELinux 임시 비활성화 테스트 (트러블슈팅용)
sudo setenforce 0
sudo logrotate -f /etc/logrotate.conf
sudo setenforce 1

종합 진단 스크립트 — logrotate 미실행 원인을 자동으로 점검하는 진단 스크립트입니다:

로컬 터미널
#!/bin/bash
echo "=== logrotate 진단 ==="
echo ""
echo "[1] logrotate 바이너리"
which logrotate && logrotate --version

echo ""
echo "[2] cron 데몬 상태"
systemctl is-active cron 2>/dev/null || systemctl is-active crond 2>/dev/null

echo ""
echo "[3] /etc/cron.daily/logrotate 권한"
ls -la /etc/cron.daily/logrotate

echo ""
echo "[4] logrotate 상태 파일 최신 항목"
tail -5 /var/lib/logrotate/logrotate.status 2>/dev/null || \
  tail -5 /var/lib/logrotate.status 2>/dev/null

echo ""
echo "[5] 설정 파일 권한 이상 확인"
find /etc/logrotate.d/ -not -perm -o-w -ls 2>/dev/null | head -5
find /etc/logrotate.d/ -perm /o+w -ls 2>/dev/null | \
  awk '{print "WARNING: world-writable:", $NF}'

echo ""
echo "[6] 디버그 모드 실행"
logrotate -d /etc/logrotate.conf 2>&1 | tail -20

9. 실무 적용 패턴

💼
실무 맥락
현업 패턴

새 서버를 프로비저닝할 때 반드시 수행해야 할 로그 관련 설정입니다.

1. journald 영구 저장 활성화 및 용량 제한 — 재부팅 후에도 로그가 유지되도록 영구 저장을 설정합니다:

로컬 터미널
# 영구 저장 디렉터리 생성
sudo mkdir -p /var/log/journal
sudo chown root:systemd-journal /var/log/journal
sudo chmod 2755 /var/log/journal

# 보존 정책 설정
sudo tee /etc/systemd/journald.conf.d/storage-policy.conf << 'EOF'
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=500M
MaxRetentionSec=3month
RateLimitBurst=10000
EOF

sudo systemctl restart systemd-journald

2. 시스템 로그 보존 기간 설정 — 오래된 로그를 자동으로 정리하는 보존 기간을 설정합니다:

로컬 터미널
# /etc/logrotate.conf 전역 설정 확인 및 조정
sudo tee /etc/logrotate.conf << 'EOF'
# 전역 기본값
weekly
rotate 4
create
dateext
compress
include /etc/logrotate.d
EOF

3. 애플리케이션별 logrotate 설정 배포 — 각 앱의 로그 경로와 정책에 맞는 logrotate 설정을 배포합니다:

로컬 터미널
# 각 애플리케이션 배포 시 함께 배포할 logrotate 설정
sudo install -m 644 -o root -g root \
    deploy/logrotate/myapp.conf \
    /etc/logrotate.d/myapp

# 즉시 테스트
sudo logrotate -d /etc/logrotate.d/myapp

4. 디스크 모니터링 cron 추가 — 디스크 사용량이 임계값을 넘으면 알림을 보내는 cron을 설정합니다:

로컬 터미널
# 로그 디렉터리 크기 모니터링
sudo tee /etc/cron.d/log-monitor << 'EOF'
# 매일 오전 9시에 로그 디렉터리 크기 리포트
0 9 * * * root du -sh /var/log/* 2>/dev/null | sort -rh | head -10 | logger -t log-size-report
EOF

체크리스트:

  • /var/log/journal/ 디렉터리 존재 (영구 저널)
  • journald.confSystemMaxUse 설정
  • journald.confMaxRetentionSec 설정
  • /etc/logrotate.d/에 앱별 설정 파일 존재
  • logrotate 설정 파일 권한 644, 소유자 root
  • logrotate -d 디버그 모드로 설정 검증 완료
  • cron/anacron 서비스 실행 중
  • 디스크 사용률 모니터링 알림 설정
💼
실무 맥락
현업 패턴

Ansible 또는 셸 스크립트로 서버 프로비저닝 시 로그 설정을 자동화하는 패턴입니다.

Ansible Task 예시 — logrotate 설정 파일을 여러 서버에 일괄 배포하는 Ansible 태스크입니다:

YAML
# roles/log-management/tasks/main.yml

- name: journald 설정 디렉터리 생성
  file:
    path: /etc/systemd/journald.conf.d
    state: directory
    mode: '0755'

- name: journald 보존 정책 설정
  copy:
    dest: /etc/systemd/journald.conf.d/storage-policy.conf
    content: |
      [Journal]
      Storage=persistent
      SystemMaxUse={{ journald_max_use | default('2G') }}
      SystemKeepFree={{ journald_keep_free | default('500M') }}
      MaxRetentionSec={{ journald_retention | default('3month') }}
  notify: restart journald

- name: 영구 저널 디렉터리 생성
  file:
    path: /var/log/journal
    state: directory
    owner: root
    group: systemd-journal
    mode: '2755'

- name: 애플리케이션 logrotate 설정 배포
  template:
    src: logrotate-app.conf.j2
    dest: /etc/logrotate.d/{{ app_name }}
    owner: root
    group: root
    mode: '0644'

- name: logrotate 설정 검증
  command: logrotate -d /etc/logrotate.d/{{ app_name }}
  changed_when: false

logrotate 설정 템플릿 (logrotate-app.conf.j2) — 앱마다 변수만 바꿔 재사용할 수 있는 Jinja2 템플릿입니다:

/var/log/{{ app_name }}/*.log {
    daily
    rotate {{ log_rotate_count | default(30) }}
    maxsize {{ log_max_size | default('500M') }}
    compress
    delaycompress
    missingok
    notifempty
    dateext
    create 0640 {{ app_user }} {{ app_group }}
    sharedscripts
    postrotate
        /bin/systemctl kill -s USR1 {{ app_name }}.service 2>/dev/null || true
    endscript
}

배포 후 검증 스크립트 — logrotate 설정 배포 후 실제로 동작하는지 자동으로 검증합니다:

로컬 터미널
#!/bin/bash
# verify-log-setup.sh

set -euo pipefail

APP_NAME="${1:-myapp}"

echo "=== 로그 설정 검증: $APP_NAME ==="

# 1. logrotate 설정 문법 검증
echo "[1] logrotate 문법 검증..."
logrotate -d "/etc/logrotate.d/$APP_NAME" 2>&1 | \
  grep -E "(error|warning|rotating)" || echo "OK: 문법 이상 없음"

# 2. journald 상태 확인
echo ""
echo "[2] journald 디스크 사용량..."
journalctl --disk-usage

# 3. 로그 디렉터리 권한 확인
echo ""
echo "[3] 로그 디렉터리 권한..."
ls -la "/var/log/$APP_NAME/" 2>/dev/null || echo "디렉터리 없음"

# 4. cron 서비스 상태
echo ""
echo "[4] cron 서비스..."
systemctl is-active cron 2>/dev/null || systemctl is-active crond 2>/dev/null

echo ""
echo "=== 검증 완료 ==="

10. 전체 흐름 요약

💡개념

logrotate 전체 동작 흐름과 의사결정 트리

logrotate 전체 동작 흐름과 의사결정 트리

logrotate -d로 dry run을 해보면 "would rotate"가 나오는데 실제 cron에서는 돌지 않거나, 강제 실행은 되는데 자동 실행이 왜 안 되는지 파악이 안 되는 경우가 있습니다. 설정 파일을 아무리 봐도 어디서 조건을 확인하고 어느 단계에서 건너뛰는지 흐름이 보이지 않기 때문입니다. logrotate가 내부적으로 어떤 순서로 조건을 평가하는지 알면, 설정 문제인지 실행 환경 문제인지 빠르게 구분할 수 있습니다.

logrotate 실행 흐름 — logrotate가 내부적으로 처리하는 단계별 흐름을 시각화합니다:

cron.daily 또는 수동 실행
        │
        ▼
logrotate /etc/logrotate.conf 읽기
        │
        ├── /etc/logrotate.d/* 각 파일 순서대로 처리
        │
        ▼ (각 설정 블록에 대해)
조건 체크:
  ├── 마지막 실행 이후 주기(daily/weekly/monthly) 지났나?
  ├── 파일 크기가 size/maxsize 초과?
  └── 파일이 비어있지 않나? (notifempty 설정 시)
        │
        ├── 조건 미충족 → 건너뜀
        │
        └── 조건 충족 → 로테이션 실행
                │
                ├── [copytruncate 없을 때]
                │   기존 파일 이름 변경 (log → log.1, log.1 → log.2 ...)
                │   새 빈 파일 create (권한/소유자 설정)
                │   postrotate 스크립트 실행 (서비스에 신호)
                │
                ├── [copytruncate 있을 때]
                │   기존 파일을 log.1로 복사
                │   원본 파일을 0바이트로 truncate
                │   (postrotate 없어도 됨 — 데몬은 같은 fd 계속 사용)
                │
                └── compress: 이전 .1 파일을 .1.gz로 압축
                    (delaycompress 있으면 다음 로테이션 때 압축)

옵션 선택 가이드 — 서비스 재시작 가능 여부에 따라 copytruncate vs postrotate 중 선택합니다:

데몬이 로그 파일 재오픈을 지원하나?
  ├── YES (SIGUSR1, SIGHUP, graceful reload 지원)
  │   └── postrotate 스크립트에서 신호 전송 → 일반 rename 방식
  │
  └── NO (재시작 없이는 파일을 계속 참조)
      ├── 잠깐의 로그 유실이 허용되나?
      │   ├── YES → copytruncate 사용
      │   └── NO  → 서비스 재시작을 postrotate에 포함 (중단 감수)
      └── 로그 파일을 직접 쓰지 않고 소켓/파이프 사용?
          └── journald + 서비스 유닛으로 로그 관리 위임 권장

주요 옵션 조합 패턴 — 자주 쓰는 상황별 옵션 조합을 정리했습니다:

상황권장 옵션 조합
Nginx, HAProxy 등 시그널 지원compress, delaycompress, postrotate (kill -USR1)
레거시 데몬 (재시작 불가)copytruncate, compress
고트래픽 빠르게 커지는 로그maxsize 500M, daily, rotate 14
감사 로그 (장기 보관)monthly, rotate 24, compress, dateext
개발 환경 (최소 보관)daily, rotate 3, compress, missingok

journald vs logrotate 역할 분리 — 두 도구의 역할 분담을 이해해야 로그를 중복 설정하지 않습니다:

항목journaldlogrotate
대상systemd 서비스의 stdout/stderr파일로 직접 쓰는 로그
설정 위치/etc/systemd/journald.conf/etc/logrotate.d/
보존 정책MaxRetentionSec, SystemMaxUserotate N, size, maxsize
조회 방법journalctlcat, less, grep
압축내장 (Compress=yes)compress 옵션

💡개념

logrotate 실무 함정 — state 파일 손상·동시 실행·postrotate 선택

logrotate 실무 함정 3가지 — state 손상·동시 실행·postrotate

logrotate를 설정하고 logrotate -f로 강제 실행했을 때는 잘 됐는데, cron에서 자동으로 돌 때는 왜인지 안 됩니다. 또는 로테이션은 됐는데 Nginx 로그 파일이 계속 이전 파일에 쌓이고 있습니다. 이런 증상에는 몇 가지 공통적인 함정이 있습니다. state 파일이 손상되어 실행 이력을 못 읽는 경우, cron에서 다른 사용자로 실행되어 파일 권한이 안 맞는 경우, postrotate 스크립트에서 서비스 신호(USR1)를 잘못 보낸 경우입니다. 운영 환경에서 logrotate가 조용히 실패하는 패턴들을 정리합니다.

logrotate state 파일 손상 복구:

logrotate는 /var/lib/logrotate/status에 마지막 실행 이력을 기록합니다. 이 파일이 손상되면 로테이션이 중복 실행되거나 건너뛰어집니다.

로컬 터미널
# state 파일 확인
cat /var/lib/logrotate/status

# 손상 여부 확인 (파싱 에러)
logrotate -d /etc/logrotate.conf 2>&1 | grep -i "error\|cannot"

# 복구: state 파일 삭제 후 재실행 (다음 실행 시 재생성됨)
sudo rm /var/lib/logrotate/status
sudo logrotate -f /etc/logrotate.conf

# 동시 실행 방지 확인 (cron에서 실행될 때)
# /etc/cron.daily/logrotate 내부에 flock 사용 여부 확인
cat /etc/cron.daily/logrotate | grep flock

postrotate: systemctl reload vs kill -HUP 선택 기준 — 두 방법의 동작 차이와 어떤 상황에 쓸지 설명합니다:

로컬 터미널
# 서비스별 올바른 로그 파일 재오픈 방법

# Nginx — SIGUSR1 권장 (graceful log reopen)
postrotate
    /bin/kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || true
endscript

# Apache — graceful 재시작
postrotate
    /usr/sbin/apachectl graceful
endscript

# rsyslog — SIGHUP (설정 리로드 + 파일 재오픈)
postrotate
    /usr/bin/kill -HUP $(cat /var/run/rsyslogd.pid 2>/dev/null) 2>/dev/null || true
endscript

# systemd 관리 서비스 — systemctl reload (권장)
postrotate
    systemctl reload nginx 2>/dev/null || true
endscript

systemctl reload vs kill -HUP 비교 — 각 방법의 장단점과 적합한 상황을 비교합니다:

방법장점단점
systemctl reload서비스 등록 여부 확인, 실패 시 에러 반환서비스가 reload를 지원해야 함
kill -HUP $(cat pid)빠름, PID 파일이 있으면 작동PID 파일이 없으면 실패, 보안 이슈
pkill -HUP nginx이름으로 찾으므로 PID 파일 불필요여러 프로세스가 있으면 모두에 전송
💡개념

journald vs /var/log 파일 — 어디에 무엇을 보관할지 정책 가이드

journald vs /var/log 파일 보관 정책 가이드

새 서비스를 배포할 때 로그를 journald에 맡길지, 직접 파일로 쓸지 결정해야 합니다. systemd 서비스로 등록하면 stdout이 자동으로 journald에 수집되지만, Elastic Stack으로 로그를 보낼 때나 logrotate로 보존 정책을 직접 제어해야 할 때는 파일이 유리합니다. 팀마다 "journald에 다 넣어" vs "파일로 써야 편해"로 의견이 갈리는데, 로그 유형별로 기준을 정해두면 서비스마다 같은 고민을 반복하지 않아도 됩니다.

언제 journald, 언제 /var/log 파일을 사용할지 — 로그 유형별로 적합한 저장 방식을 선택하는 기준입니다:

로그 유형권장 저장 방식이유
systemd 서비스 stdout/stderrjournald자동 수집, 구조화된 메타데이터
애플리케이션 자체 로그 파일/var/log/앱명/로그 포맷 제어, 외부 도구 연동
보안 감사 로그/var/log/audit/ (auditd)별도 권한, 장기 보관
시스템 부팅/커널 로그journald (영구화)dmesg 이력 보존
중앙 집중 로그rsyslog/Fluentd → ELK/Loki여러 서버 통합
로컬 터미널
# journald 영구화 설정 (재부팅 후에도 보존)
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

# journald 로그를 /var/log 파일로 포워딩 (하이브리드)
# /etc/systemd/journald.conf
# ForwardToSyslog=yes  # rsyslog로 전달

# rsyslog가 파일로 기록
# /etc/rsyslog.d/50-default.conf 에서 /var/log/syslog 등으로 기록

# 검색: journalctl vs grep
journalctl -u nginx --since "1 hour ago"  # 구조화 검색 (빠름)
grep "ERROR" /var/log/nginx/error.log     # 전통적 방법

마무리

로그 관리는 시스템이 조용할 때 설정해두어야 장애 상황에서 빛을 발합니다. 디스크 풀 상황에서 허겁지겁 로그를 지우는 것이 아니라, 처음부터 logrotatejournald 정책을 올바르게 설정하면 로그는 자동으로 통제됩니다.

핵심 원칙:

  • 모든 서비스 로그에는 반드시 logrotate 설정이 있어야 합니다
  • postrotatecopytruncate의 차이를 이해하고 상황에 맞게 선택하세요
  • journald는 영구 저장 + 용량 제한을 반드시 설정하세요
  • 설정 배포 후에는 항상 logrotate -d로 검증하세요
  • 디스크 사용률 모니터링과 임계값 알림을 병행하세요

다음 모듈에서는 journalctl 고급 필터와 구조화 필드 쿼리를 사용해 장애 시점의 로그를 정밀하게 추적하고 포스트모텀 보고서를 작성하는 방법을 다룹니다.

지식 확인

퀴즈 — 5문제

Q1

`logrotate`에서 `rotate 7` 디렉티브의 의미는?

Q2

`logrotate` 설정에서 `compress`와 함께 사용하면 새로 rotate된 파일은 바로 압축하지 않고 한 사이클 뒤에 압축하는 디렉티브는?

Q3

`/etc/logrotate.d/` 디렉터리의 역할은?

Q4

logrotate 설정 중 `size 50M` 디렉티브의 올바른 작동 방식은?

Q5

`logrotate`의 `size 100M` 디렉티브와 `daily` 디렉티브를 함께 사용할 때의 동작은?

0 / 5 답변

🧪 실습으로 확인하기

/var 파티션 100% 장애 — 원인 분석과 복구

중급

운영 서버 /var 파티션이 100% 찼다. 서비스가 죽어있다. df/du로 원인 경로를 좁히고, find로 대형 파일을 탐지하고, truncate와 journalctl --vacuum으로 공간을 확보한다. lsof로 삭제된 파일 핸들까지 잡아낸다.

45📋 6단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

linux중급 · 60
[Linux] OOM Killer 방지를 위한 Swap 메모리 파티션 튜닝
Linux 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점