Contents
see List운영 서버 로그는 장애가 난 뒤가 아니라 배포 전에 설계해야 합니다
Linux 서버에서 장애 분석이 어려워지는 가장 흔한 이유는 로그가 없는 것이 아니라, 필요한 시점의 로그가 이미 사라졌거나 여러 위치에 흩어져 있는 것입니다. 특히 systemd 기반 배포판에서는 애플리케이션 표준 출력, 서비스 상태, 커널 메시지, SSH 접속 기록, 패키지 작업 로그가 서로 다른 계층에 남습니다. 이 문서는 작은 서비스 운영팀이 바로 적용할 수 있도록 journald 영구 저장, 보관 용량 제한, logrotate 확인, 장애 시 조회 명령을 하나의 기준으로 정리합니다.
목표는 모든 로그를 무조건 오래 저장하는 것이 아닙니다. 운영 로그는 보안, 비용, 개인정보, 디스크 장애 위험을 동시에 고려해야 합니다. 따라서 실무에서는 최근 장애를 재현할 수 있는 기간은 충분히 남기고, 디스크가 꽉 차서 서비스가 멈추지 않도록 상한선을 명확히 두는 방식이 안전합니다. 일반적인 웹/API 서버라면 systemd journal은 7일에서 30일 정도, 애플리케이션 파일 로그는 서비스 중요도에 따라 14일에서 90일 정도로 시작한 뒤 실제 트래픽과 장애 대응 시간을 보고 조정하는 편이 좋습니다.
1. journald를 영구 저장으로 바꾸기
기본 설정에 따라 journald 로그는 재부팅 후 사라질 수 있습니다. 운영 서버에서는 재부팅 직전의 OOM, 커널 오류, 서비스 종료 원인을 확인해야 하므로 영구 저장을 켜는 것이 좋습니다. 핵심은 /var/log/journal 디렉터리를 만들고 Storage=persistent를 설정하는 것입니다. 동시에 SystemMaxUse, RuntimeMaxUse, MaxRetentionSec 같은 제한을 걸어 디스크를 보호해야 합니다.
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/10-persistent-limit.conf >/dev/null <<'EOF'
[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=2G
RuntimeMaxUse=512M
MaxRetentionSec=30day
ForwardToSyslog=no
EOF
sudo systemctl restart systemd-journald
journalctl --disk-usage
SystemMaxUse=2G는 서버 전체 기준이 아니라 journal 저장소가 사용할 수 있는 최대 용량입니다. 서버 디스크가 20GB라면 2GB도 클 수 있고, 500GB라면 너무 작을 수 있습니다. 로그량을 모르는 초기에는 디스크의 5~10%를 넘기지 않는 선에서 시작하고, journalctl --disk-usage와 df -h를 함께 보며 조정합니다.
2. 서비스별 로그 조회 기준 만들기
systemd 서비스로 실행되는 애플리케이션은 파일 로그를 남기지 않아도 journalctl -u 서비스명으로 표준 출력과 표준 오류를 볼 수 있습니다. 장애 대응 문서에는 서비스별 조회 명령을 반드시 적어두는 것이 좋습니다. 사람이 기억에 의존하면 장애 순간에 grep 위치부터 찾게 됩니다.
# 최근 부팅 이후 nginx 로그
journalctl -u nginx.service -b --no-pager
# 최근 2시간 동안 app 서비스의 warning 이상만 보기
journalctl -u app.service --since '2 hours ago' -p warning --no-pager
# 특정 요청 ID나 에러 키워드 추적
journalctl -u app.service --since today --no-pager | grep 'request_id=abc123'
# 이전 부팅에서 커널/OOM 흔적 확인
journalctl -k -b -1 --no-pager | grep -Ei 'oom|killed process|segfault|ext4|nvme'
운영 기준으로는 -b, --since, -p, -u 네 가지 옵션만 팀원이 익숙해져도 대응 속도가 크게 올라갑니다. -f로 실시간 추적을 할 수 있지만, 장애 분석 기록을 남길 때는 시간 범위를 지정한 명령이 재현성과 공유에 더 좋습니다.
3. 애플리케이션 파일 로그는 logrotate로 별도 관리
Java, Node.js, Python 애플리케이션은 여전히 /var/log/app/*.log 형태로 파일 로그를 남기는 경우가 많습니다. 파일 로그는 journald와 다르게 서비스가 파일 핸들을 계속 잡고 있을 수 있으므로 회전 방식이 중요합니다. 애플리케이션이 SIGHUP이나 재오픈을 지원하지 않는다면 copytruncate를 쓰되, 짧은 순간 로그 유실 가능성이 있다는 점을 알고 써야 합니다. 가능하면 애플리케이션 로거가 직접 일자별 파일을 만들거나, logrotate 후 서비스를 재로드하는 방식이 더 명확합니다.
sudo tee /etc/logrotate.d/app >/dev/null <<'EOF'
/var/log/app/*.log {
daily
rotate 30
missingok
notifempty
compress
delaycompress
dateext
create 0640 app app
sharedscripts
postrotate
systemctl kill -s HUP app.service 2>/dev/null || true
endscript
}
EOF
# 실제 회전 전 설정 검증
sudo logrotate -d /etc/logrotate.d/app
# 강제 회전 테스트는 운영 영향이 있으므로 점검 창에서 수행
sudo logrotate -f /etc/logrotate.d/app
logrotate -d는 dry-run이라 실제 파일을 바꾸지 않고 어떤 회전이 일어날지 보여줍니다. 운영 서버에서 새 설정을 넣은 뒤 바로 -f를 실행하는 습관은 위험합니다. 먼저 dry-run으로 경로, 권한, 소유자, postrotate 명령을 확인하고, 서비스가 로그 파일을 정상적으로 다시 여는지 점검해야 합니다.
4. 디스크가 꽉 차기 전에 경보 기준 잡기
로그 운영의 실패는 대부분 디스크 사용률 100%에서 드러납니다. 데이터베이스가 쓰기를 멈추고, 웹 서버가 임시 파일을 만들지 못하고, SSH 접속마저 느려지는 상황이 생깁니다. 최소한 루트 파티션 사용률, inode 사용률, journal 사용량은 주기적으로 확인해야 합니다.
df -h / /var /var/log 2>/dev/null
df -i / /var /var/log 2>/dev/null
journalctl --disk-usage
# journal을 즉시 정리해야 할 때의 임시 대응
sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=1G
--vacuum-time과 --vacuum-size는 긴급 대응용으로 유용하지만, 반복해서 실행하고 있다면 설정이 잘못된 것입니다. 정상 상태에서는 journald 설정의 상한선과 logrotate 정책만으로 디스크 사용량이 예측 가능해야 합니다. 또한 /var/log를 별도 파티션으로 분리하면 로그 폭주가 루트 파일시스템 전체 장애로 번지는 것을 줄일 수 있습니다.
5. 보안과 개인정보 기준
로그에는 생각보다 민감한 정보가 자주 섞입니다. Authorization 헤더, 세션 쿠키, 주민등록번호, 이메일 인증 토큰, 결제 요청 파라미터가 그대로 남으면 장애 분석 도구가 아니라 보안 사고의 출발점이 됩니다. 운영 기준에서는 애플리케이션 로그에 토큰과 비밀번호를 남기지 않는 필터를 먼저 적용하고, 로그 파일 권한을 서비스 계정과 운영자 그룹으로 제한해야 합니다.
- 로그 파일 권한은 일반적으로
0640, 소유자는 서비스 계정과 운영 그룹으로 맞춥니다. - 요청 본문 전체 기록은 기본 비활성화하고, 필요한 API만 짧은 기간 켭니다.
- 장애 공유용 로그를 외부 도구에 붙여넣기 전에 토큰, 쿠키, 개인식별정보를 제거합니다.
- 로그 보관 기간은 서비스 약관, 개인정보 처리방침, 내부 보안 정책과 맞춥니다.
마무리 체크리스트
- journald가 재부팅 후에도 남도록
Storage=persistent를 설정했는가? SystemMaxUse와MaxRetentionSec로 journal 용량과 기간을 제한했는가?- 주요 서비스별
journalctl -u 서비스명 --since ...조회 명령이 문서화되어 있는가? - 파일 로그가 있다면
logrotate -d로 회전 설정을 검증했는가? - 루트 파티션, inode, journal 사용량을 감시하고 경보 기준을 정했는가?
- 로그에 토큰, 쿠키, 개인정보가 남지 않도록 필터와 권한을 적용했는가?