대상 OS: Debian 12 (bookworm)
장애가 났을 때 제일 먼저 필요한 건 ‘멋진 툴’보다 “지금 이 서버에서 무슨 일이 일어났는지”를 빠르게 재구성하는 능력입니다.
로그가 흩어져 있으면 팀은 추측으로 싸우게 되고, 추측은 보통 더 큰 변경을 부릅니다.
Debian 12의 systemd+journald는 생각보다 강력해서, 그냥 쌓아두는 저장소가 아니라 ‘질문’에 답하는 데이터베이스처럼 쓸 수 있습니다.
오늘은 journalctl을 실무에서 바로 먹히는 패턴으로 정리합니다.
목표는 “로그를 보는 시간”이 아니라 “원인을 찾는 시간”을 줄이는 겁니다.
오늘의 목표: ‘필터 3개’로 탐색 범위를 1/100로 줄이기
대부분의 트러블슈팅은 아래 3가지만 잡아도 속도가 확 달라집니다.
1) 시간 범위(언제부터 언제까지?)
2) 범위(어느 서비스/커널/사용자 세션?)
3) 중요도(에러만? 경고까지?)
이 3개를 journalctl에서 한 번에 묶는 습관을 들이면 “일단 tail -f”로 시작하는 운이 크게 줄어듭니다.
기본 준비: 내 journald가 ‘어디까지’ 가지고 있는지 확인
먼저 현재 저장 상태부터 봅니다.
journalctl --disk-usage
journalctl --list-boots | head
- `--disk-usage`는 용량/보존이 적절한지 판단하는 1차 지표입니다.
- `--list-boots`는 “재부팅 전후”로 사건을 나눌 수 있게 해줍니다.
부팅 목록이 너무 짧게 나오면(예: -1, 0 정도만 유지) 디스크에 영구 저장이 꺼져 있을 가능성이 있습니다.
로그를 ‘타임라인’으로 자르기: since/until, 그리고 상대 시간
사건의 창이 좁아질수록 원인 후보가 줄어듭니다.
1) 특정 시각부터 30분만 보기
journalctl --since '2026-02-25 11:00' --until '2026-02-25 11:30'
2) “최근 20분”만 보기(현장 대응에서 제일 자주 씁니다)
journalctl --since '-20 min'
3) 시간순(오래된 것부터)로 재생
journalctl --since '-30 min' -o short-iso --no-pager
journalctl --since '-30 min' -o short-iso --no-pager --reverse | tail -n 200
- `--reverse`는 “지금 막 터진 것부터” 훑는 데 좋고,
- 정방향은 “앞뒤 관계”를 볼 때 좋습니다.
부팅 단위로 자르기: “이번 부팅에서만” 발생했나?
재부팅 이후에만 나타난 장애인지, 이전부터 쌓이던 문제인지부터 나누면 절반은 해결됩니다.
1) 부팅 목록 확인
journalctl --list-boots
2) 현재 부팅(0)만 보기
journalctl -b 0
3) 직전 부팅(-1)과 비교
journalctl -b -1 -p warning
journalctl -b 0 -p warning
4) 특정 부팅 ID로 보기(재부팅이 잦은 환경에서 유용)
journalctl -b 7a2c3f0a7d3c4d3a9b2b8f3a0a0c1111
서비스(유닛) 단위로 좁히기: -u, 그리고 “연관 유닛”까지
서비스 하나만 보는 것보다, 연관 유닛까지 같이 보는 습관이 중요합니다.
1) 유닛 하나만
journalctl -u nginx --since '-2 h'
2) 유닛 여러 개를 한 번에(웹+앱 조합)
journalctl -u nginx -u php8.2-fpm --since '-2 h'
3) 특정 유닛의 ‘이번 부팅’만
journalctl -u nginx -b 0
4) 유닛의 최근 실패 원인만 빠르게
systemctl status nginx --no-pager
systemctl show -p ExecMainStatus -p Result -p ActiveEnterTimestamp nginx
Tip: “nginx가 죽었다”는 결과일 뿐, 원인이 아닐 때가 많습니다. 예를 들어 다음을 같이 확인하면 연결고리를 더 빨리 찾습니다.
- 리버스 프록시라면: `nginx` + `systemd-resolved`(DNS) + 앱 유닛
- 스토리지 이슈면: 해당 마운트 유닛, 커널 메시지
커널 메시지를 따로 뽑기: dmesg 대신 journalctl -k
커널 로그는 원인에 가까운 단서가 있을 때가 많습니다(디스크, NIC, OOM 등).
1) 이번 부팅의 커널 로그
journalctl -k -b 0
2) 에러/경고만
journalctl -k -b 0 -p warning
3) 특정 키워드로 좁히기(예: NVMe, EXT4, OOM)
journalctl -k -b 0 | grep -E 'nvme|ext4|oom|i/o error|reset'
중요도 우선순위: -p 로 “읽을 만한 것만” 남기기
현장은 로그가 너무 많아서 문제입니다. 급할 때는 우선순위를 강제하세요.
1) 에러 이상만 보기
journalctl -p err --since '-1 h'
2) 경고 이상만 보기(장애 전조 포함)
journalctl -p warning --since '-6 h'
3) 커널 + 경고 이상
journalctl -k -p warning -b 0
출력 형식과 ‘필드’ 활용: short-iso, json, 그리고 grep 지양
grep은 편하지만, 구조화된 필드를 쓰면 훨씬 정확해집니다.
1) 사람이 읽기 편한 ISO 시간
journalctl -o short-iso --since '-1 h'
2) 핵심 필드만 뽑고 싶을 때(json 출력 후 필터링)
journalctl -u nginx --since '-1 h' -o json
json은 파이프라인으로 가공하기 좋지만, 이 글에서는 ‘bash만’ 쓰는 규칙이 있으니 여기서는 출력만 보여줍니다.
실무에서는 `_PID`, `_COMM`, `SYSLOG_IDENTIFIER`, `MESSAGE`, `_SYSTEMD_UNIT` 같은 필드가 특히 유용합니다.
현장 케이스 3개: 자주 만나는 장애를 journalctl로 빨리 끝내기
# 케이스 A) “서비스가 재시작 루프”에 빠졌다
1) 해당 유닛 로그를 최근 10분만
journalctl -u myapp --since '-10 min'
2) systemd가 죽였는지(Exit code), 앱이 죽었는지(시그널) 확인
systemctl status myapp --no-pager
systemctl show -p ExecMainCode -p ExecMainStatus -p ExecMainExitTimestamp myapp
3) 의존성(예: DB, 네트워크)이 있는 경우 동시에 보기
journalctl -u myapp -u network-online.target --since '-30 min'
# 케이스 B) “디스크 I/O가 튄다 / 파일시스템이 읽기 전용이 됐다”
1) 커널 로그에서 I/O error, remount-ro 같은 흔적 찾기
journalctl -k -b 0 -p warning | grep -E 'I/O error|Buffer I/O|reset|remount-ro|EXT4-fs error'
2) 해당 시간대의 사용자 공간(서비스) 로그도 함께
journalctl --since '-2 h' -p warning | grep -E 'read-only|Input/output error'
# 케이스 C) “갑자기 느려졌다 / 프로세스가 죽는다(의심: OOM)”
1) 커널 OOM 흔적부터
journalctl -k --since '-6 h' | grep -E 'Out of memory|Killed process|oom-killer'
2) systemd-oomd가 개입했는지(사용 중인 경우)
journalctl -u systemd-oomd --since '-6 h'
이렇게 보면 ‘누가 죽었는지’는 바로 나오고, 다음 단계로는 해당 서비스의 `MemoryMax` 같은 리소스 제한이나 실제 메모리 누수 여부를 확인하게 됩니다.
보존 정책(운영 팁): 로그가 ‘사라져서’ 장애가 커지지 않게
journald는 기본 설정에서 로그가 휘발성(메모리)일 수 있고, 디스크 보존도 환경마다 다릅니다.
장애를 재현 못 하는 유형일수록 “최소 며칠”은 남겨두는 게 이득입니다.
1) 영구 저장 활성화(디스크)
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usage
2) 용량 상한을 보수적으로 잡기(예: 1G)
sudo sed -i 's/^#\?SystemMaxUse=.*/SystemMaxUse=1G/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
3) 레이트리밋(로그 폭주)로 ‘원인 로그’가 묻히는 상황 완화
sudo sed -i 's/^#\?RateLimitIntervalSec=.*/RateLimitIntervalSec=30s/' /etc/systemd/journald.conf
sudo sed -i 's/^#\?RateLimitBurst=.*/RateLimitBurst=1000/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
운영 포인트:
- 너무 빡세게 제한하면 필요한 로그가 드롭될 수 있습니다.
- 반대로 제한이 없으면 폭주 상황에서 디스크를 잡아먹어 2차 장애가 날 수 있습니다.
작은 체크리스트: 장애 대응에서 바로 쓰는 커맨드 세트
1) 최근 20분, 에러 이상
journalctl --since '-20 min' -p err --no-pager
2) 이번 부팅, 커널 경고 이상
journalctl -k -b 0 -p warning --no-pager
3) 핵심 유닛 2~3개 묶어서
journalctl -u nginx -u myapp --since '-1 h' --no-pager
4) 재부팅 전후 비교
journalctl -b -1 -p warning --no-pager | tail -n 200
journalctl -b 0 -p warning --no-pager | tail -n 200
여기까지 익숙해지면 “로그가 너무 많아서 못 보겠다”가 아니라 “필터를 못 걸어서 못 본다”로 문제가 바뀝니다.
필터를 잘 걸면, 로그는 생각보다 친절합니다.