로그 수집 시스템 필터링의 필요성
로그 수집 시스템에서 필터링이란 애플리케이션 서버 네트워크 인프라등에서 쏟아지는 원본 로그의 raw log 데이터를
최종 중앙 저장소 (Elasticsearch, Loki, Splunk등)로 전송하기전에 정제하는 전처리 과정을 의미한다.
단순히 로그를 버리는것뿐만 아니라 다음과 같은 작업도 같이 한다.
- Drop (제외): 저장 가치가 없는 무의미한 로그를 아예 수집 파이프라인에서 폐기
- Masking/Redaction(난독화): 개인정보나 보안에 민감한 데이터가 포함된 문자열을 ***로 가린다.
- Enrichment(강화): 로그 ip기반의 위치 정보나 컨테이너 라벨등을 추가하여 분석하기 좋은 형태로 만든다.
문제 정의 #
발생하는 모든로그를 일단 저장하자 log everything 시스템을 설계했다고 치자. 접근 방식은 시스템 규모가 커지면 당연히 문제가 생긴다
- 스토리지 비용 폭발 및 ingest 병목: 로그 데이터의 양은 서비스 트래픽에 정비례하므로 데이터가 폭증하는데 무의미한 정상로그 200ok 같은것들까지 전부 저장하면 스토리지 비용이 기하급수적으로 늘어나며 수집 서버 자체가 과부하에 걸려 정작 중요한 장애 로그가 유실되는 현상이 발생한다.
- 보안 및 컴플라이언스 위반: 개발 환경에서 디버깅을 위해 켜둔 옵션이나 예기치 못한 에러로인해 유저의 비밀번호 세션토큰 신용카드 번호같은게 평문으로 로그 저장소에 적재될수있고 이는 심각한 보안 침해 사고로 이어진다.
- 가시성 저하: 하루에 수억건의 로그가 쌓이면 장애가 발생했을 때 원인을 가리키는 몇줄의 에러로그를 찾기위해 엄청난 양의 가비지 데이터를 뒤져야하고 로그 검색 쿼리 자체가 타임아웃나면서 골든타임을 놓치게된다.
Example #
정상적인 상황을 먼저 예를 들어보면 그냥 개발초기에 트래픽이 적어 서버 1~2대에서 모든 웹서버 로그 nginx access log를 그대로 수집해도 시스템이 잘 버틴다.
장애 비용 폭탄을 유발하는 상황을 예로 들어보자 필터링 부재시 서비스가 성장하여 k8s로 이전하고 파드가 100개로 스케일 아웃되어야할때, k8s liveness/readiness probe가 각 파드마다 2~3초 간격으로 /health endpoint를 호출한다.
갑자기 로그 수집기의 cpu 사용률이 100%로 차고 로그 저장소 스토리지 500gb가 단 반나절만에 찬다고 생각해보자. 원인을 분석하려고 저장소에 쿼리를 날렸으나 데이터가 방대해 엔진이 뻗어버린다.
겨우 로그를 까보니 전체 로그 90% 이상이 GET /health 200 이런거임.. 시스템 운영상 가치없는 헬스체크 성공 로그로 도배된 상황
해결 #
로그의 비중을 분석하고 타겟을 잡고 수집기단 agent, shipper에서 강력한 필터링룰을 적용해야한다.
터미널 분석을 통한 가비지 로그 주범 #
어떤 로그를 필터링해야 할지 막막할 때 운영 서버의 원본 로그 파일을 터미널 도구로 직접 분석하여 불필요한 로그 비중을 수치화한다.
# Nginx access.log에서 HTTP Request URI의 호출 빈도를 내림차순으로 추출
$ awk -F'"' '{print $2}' /var/log/nginx/access.log | awk '{print $2}' | sort | uniq -c | sort -nr | head -n 5
850412 /health
42105 /api/v1/users/profile
12040 /api/v1/payments/status
5021 /login
1024 /favicon.ico
health 경로가 전체 로그 볼륨의 압도적인 비율을 차지하고 있는게 확인되었고 이를 필터링 타겟으로 확정! 이런느낌으로
로그수집기(fluentbit) 에서 불필요한 로그 드랍 및 민감정보 마스킹 설정 #
가장 대중적인 수집기중 하나인 fluent bit로 예시를들어보면 설정 파일에 Filter 파이프라인을 추가하여
전처리를 수행할 수 있다. 로그가 네트워크를 타고 중앙 저장소로 날아가기 전에 클라이언트 노드에서 쳐내는것이 핵심이다.
``lni
fluent-bit.conf
1. 쓸모없는 헬스체크 로그 통째로 버리기 (Drop)
[FILTER] Name grep Match nginx.* # /health 경로를 포함하는 로그는 수집 파이프라인에서 제외(Exclude) Exclude log ^.GET /health.$
2. 로그에 포함된 비밀번호/토큰 마스킹하기 (Masking)
[FILTER] Name modify Match app.logs.* # password=블라블라 혹은 token=블라블라 부분을 *로 치환 Condition Key_Value_Matches log .(password|token)=. Set log_masked true # (실제 환경에서는 Lua 스크립트 필터나 정규식 치환 플러그인을 활용하여 해당 값을 "REDACTED" 처리함)
이런식으로 전처리 파이프라인을 거치면 중앙 스토리지 채널로 유입되는 데이터양을 극적으로 줄이면서도
실제 디버깅과 분석에 필요한 핵심 시그널만 남겨 모니터링 시스템의 퍼포먼스와 가시성을 동시에 확보할 수 있다.