Published on

Linux journalctl 로그 폭주로 디스크 100% 찰 때

Authors

서버가 멀쩡히 돌다가 갑자기 디스크 사용률이 100%가 되고, 애플리케이션이 파일을 못 쓰면서 장애가 나는 케이스가 있습니다. 그중 흔한 원인이 systemd-journald 저널 로그 폭주입니다. 특히 에러 루프가 난 서비스가 초당 수천 줄을 뿜거나, 커널/드라이버 로그가 폭발하면 /var/log/journal 아래의 저널 파일이 순식간에 커집니다.

이 글에서는 (1) 지금 당장 디스크를 살리는 응급처치, (2) 어떤 서비스가 로그를 폭주시켰는지 추적, (3) 다시는 같은 일이 안 나게 journald 보존 정책을 튜닝하는 흐름으로 정리합니다.

장애 상황에서 로그 폭주는 종종 메모리/프로세스 문제와 같이 옵저버빌리티 신호로 동반됩니다. OOM으로 재시작 루프가 돌며 로그가 폭발하는 패턴도 많으니, 필요하면 리눅스 OOM Killer로 프로세스 죽음 진단·방지도 함께 보세요.

1) 증상 확인: 정말 journald가 디스크를 먹고 있나

먼저 디스크가 어느 마운트에서 찼는지 확인합니다.

df -h

대부분 루트(/)나 /var가 꽉 차며, 저널은 보통 아래 위치 중 하나에 있습니다.

  • 영구 저장(기본): /var/log/journal
  • 휘발 저장: /run/log/journal

저널 자체 크기는 journalctl로 빠르게 확인 가능합니다.

sudo journalctl --disk-usage

또는 디렉터리 단위로 상위 사용량을 확인합니다.

sudo du -sh /var/log/* | sort -h
sudo du -sh /var/log/journal/* 2>/dev/null | sort -h | tail

여기서 system.journal 또는 해시처럼 생긴 디렉터리(머신 ID) 아래 파일이 크면 journald가 범인일 확률이 큽니다.

2) 응급처치: 디스크 공간을 즉시 확보하는 방법

장애 대응에서는 “원인 규명”보다 “서비스 복구”가 먼저인 경우가 많습니다. 아래 순서대로 진행하면 안전합니다.

2-1) journald에 보존 축소(즉시 적용)

가장 먼저, 저널을 일정 기간/크기만 남기고 잘라냅니다.

  • 기간 기준 삭제:
sudo journalctl --vacuum-time=2d
  • 크기 기준 삭제:
sudo journalctl --vacuum-size=500M
  • 파일 개수 기준 삭제:
sudo journalctl --vacuum-files=10

이 명령은 저널 파일을 “정상적인 방식으로” 정리하므로, 급한 상황에서 우선 선택하기 좋습니다.

2-2) 로그 폭주가 현재 진행 중이면, 원인 서비스부터 멈추기

계속 쓰고 있으면 vacuum이 따라잡지 못합니다. 의심 서비스가 있다면 일단 중지하고 공간을 확보하세요.

sudo systemctl stop your-service
sudo systemctl status your-service --no-pager

원인 서비스가 아직 불명확하면, 다음 섹션의 “폭주 유닛 찾기”를 먼저 수행한 뒤 멈추는 게 좋습니다.

2-3) 최후의 수단: 저널 디렉터리 정리(주의)

정말 급해서 vacuum이 안 먹히거나, 저널 파일이 깨져서 정리가 안 되는 경우가 있습니다. 이때는 journald를 멈추고 파일을 정리합니다.

sudo systemctl stop systemd-journald
sudo rm -f /var/log/journal/*/*.journal /var/log/journal/*/*.journal~ 2>/dev/null
sudo systemctl start systemd-journald

주의 사항:

  • 운영 중인 장애 분석에 필요한 로그까지 삭제될 수 있습니다.
  • 가능하면 먼저 journalctl --disk-usagejournalctl -S로 필요한 구간을 다른 곳으로 덤프해두세요.

예: 최근 1시간을 파일로 저장

sudo journalctl -S "1 hour ago" -o short-iso > /tmp/journal-last-1h.log

3) 원인 추적: 어떤 유닛/프로세스가 로그를 폭주시켰나

디스크를 비웠다면, 다음 장애를 막기 위해 “누가 로그를 쏟았는지”를 찾아야 합니다.

3-1) 최근 로그에서 특정 유닛이 반복되는지 확인

가장 단순한 방법은 최근 구간을 보고 같은 메시지가 반복되는지 확인하는 것입니다.

sudo journalctl -S "30 min ago" -o short-iso | tail -n 200

특정 유닛 로그만 보려면:

sudo journalctl -u your-service -S "2 hours ago" -o short-iso

부팅 단위로도 확인 가능합니다.

sudo journalctl -b -0 -S "1 hour ago" -o short-iso

3-2) “가장 많이 말한 유닛”을 통계로 찾기

저널에서 유닛 필드를 뽑아 카운트하면 폭주 유닛이 바로 드러나는 경우가 많습니다.

sudo journalctl -S "1 hour ago" -o json | \
  jq -r '._SYSTEMD_UNIT // "(no-unit)"' | \
  sort | uniq -c | sort -nr | head

jq가 없으면 설치하거나, 임시로 짧은 구간을 grep/awk로 처리할 수도 있지만 JSON 파싱이 가장 정확합니다.

3-3) 커널 로그 폭주 여부 확인

드라이버/네트워크/파일시스템 오류가 반복되면 커널 로그가 폭발합니다.

sudo journalctl -k -S "1 hour ago" -o short-iso | tail -n 200

여기서 같은 에러가 반복된다면, journald 튜닝만으로는 근본 해결이 안 됩니다. 장치/커널 파라미터/드라이버/네트워크 문제를 같이 봐야 합니다.

3-4) 재시작 루프(크래시 루프) 확인

서비스가 죽고 systemd가 계속 재시작하면, 시작/종료 로그가 폭발합니다.

sudo systemctl status your-service --no-pager
sudo systemctl show your-service -p NRestarts

재시작 루프의 원인이 메모리 부족(OOM)인 경우도 흔합니다. 이 경우 journald만 줄여도 재발하므로, OOM 원인을 함께 잡아야 합니다.

4) 재발 방지: journald 보존 정책을 “의도적으로” 제한하기

응급처치로 --vacuum-*를 해도, 설정이 그대로면 또 찹니다. journald 설정 파일에서 상한을 걸어야 합니다.

설정 파일은 보통 다음 중 하나입니다.

  • /etc/systemd/journald.conf
  • /etc/systemd/journald.conf.d/*.conf

권장 방식은 드롭인 파일을 추가하는 것입니다.

sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/99-limit-journal.conf > /dev/null <<'EOF'
[Journal]
SystemMaxUse=1G
SystemKeepFree=2G
RuntimeMaxUse=200M
MaxRetentionSec=7day
Compress=yes
EOF

설정 의미(핵심만):

  • SystemMaxUse: /var/log/journal 전체 상한
  • SystemKeepFree: 파일시스템에 이만큼 여유를 남기도록 유도(디스크 100% 방지에 유용)
  • RuntimeMaxUse: /run/log/journal 상한
  • MaxRetentionSec: 시간 기준 보존
  • Compress: 압축 저장(대개 켜는 편이 이득)

적용:

sudo systemctl restart systemd-journald
sudo journalctl --disk-usage

4-1) 영구 저장이 꼭 필요 없으면 Storage를 volatile로

장애 분석 정책상 저널을 장기 보관할 필요가 없고, 별도 로그 수집(예: Fluent Bit, Vector, CloudWatch, ELK 등)을 쓴다면 휘발로 돌리는 것도 방법입니다.

sudo tee /etc/systemd/journald.conf.d/10-storage.conf > /dev/null <<'EOF'
[Journal]
Storage=volatile
EOF

sudo systemctl restart systemd-journald

이러면 재부팅 시 저널이 사라지므로, 운영 정책과 맞는지 반드시 확인해야 합니다.

4-2) rate limit(속도 제한) 이해하기

journald에는 기본적으로 rate limit이 있습니다. 다만 “디스크가 차는 문제”는 rate limit만으로 완전히 막기 어렵습니다.

  • 로그가 다양한 유닛에서 분산되면 제한이 잘 안 느껴질 수 있음
  • rate limit으로 드롭된 로그가 생기면, 장애 원인 분석이 더 어려워질 수 있음

그래서 실무에서는 (1) 상한 설정(SystemMaxUse/KeepFree) 을 기본으로 하고, (2) 폭주 서비스 자체를 수정하는 접근이 가장 안정적입니다.

5) 폭주 로그를 만드는 애플리케이션/서비스 측 개선 포인트

디스크를 살리고 journald 상한을 걸었어도, 근본 원인이 남아 있으면 다음 장애로 이어집니다.

5-1) 동일 에러 무한 반복(타이트 루프) 제거

대표 패턴:

  • 외부 의존성 실패(예: DB 다운, API 타임아웃) 시 즉시 재시도
  • 예외 스택트레이스를 매번 출력
  • 설정 오류로 매 요청마다 경고 로그 출력

해결:

  • 지수 백오프와 지터 적용
  • 동일 메시지 샘플링/집계
  • “요청 단위 에러 로그”와 “상태 변화 로그”를 분리

예를 들어 네트워크 오류 재시도 패턴은 로그 폭주로 직결됩니다. Node.js 환경에서 연결 리셋/타임아웃이 반복될 때의 원인 접근은 Node.js fetch ECONNRESET·ETIMEDOUT 해결법도 참고할 만합니다.

5-2) systemd 서비스 설정에서 재시작 정책 점검

Restart=always가 무조건 나쁜 건 아니지만, 크래시 루프에서는 로그 폭주+자원 고갈을 같이 부릅니다.

점검 포인트:

  • Restart=on-failure로 충분한가
  • RestartSec=5s 등 재시작 간격을 늘릴 수 있는가
  • StartLimitIntervalSecStartLimitBurst로 재시작 폭주를 차단할 수 있는가

드롭인 예시:

sudo systemctl edit your-service

아래 내용 추가:

[Service]
Restart=on-failure
RestartSec=5s

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

적용:

sudo systemctl daemon-reload
sudo systemctl restart your-service

6) 운영 체크리스트: 다음엔 “장애 전”에 감지하기

  • journalctl --disk-usage를 주기적으로 수집(노드 단위 메트릭)
  • /var 또는 / 파일시스템 사용률 알람(예: 80%, 90%)
  • 특정 유닛의 로그량 급증 탐지(로그 파이프라인에서 카운트)
  • 크래시 루프 탐지: NRestarts 증가 알람

디스크가 꽉 차면 DB, 컨테이너 런타임, 패키지 업데이트 등 연쇄적으로 문제가 커집니다. 특히 컨테이너 노드에서는 이미지 레이어/로그/저널이 동시에 자라며 더 빨리 장애가 납니다.

7) 정리: 가장 안전한 해결 순서

  1. df -h로 꽉 찬 마운트 확인
  2. journalctl --disk-usage로 저널 크기 확인
  3. 폭주가 진행 중이면 원인 유닛을 찾아 systemctl stop
  4. journalctl --vacuum-size 또는 --vacuum-time으로 공간 확보
  5. /etc/systemd/journald.conf.d/*.confSystemMaxUse, SystemKeepFree, MaxRetentionSec 설정
  6. 폭주를 만든 애플리케이션/서비스의 재시도/로그 정책 및 systemd 재시작 정책 개선

이 순서대로 하면 “당장 디스크를 살리면서”도 “재발 방지”까지 같이 가져갈 수 있습니다.