[ 글의 목적: linux의 logging architecture 이해를 통해 depth있는 os trouble shotting과 고도화된 application log system 구축을 위해 ]
Linux OS의 log는 커널, 서비스, 애플리케이션 등 시스템에 발생한 이벤트를 분류하여 기록한 파일이다. 크게 syslog, journal 2가지로 나뉘며 시스템 이슈 트레킹, 모니터링 등에 중요한 지표가 된다. 기본적으로 리눅스 로그 아키텍쳐와 두 가지 로깅에 대해 조금 더 자세히 뜯어보자. 그리고 마지막으로 logrotate 까지 살펴보자.
mongodb log rotate 가 급하신 분은 글의 가장 밑을 확인해 주세요!
🔥 log는 진짜 중요하다.
application에 대한 log 중요성은 BE/System 개발자라면 익히 깨닫게된다. 하지만 배포 형태와 log 활용 방안에 대해 매번 고민이 있을 것 이다.
service로 등록해 관리한다면 systemd 에 의해 관리될텐데 리눅스 자체의 로그 시스템을 모른다면 트러블 슈팅에 불편함이 있을 수 있다. 특히 application web-server (nginx) 등과 같은 third-party 활용에도 journal log를 확인해야할 순간이 꼭 온다.
network와 관련된 로깅 활용으로 침해사고 발생 시 침해사고의 원인과 침입경로, 피해상황 등을 파악할 수 있는 중요한 단서가 될 수 있다.
이 위의 모든 내용을 포괄하는 추적과 모니터링, 그에 따른 성능 개선에도 활용이 가능하다.
Linux Logging Types 은 크게 kernal logging & user logging으로 나뉜다.
Kernel logging : related to errors, warning or information entries that your kernel may write;
User logging : linked to the user space, those log entries are related to processes or services that may run on the host machine.
linux를 system을 나눌때 크게 [ user space <-> kernel space ] 분할해서 본다. kernel space에서 logging은 kernel의 "Ring Buffer" 를 통해 수행된다. 커널 링 버퍼는 시스템이 부팅될 때 로그 메시지를 저장하는 첫 번째 데이터 구조인 순환 버퍼이다.
앞서 말한대로, 링 버퍼는 고정 크기의 순환 버퍼로 구성되어 있으며, "최신 데이터를 기록"하고 "오래된 데이터는 덮어쓰는" 방식으로 동작한다. 이를 통해 제한된 메모리 공간을 효율적으로 활용하면서도 중요한 로그 정보를 유지할 수 있다.
더 자세하게 살펴볼 것 이지만 커널 메시지(printk)를 기록하는 dmesg
명령어는 링 버퍼를 통해 커널 메시지를 읽어오고, syslogd
와 같은 로깅 데몬은 링 버퍼를 사용하여 로그를 수집하고 저장한다.
커널 관련 로그는 dmesg
명령어를 통해 확인이 가능하다. (root 권한이 필요하다.)
linux는 모든 것이 파일이다. 즉 장치도 하나의 파일로 인식된다. linux에서 ring buffer는 /dev
디렉토리의 "kmsg" 라는 character device 에 의해 구체화 된다.
ls -l /dev/ | grep kmsg
명령어로 kmsg을 찾아보자. 그리고 cat
을 때려보면 커널 로그 내용인것을 확인할 수 있다.
/dev/kmsg
는 ring buffer를 읽고 쓰기 위해 사용되는 추상화된 디바이스(virtual device)다. 이를 통해 user space 프로세스가 kernel ring buffer 접근 (읽기, 쓰기) 하기 위한 진입점이 된다. (또는 그 반대의 경우)
그리고 우리는 dmesg
cli 를 통해 커널 로깅메시지를 확인할 수 있다.
그리고 ring buffer는 /proc/kmsg
로 data를 Dump 한다.
원래 커널 로그의 경우 klogd
이 담당했지만 (커널은 klogd, 시스템로깅은 syslog) 지금은 rsyslogd
가 대체 했다.
user space에서 로그는 최근 배포판 기준으로 크게 syslog과 journal 2가지로 나눠서 비교가 된다. rsyslogd에 의해 수집되는 로그는 보통 syslog라고 부른다.
syslog & journal log 비교 표
항목 | syslog(rsyslog) | journal | 비고 |
---|---|---|---|
저장하는 정보 | syslog에 해당하는 특정 종류들 | 부팅 이후 모든 메시지 | - |
저장 여부 | /var/log | 저장하지 않음 | 리눅스계열에 따라 저장 위치 바뀜 |
저장 메시지 타입 | 일반 text, format은 다양 | 바이너리 형태 | - |
서비스(데몬) | syslogd (rsyslogd) | system-journald | - |
순환 | logrotate에 의해 4주 | 저장하지 않아서 부팅마다 초기화 | - |
위 사진은 syslog 아키텍쳐의 간단 도식화 이다.
syslog 서버가 존재하고 UDP 프로토콜 및 포트번호 514 사용하여 facility (로그 메세지 유형), severity (로그 메세지 중요도), 메시지 (로그 내용)
메시지 형태로 전송한다. (각 세부사항은 나중에 다시 살펴보자.)
syslog는 위에서 언급하였듯이, 최근 배포판에서는 멀티스레드 지원, TCP 지원, SSL/TLS 지원 및 보안이슈로 업데이트 된 rsyslogd
를 사용한다. (정확하게는 syslogd 가 rsyslogd로 바뀐 것이고 syslog라는 용어는 아직 하나로 사용한다)
시스템 내 수많은 커널 경고, 디버깅 정보, 각종 메시지 출력 등에 대한 로그 정보를 기록한다.
tail -f /var/log/syslog
로 실제 logging된 데이터 예시는 아래와 같다.
Jan 2 01:14:36 amkr-main kernel: [20220286.684301] [UFW BLOCK] IN=eth0 OUT= MAC=33:33:00:00:00:01:00:15:5d:3a:7b:5a:86:dd SRC=fe80:0000:0000:0000:1ce8:db1b:9a20:2b66 DST=ff02:0000:0000:0000:0000:0000:0000:0001 LEN=52 TC=0 HOPLIMIT=1 FLOWLBL=0 PROTO=UDP SPT=5678 DPT=5678 LEN=12
Jan 2 01:15:01 amkr-main CRON[2228318]: (root) CMD (/bin/bash -c 'if ! pgrep mongod; then /usr/sbin/service mongodb start; fi')
Jan 2 01:15:01 amkr-main CRON[2228319]: (copfirst) CMD (/usr/bin/python3.8 /home/copfirst/amnotifyKR-Renewal/crawler/schedule/crawler_health_checker.py 2>&1)
DATE | TIME | hostname | (user) process name | message (log content)
로 구분이 된다.
이 rsyslogd
은 /etc/rsyslog.d
하위에 데몬 설정 파일이 존재한다. 20-ufw.conf
설정 파일을 보면 아래 사진과 같다.
/etc/rc.d/init.rsyslog
: rsyslogd 데몬을 동작시키는 스크립트/etc/rsyslog.conf
: rsyslogd 데몬 환경 설정 파일/etc/sysconfig/rsyslog
: rsyslogd 데몬 실행과 관련된 옵션 설정 파일/sbin/rsyslogd
: 실제 rsyslogd 데몬 실행 명령ufw.conf
와 같이 conf 파일을 등록하고 재시작만 해주면 로깅 등록이 가능하다. 하지만 그 전에 기본적인 용어에 대한 이해가 필요하다. Facility Number | Keyword | C code | Facility Description |
---|---|---|---|
0 | kern | LOG_KERN | kernel messages |
1 | user | LOG_USER | user-level messages |
2 | LOG_MAIL | mail system | |
3 | daemon | LOG_DAEMON | system daemons |
4 | auth | LOG_AUTH | security/authorization messages |
5 | syslog | LOG_SYSLOG | messages generated internally by syslogd |
6 | lpr | LOG_LPR | line printer subsystem |
7 | news | LOG_NEWS | network news subsystem |
8 | uucp | LOG_UUCP | UUCP subsystem |
9 | clock | LOG_CRON | clock daemon |
10 | authpriv | LOG_AUTHPRIV | security/authorization messages |
11 | ftp | . | FTP daemon |
12 | ntp | . | NTP subsystem |
13 | security | . | Security log audit |
14 | console | . | Console log alerts |
15 | solaris-cron | . | Scheduling logs |
16-23 | local0-7 | LOG_LOCAL0-7 | Locally used facilities use 0-7 (local0) |
Code | Severity | Keyword | C code | Description |
---|---|---|---|---|
0 | Emergency | emerg (panic) | LOG_EMERG | System is unusable. |
1 | Alert | alert | LOG_ALERT | Action must be taken immediately. |
2 | Critical | crit | LOG_CRIT | Critical conditions. |
3 | Error | err (error) | LOG_ERR | Error conditions. |
4 | Warning | warning (warn) | LOG_WARNING | Warning conditions. |
5 | Notice | notice | LOG_NOTICE | Normal but significant condition. |
6 | Informational | info | LOG_INFO | Informational messages. |
7 | Debug | debug | LOG_DEBUG | Debug-level messages. |
application을 통해서 말고 cli를 통해 logging을 할 수 있는 가장 단순한 방법은 logger
cli를 활용하는 것이다.
logger log test | tail -1 /var/log/syslog
를 쳐보자. 그러면 다음과 같은 로그를 확인할 수 있다. Jan 2 01:59:23 amkr-main copfirst: log test
/etc/rsyslog.d/50-default.conf
파일 가장 하단에 아래 내용을 추가하자.
# for test rsyslog
local0.* /var/log/test.log
그리고 *.*;auth,authpriv.none -/var/log/syslog
line을 찾아 local0.none
를 추가하는 *.*;auth,authpriv.none,local0.none -/var/log/syslog
와 같이 바꿔주자.
이 줄의 의미는 모든 로그(.)를 /var/log/syslog에 기록하지만, 세미콜론(;) 이후의 facility들인 auth, authpriv, local0 은 제외(none)하라는 것이다. 파일이름 앞의 - 은 로그를 파일에 바로 쓰지 말고 메모리에 로그를 가지고 있다가 디스크에 입출력 여유가 있을 경우 쓰라는 의미이다. (http://shallowsky.com/blog/linux/rsyslog-conf-tutorial.html 의 Rules Section을 체크하자)
적용을 위해 sudo service rsyslog restart
(또는 sudo systemctl restart rsyslog.service) 통해 restart 하자
c 코드에서 다음과 같이 local0에 쓰도록 하면 /var/log/test.log
에 로그가 기록된다. (당연히, 다른 application을 통해서 /var/log/test.log
를 쓰게끔 하는 것도 방법이다.)
#include <syslog.h>
int main()
{
syslog(LOG_INFO | LOG_LOCAL0, "write your log message");
return 0;
}
그래서 저널로그는? 위 kernel logging 에 대한 설명 도식화에서 systemd-journal
이 등장했다. 저널로그는 systemd-journal
의해 수집되는 로그를 말하며 최근 배포판에 등장하여 rsyslogd
와 공존하고 있는 상태다.
사실 rsyslogd
와 핵심 차이는 "저장 여부" 정도로 볼 수 있다. 아래 도식화 그림에서 rsyslog
와 systemd-jurnal
의 차이를 살펴보자
로그 파일은 systemd에 의해 바이너리 파일 로 저장 되므로 일반적인 cat 또는 less 명령을 사용하여 파일을 검사할 수 없다.
그래서 journalctl
이라는 cli를 사용한다. journalctl -r
을 통해 최신 저널 로그 중심으로 볼 수 있다.
journalctl -u ssh
와 같이 ssh 자리에 특정 service name을 넣어서 사용이 가능하다. 위 option과 같이 사용해 journalctl -r -u ssh
과 같은 cli를 가장 많이 사용한다.
그 외에 journalctl -since '2022-12-30'
or journalctl -since '2022-12-30' -until '2022-12-31'
과 같이 날짜 설정값도 가능하다.
그 외 상세한 option은 journalctl(1) — Linux manual page 에서 확인하자.
/etc/systemd/journald.conf
에서 저널 로그 관련 설정이 존재하며 기본 설정값은 아래와 같다./var/log/journal
하위에 존재하게 된다. [Journal]
Storage=persistent
SystemMaxFiles=500
특정 디렉터리 생성하고 해당 디렉터리에 대해 권한과 소유권을 설정하자. 예시로 /var/log/myjournal
을 활용하겠다.
sudo chown root:systemd-journal /var/log/myjournal
sudo chmod g+s /var/log/myjournal
systemd 에게 tmpfile 의 경로를 알려 주자
sudo systemd-tmpfiles --create --prefix /var/log/myjournal
변경된 설정을 다시 읽도록 killall -USR1 systemd-journald
signal 전송
영구적 저장에 제한 사항이 있다. 기본 제한 사항은 file system 전체 사이즈 10% 초과하면 안되고, file system 여유 공간 중 15%를 초과하면 안된다.
해당 값은 위 conf 에서 바꿀 수 있다. 자세한 사항은 official docs를 확인하자.
지정된 로그 파일에 계속해서 로그를 쌓게 되면 로그 파일의 크기가 과도하게 커질 수 있다. 이럴 경우 로그 파일을 읽어오기 위해 보다 많은 메모리가 필요할 뿐 아니라 로그를 확인하고 분석하는 것도 어려워진다. 이러한 문제를 해결하기 위해 일정시간 단위로 로그화일을 구분하여 저장하고 오래된 로그 파일을 정리하는, 일명 logging file-roation 을 해주는 logrotate 가 있다.
/etc/cron.daily/logrotate
파일을 한 번 확인해보자. 참고로 내용은 버전에 따라 상이할 수 있다. (cron에 대한 설명은 리눅스 - 리눅스 작업 예약 스케쥴러 cron, crond, crontab/usr/sbin/
는 cli 라고 생각할 수 있다. (sbin 디렉토리에 대한 의문은 리눅스 파일 시스템 소유권과 권한 글에서 확인 가능하다.) logrotate cli를 실행하면서 /etc/logrotate.conf
파일을 인자로 넘겨주는 것이다. 그래서 매일 자정에 logrotate 데몬을 conf 파일과 같이 돌리는 것이다./etc/logrotate.d
: logrotate를 설정할 개별 프로세스 설정파일/etc/logrotate.conf
: logrotate 설정파일 (모드 로그들의 총괄적 설정)/var/lib/logrotate.status
: logrotate 작업내역 보관 파일/etc/cron.daily/logrotate
: logrotate cron/usr/sbin/logrotate
: logrotate 데몬 위치include
문단을 보면 logrotate 유틸리티는 /etc/logrotate.d
폴더에 있는 사용자가 정의한 파일들을 로드하여 로그를 관리하게 된다.
즉 우리가 설정할 땐 conf를 바로 수정할 필요 없이 /etc/logrotate.d
폴더에다가 원하는 로그를 관리하기 위한 작업파일을 작성하면 된다.
rotate <숫자>
: 로그파일 개수create <option> | empty
: 새로운 로그 파일 생성 여부와 create시 해당 logfile에 대한 설정dateext
: 로그 파일명의 날짜 부여 여부dateformat
: 파일명 date format 형태!compress
: 로그 파일 압축 여부postrotate
에 대한 설명은 mongodb를 다루면서 더 자세하게 하겠다. 공식 홈페이지 설명 과 개인적 견해를 더해 logrotate file 로 처리해버렸다. /var/log/mongodb/*.log {
create 0644 nobody root # log파일 sudo 권한 없이 볼 수 있게 하기
daily # 메일
size 10000M
dateext # 백업 파일명에 날짜가 기입되도록 함
dateformat %Y-%m-%d-%s # 압축 파일명 date format 형태
rotate 10 # 10개 까지만 저장, 나머지 삭제
copytruncate
delaycompress
compress
notifempty # 로그 내용이 없어도 rotate 진행
missingok
sharedscripts # 로그 파일이 여러 개 있어도 스크립트를 공유하여 postrotate 스크립트를 한 번만 실행
postrotate # rotate 실행 후 스크립트 파일 실행
/bin/kill -SIGUSR1 `${pidof mongod}`
endscript
}
일반적인 강제 실행은 cron에서 살펴본 바와 같이 /usr/sbin/logrotate -f /etc/logrotate.d/만든설정파일
로 할 수 있다.
디버그 모드는 실제 실행은 하지 않지만 잘 실행되는지, 어떻게되는지 체크할 수 있다. /usr/sbin/logrotate -d /etc/logrotate.d/만든설정파일
"만든설정파일" 의 step by step 실행 과정이 궁금하면 -v
option을 사용할 수 있다. /usr/sbin/logrotate -v /etc/logrotate.d/만든설정파일
"Centralized logging" 의 중요성을 다시 한 번 깨닫게 되는 계기였다. 개별의 application의 logging이 아무리 잘 되어 있어도, 특히 요즘과 같이 MSA가 대세가 되는 환경에서는, 분산된 logging에 대한 monitoring이 쉽지가 않다.
server 자체를 위한 logging과 application logging 은 모두 활용되어야 한다. linux 기반의 os가 활용하는 logging architecture 를 제대로 알고 어떻게 로그를 관리하고 활용할지에 대한 더 심도있는 고민이 필요하다.
거시적으로 rsyslogd, systemd-journald + [ (1) application file logging + (2) application file logging .... ] + ogrotate config
가 하나의 server와 속하는 application (web, was server, agent ... etc) 들 Logging 관리를 위한 올바른 접근법이 아닐까 생각된다.
그리고 관리되는 Log file들은 Logstash
등 활용으로 ELK (EFK), grafana loki
등의 stack과 결합 하여 활용되는 것이 아주 바람직한 로그 관리법이 아닐까 생각이 든다.
이러한 에러 대응 및 로깅을 아주 쉽게 도와주는 third-party Sentry가 있다. Django - Sentry 도입과 적용 원리 글에서 한 번 살펴보자!
좋은 글 감사합니다!