리눅스 로그 보기: journalctl, /var/log, rsyslog, logrotate 정리
최신 리눅스 배포판에서는 journald가 로그를 모으고 journalctl 명령으로 읽습니다. journalctl -u nginx는 서비스 하나의 기록을, -f는 실시간 스트림을 보여 주며, --since와 -p로 시간과 중요도를 거릅니다. 텍스트 로그는 /var/log에 남고, rsyslog가 기록하거나 전달하며, logrotate가 오래된 파일을 압축하고 삭제해 디스크가 차는 것을 막습니다.
서비스가 밤에 멈췄다면 원인은 거의 항상 로그에 남아 있습니다. 문제는 어디서 찾느냐입니다. Ubuntu, Debian, RHEL, Fedora, Arch 같은 최신 배포판에서는 journald가 이벤트를 수집하고 journalctl로 읽습니다. 많은 프로그램은 여전히 /var/log에 일반 텍스트 파일을 쓰고, 그 크기는 logrotate가 관리합니다. 아래에서는 journalctl로 로그를 읽는 실제 방법, 나머지 로그의 위치, 로테이션 중에 중요한 정보를 잃지 않는 방법을 설명합니다.
리눅스 로그는 어디에 있나
로그의 출처는 세 가지입니다. 지도처럼 기억해 두면 편합니다.
- journald — systemd의 바이너리 로그입니다. 모든 서비스의 stdout과 stderr, 커널 메시지, syslog를 받습니다.
/var/log/journal/(영구 저장) 또는/run/log/journal/(재부팅 전까지)에 저장됩니다. - /var/log — 텍스트 파일입니다.
rsyslog(예:/var/log/syslog,/var/log/auth.log)나 nginx, PostgreSQL, MySQL 같은 애플리케이션이 직접 씁니다. - 커널 링 버퍼 — 드라이버와 하드웨어 메시지입니다.
dmesg또는journalctl -k로 봅니다.
| 파일 | 내용 |
|---|---|
/var/log/syslog (Debian/Ubuntu) | 시스템 전체의 일반 로그 |
/var/log/messages (RHEL/Fedora) | Red Hat 계열에서 같은 역할 |
/var/log/auth.log 또는 /var/log/secure | 로그인, sudo, SSH |
/var/log/kern.log | 커널 메시지 |
/var/log/dpkg.log, /var/log/apt/ | 패키지 설치 기록 |
/var/log/nginx/ | 웹 서버의 access.log와 error.log |
/var/log/wtmp, /var/log/btmp | 로그인 기록(바이너리). last와 lastb로 읽음 |
/var/log의 대부분 파일은 root와 adm 그룹만 읽을 수 있으므로, 읽는 명령은 종종 sudo로 실행해야 합니다.
journalctl 기본 명령
인자 없이 journalctl을 실행하면 가장 오래된 기록부터 전체 journal이 less로 열립니다. 실제로는 거의 쓰지 않고, 보통 범위를 바로 좁힙니다.
journalctl # 전체 journal, 오래된 것부터
journalctl -e # 끝으로 바로 이동
journalctl -n 50 # 마지막 50줄
journalctl -r # 역순, 최신이 위로
journalctl --no-pager # less 없이 출력. 스크립트나 grep에 적합
페이저 안에서는 less의 키를 그대로 쓸 수 있습니다. /는 검색, G는 끝으로, q는 종료입니다. 긴 줄은 화면 너비에서 잘리며, 좌우 화살표로 스크롤합니다.
특정 서비스의 로그: journalctl -u
가장 흔한 상황은 systemd 유닛 하나가 무엇을 기록했는지 보는 것입니다. 파일을 찾아 grep할 필요가 없습니다.
$ sudo journalctl -u nginx -n 20
Oct 14 03:12:41 web1 systemd[1]: Starting nginx.service - A high performance web server...
Oct 14 03:12:41 web1 nginx[1834]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Failed with result 'exit-code'.
-u는 여러 번 쓸 수 있습니다. journalctl -u nginx -u php8.2-fpm은 두 서비스의 기록을 시간순으로 섞어 보여 주므로, 두 서비스 사이에서 오류가 생길 때 편합니다. 사용자 유닛(systemctl --user)은 journalctl --user -u 이름으로 봅니다.
실시간 로그: journalctl -f
sudo journalctl -u nginx -f
tail -f와 같은 역할입니다. 마지막 10개의 기록을 보여 준 뒤 새 기록을 기다립니다. 서비스를 재시작하거나 오류를 재현할 때 다른 터미널에 띄워 두면 좋습니다. 종료는 Ctrl+C입니다.
시간과 부팅 기준으로 거르기
장애가 난 시각을 알고 있다면 --since와 --until로 시간 범위를 지정하십시오. 정확한 날짜와 상대적인 표현을 모두 쓸 수 있습니다.
journalctl --since "2026-10-14 02:50" --until "2026-10-14 03:30"
journalctl --since "1 hour ago"
journalctl --since yesterday --until today
journalctl -u nginx --since "10 min ago"
또 다른 유용한 기준은 시스템의 부팅 단위입니다. 서버가 스스로 재부팅했다면, 원인은 이전 세션의 끝에서 찾습니다.
$ journalctl --list-boots
-2 8f1c... Sat 2026-10-12 09:01:12 UTC Sun 2026-10-13 22:47:03 UTC
-1 a7d2... Sun 2026-10-13 22:48:30 UTC Tue 2026-10-14 03:05:51 UTC
0 c3e9... Tue 2026-10-14 03:07:02 UTC Tue 2026-10-14 09:15:40 UTC
$ journalctl -b -1 -e # 직전 부팅의 끝부분
$ journalctl -b # 현재 부팅만
부팅 목록은 journal이 디스크에 저장될 때만 볼 수 있습니다. 설정 방법은 아래 journald 설정 절에서 설명합니다.
중요도로 거르기: journalctl -p
각 기록에는 0부터 7까지의 syslog 우선순위가 있습니다. -p는 지정한 수준과 그보다 중요한 기록을 보여 줍니다.
| 코드 | 이름 | 사용 시점 |
|---|---|---|
| 0 | emerg | 시스템 사용 불가 |
| 1 | alert | 즉시 조치 필요 |
| 2 | crit | 치명적인 오류 |
| 3 | err | 오류 |
| 4 | warning | 경고 |
| 5 | notice | 정상이지만 주목할 만한 이벤트 |
| 6 | info | 정보 메시지 |
| 7 | debug | 디버그 |
journalctl -p err -b # 이번 부팅 이후의 모든 오류
journalctl -p warning..err -u ssh # 특정 서비스에서 수준 범위 지정
journalctl -p err -b은 재부팅이나 업데이트 후 가장 먼저 해 볼 점검입니다. 무엇이 잘못되었는지 몇 초 만에 보여 줍니다.
검색과 기타 필터
journald는 각 기록마다 구조화된 필드를 저장하므로, grep 없이도 필드로 거를 수 있습니다.
journalctl _PID=1834 # 프로세스 번호로
journalctl _COMM=sshd # 프로그램 이름으로
journalctl _UID=1000 # 사용자 ID로
journalctl /usr/sbin/sshd # 실행 파일 경로로
journalctl -k # 커널 메시지만 (dmesg와 같음)
journalctl -t CRON # syslog 식별자로
journalctl -g "Out of memory" # 텍스트 검색, 정규표현식 지원
-g(--grep) 옵션은 systemd 237부터 있습니다. 더 오래된 시스템에서는 journalctl --no-pager | grep ... 파이프라인을 쓰십시오. 서로 다른 필드의 필터는 "그리고"로 결합됩니다. 따라서 journalctl -u nginx -p err --since today는 오늘 nginx에서 발생한 오류만 보여 줍니다.
출력 형식
-o 옵션으로 형식을 바꿉니다. 자주 쓰는 형식은 다음과 같습니다.
-o short-iso— 시간을 ISO 8601 형식으로 표시. 애플리케이션 로그와 시각을 비교하기 좋습니다-o verbose—_SYSTEMD_UNIT,_PID,_BOOT_ID등 모든 필드 표시-o json또는json-pretty— 스크립트나jq로 처리할 때-o cat— 시간과 호스트 없이 메시지만
journalctl -u myapp -o json --since today | jq -r '.MESSAGE'
journald 설정: 저장 방식과 용량
설정은 /etc/systemd/journald.conf에 있습니다. 변경 사항은 /etc/systemd/journald.conf.d/*.conf에 따로 두는 편이 관리하기 쉽습니다. 핵심 옵션은 Storage입니다.
persistent—/var/log/journal에 디스크로 기록하며, 재부팅 후에도 남습니다volatile— 메모리에만,/run/log/journal에 저장합니다auto(기본값) —/var/log/journal디렉터리가 있으면 디스크에 저장합니다
일부 최소 설치 이미지에는 이 디렉터리가 없어서, 재부팅하면 기록이 사라집니다. 영구 저장을 켜려면 다음과 같이 하십시오.
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journal이 디스크를 전부 차지하지 않도록 한도를 정하십시오.
# /etc/systemd/journald.conf.d/size.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=1month
기본 SystemMaxUse는 파일 시스템 크기의 10%이며, 최대 4GB입니다.
journalctl 로그 지우기
$ journalctl --disk-usage
Archived and active journals take up 1.8G in the file system.
sudo journalctl --vacuum-size=300M # 300MB 이하로 줄이기
sudo journalctl --vacuum-time=2weeks # 2주보다 오래된 기록 삭제
sudo journalctl --rotate # 활성 파일을 닫아서 이것도 삭제 가능하게 하기
/var/log/journal의 파일을 rm으로 지우지 마십시오. vacuum은 같은 일을 안전하게 하면서 활성 파일은 건드리지 않습니다.
rsyslog: 전통적인 텍스트 로그
rsyslog는 syslog 메시지(journald에서 넘어온 것도 포함)를 받아 규칙에 따라 파일에 나눠 씁니다. 설정은 /etc/rsyslog.conf와 /etc/rsyslog.d/*.conf에 있습니다. 규칙은 출처.수준 선택자와 동작으로 구성됩니다.
# /etc/rsyslog.d/50-default.conf (일부)
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.* -/var/log/kern.log
경로 앞의 하이픈은 매 줄마다 디스크에 동기화하지 않는다는 뜻입니다. 빠르지만, 장애가 나면 마지막 몇 줄을 잃을 수 있습니다. 설정을 바꾼 뒤에는 문법을 검사하고 서비스를 재시작하십시오.
sudo rsyslogd -N1
sudo systemctl restart rsyslog
rsyslog를 journald와 함께 두는 가장 큰 이유는 로그를 중앙 서버로 전달하기 위해서입니다. 한 줄이면 모든 로그를 TCP로 보낼 수 있습니다.
# /etc/rsyslog.d/90-remote.conf
*.* @@logs.example.com:514
@ 하나는 UDP, @@ 두 개는 TCP입니다. 서버 한 대가 완전히 망가져도 로그 사본은 다른 서버에 남습니다. 스크립트에서 시스템 로그에 메시지를 남기려면 logger를 쓰십시오.
logger -t backup -p user.err "데이터베이스 백업 실패"
journalctl -t backup -n 5
logrotate: 로그 회전
텍스트 로그는 누가 잘라 주지 않으면 계속 커집니다. 이 일을 하는 것이 logrotate입니다. 하루에 한 번(cron 또는 logrotate.timer) 현재 파일의 이름을 바꾸고, 오래된 파일은 압축하며, 가장 오래된 파일은 삭제합니다. 전역 설정은 /etc/logrotate.conf에, 프로그램별 규칙은 /etc/logrotate.d/에 있습니다.
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp adm
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
| 지시어 | 의미 |
|---|---|
daily / weekly / size 100M | 회전 시점 |
rotate 14 | 보관할 오래된 파일의 개수 |
compress, delaycompress | gzip으로 압축하되, 가장 최근 아카이브는 압축하지 않음 |
missingok, notifempty | 파일이 없어도 오류 없이 넘어가고, 빈 파일은 회전하지 않음 |
create 0640 user group | 새 파일을 만들 때의 권한 |
copytruncate | 파일을 복사한 뒤 원본을 비움. 로그 파일을 다시 열지 못하는 프로그램용 |
postrotate ... endscript | 회전 후 실행할 명령. 보통 프로그램에 파일을 다시 열라고 신호를 보냄 |
규칙을 바꾸지 않고 검사하거나, 바로 회전을 실행할 수 있습니다.
sudo logrotate -d /etc/logrotate.d/myapp # 디버그 출력과 함께 시험 실행
sudo logrotate -f /etc/logrotate.d/myapp # 지금 바로 회전
흔한 실수는 postrotate나 copytruncate를 빠뜨리는 것입니다. 그러면 프로그램은 이름이 바뀐 app.log.1에 예전 디스크립터로 계속 쓰고, 새 app.log는 비어 있게 됩니다.
텍스트 로그 읽기
/var/log의 파일은 기본 도구로 충분히 읽을 수 있습니다.
sudo tail -n 100 /var/log/syslog # 마지막 줄들
sudo tail -F /var/log/nginx/error.log # 로테이션을 거쳐도 계속 추적
sudo less +G /var/log/auth.log # 끝에서 열기
sudo zgrep "Failed password" /var/log/auth.log.*.gz # 압축 아카이브 검색
sudo grep -c " 500 " /var/log/nginx/access.log # 500 응답 개수
tail -F의 대문자 F를 눈여겨보십시오. -f와 달리, logrotate가 파일을 바꿔 치기하면 파일을 다시 엽니다.
장애 분석 순서: 밤에 서비스가 멈췄을 때
"밤에 서비스가 멈췄다"는 상황은 다음 순서로 분석하면 빠릅니다.
systemctl status myapp— 현재 상태와 마지막 로그 줄.journalctl -u myapp --since "03:00" --until "03:30"— 중단 시점에 서비스가 무엇을 기록했는지.journalctl -p err -b— 같은 시간에 시스템 전체에 다른 오류가 있었는지.journalctl -k --since "03:00" -g "Out of memory|Killed process"— 커널의 OOM Killer가 프로세스를 죽였는지.- 애플리케이션 자체의 로그(
/var/log/myapp/)를.gz아카이브까지 포함해 확인. - 서버가 재부팅되었다면
journalctl -b -1 -e.
정리
일상적인 리눅스 로그 작업에는 몇 가지 명령이면 충분합니다. 서비스는 journalctl -u, 실시간 흐름은 -f, 범위를 좁힐 때는 --since와 -p err, 재부팅을 분석할 때는 -b -1을 씁니다. 텍스트 로그는 /var/log에 있고, rsyslog가 나누어 기록하고 전달하며, logrotate와 journalctl --vacuum-*가 로그가 디스크를 채우지 않도록 막습니다. journalctl, 로테이션, 부하 진단은 강의 실습에서 실제 서버로 다룹니다.
자주 묻는 질문
journalctl이 마지막 부팅 이후의 기록만 보여 주는 이유는 무엇인가요?
로그가 메모리(/run/log/journal)에만 저장되기 때문입니다. /var/log/journal 디렉터리를 만들거나 journald.conf에 Storage=persistent를 설정하고 systemd-journald를 재시작하십시오.
sudo 없이 일반 사용자가 시스템 로그를 읽게 하려면 어떻게 하나요?
사용자를 systemd-journal 그룹(Debian과 Ubuntu에서는 adm)에 추가하십시오: sudo usermod -aG systemd-journal 이름. 그다음 다시 로그인해야 적용됩니다.
연결된 다른 디스크에 있는 시스템의 journal을 볼 수 있나요?
네. -D 옵션으로 journal 디렉터리를 지정하십시오: journalctl -D /mnt/var/log/journal. --file 옵션은 개별 .journal 파일을 읽습니다.
journald가 있으면 rsyslog가 꼭 필요한가요?
꼭 그렇지는 않습니다. 오래된 도구가 텍스트 파일을 요구하거나 중앙 서버로 로그를 보내야 할 때 유용하며, 많은 최소 이미지에서는 설치하지 않습니다.
서비스 로그를 파일로 내보내서 동료에게 보내려면 어떻게 하나요?
journalctl -u 이름 --since today --no-pager > service.log를 실행하면 일반 텍스트 파일이 만들어집니다.