“로그 를 수집 한다” 는 말 은 쌓는다 처럼 들리지만, 실제 로 는 여러 결정 의 집합 이다 — 어디서 뽑고, 어떻게 흘리고, 무슨 형태 로 담고, 얼마 를 태울지. 각 축 에서 잘못 고르면, 정작 장애 났을 때 검색 이 안 되거나 · 로그 가 없거나 · 청구서 가 폭발 한다.

이 글 은 로그 수집 을 컴포넌트(ELK 각 부품 은 따로 다뤘다) 가 아니라 결정 의 축 으로 훑는다. 내 홈랩 의 실제 스택(6-node K3s, fluent-bit DaemonSet 5개, Elasticsearch hot/warm/cold 3-tier) 을 근거 로.


1. 어디서 뽑나 — 수집 지점 의 네 갈래

로그 를 어느 지점 에서 잡느냐 가 첫 결정 이다:

방식 어떻게
stdout + 노드 에이전트 앱 은 stdout 에만 찍고, 노드 의 DaemonSet(fluent-bit)이 컨테이너 로그 파일 을 tail 앱 이 로깅 인프라 를 모름, k8s 표준 노드 파일 경로·로테이션 에 의존
사이드카 파드 마다 로그 수집 컨테이너 를 붙임 앱 별 커스텀 파이프라인 파드 마다 리소스 ×2, 복잡
앱 직접 push 앱 이 로그 를 직접 ES/Loki 로 전송 중간 단계 없음 앱 이 수집처 장애 에 물림(결합)
시스템/커널 journald·syslog·감사 로그 인프라 레벨 가시성 앱 로그 와 포맷 이 다름

k8s 의 정답 은 대개 첫 번째 — 앱 은 stdout 에만 찍고(12-factor), 노드 DaemonSet 이 걷는다. 내 클러스터 도 fluent-bit 를 노드 마다(5개) 띄워 모든 컨테이너 stdout 을 수집 한다. 앱 은 로그 를 어디로 보내는지 조차 몰라도 된다 — 이 무지 가 곧 느슨한 결합 이다.


2. 어떻게 흘리나 — 에이전트 → 수집기 → 저장 → 조회

수집 은 파이프라인 이다: 경량 에이전트(fluent-bit) → 수집기·가공(Logstash) → 저장·색인(Elasticsearch) → 조회(Kibana).

여기서 중요한 건 유실 지점 이다. 각 단계 의 경계 마다 로그 가 샐 수 있다:

  • 에이전트 가 tail 하다 파일 이 로테이션 되며 놓침
  • 버퍼 가 가득 차 드롭
  • 수집기 가 다운 된 사이 의 로그

그래서 좋은 수집 은 at-least-once + 백프레셔 를 설계 한다 — 에이전트 에 디스크 버퍼 를 두고, 수집처 가 죽으면 로컬 에 쌓았다가 재전송. “로그 는 유실 돼도 되는 데이터” 라는 통념 은 디버깅 하는 새벽 3시 에 배신 한다.


3. 무엇 을 담나 — 구조화 로그 가 이긴다

같은 정보 라도 형태 가 검색가능성 을 가른다:

# 비구조화 — grep 은 되지만 집계·필터 가 지옥
2026-07-11 03:00:01 ERROR payment failed for user 42 amount 32000

# 구조화(JSON) — 필드로 검색·집계·알림
{"ts":"...","level":"ERROR","event":"payment_failed","user_id":42,"amount":32000,"trace_id":"abc"}

구조화 로그 의 값어치 는 셋 이다 — 필드 검색(level:ERROR AND user_id:42), 집계(에러율·금액 합), 상관관계(trace_id 로 요청 전체 추적). 특히 trace_id/request_id 를 모든 로그 에 심으면, 흩어진 로그 가 하나의 요청 스토리 로 꿰어진다.

로그 는 사람 이 읽는 문장 이 아니라 기계 가 질의 하는 데이터 다. 이 관점 의 전환 이 구조화 로깅 의 핵심.


4. 수집 의 함정 — 버퍼·유실·PII·볼륨

실무 에서 실제 로 데는 지점 들:

  • 버퍼링 / flush. 프로세스 가 stdout 을 버퍼 링 하면, 크래시 직전 로그 가 안 찍힌 채 사라진다. 정작 원인 인 마지막 로그 를. 내 데이터 파이프라인 에서도 이걸 겪어 — unbuffered 출력 + 라인 마다 flush 로 강제 해 해결했다. 크래시 로그 는 버퍼 에 있으면 없는 것 이다.
  • 순서 와 유실. 분산 수집 에서 로그 순서 는 보장 되지 않는다. 그래서 타임스탬프 를 앱 이 찍어야지, 수집 시각 에 의존 하면 안 된다.
  • PII. 로그 에 비밀번호·토큰·주민번호 가 찍히면, 한 번 색인 된 순간 회수 불가. 수집 단계 에서 마스킹 필터 를 건다.
  • 볼륨 폭발. DEBUG 를 프로덕션 에 켜두면 로그 가 앱 보다 무겁다. 레벨·샘플링 이 곧 비용 통제.

5. 얼마에 모으나 — 로그 는 공짜 가 아니다

가장 자주 잊는 축. 로그 는 전송 + 색인 + 저장 + 조회 마다 돈·CPU 를 먹는다. 그래서 수집 설계 는 비용 설계 이기도 하다:

  • 레벨·샘플링 — 성공 요청 은 1% 만, 에러 는 100%. 정보량 대비 비용 을 맞춘다.
  • 저장 티어링(hot/warm/cold) — 최근 로그 는 빠른 SSD(hot), 오래된 건 느린 디스크(cold). 내 ES 는 hot/warm/cold 3-tier + ILM(수명 정책) 으로 자동 강등·삭제. 최근 며칠 만 빨라야 하고, 90일 전 로그 는 싸게 보관 하면 된다.
  • self-host vs managed — ELK 를 직접 굴리면 인프라·운영 비용, Datadog 같은 SaaS 는 GB당 요금. 이건 기술 도입비용 의 저울 그대로 — 트래픽·팀 규모 로 결정 한다. (Datadog vs 무료 관측성 에서 깊게)

무제한 으로 다 모으는 건 설계 가 아니라 방치 다. 무엇 을 안 모을지 를 정하는 게 로그 수집 설계 의 절반.


6. 3기둥 에서 로그 의 자리 — 혼자서는 부족하다

로그 는 관측성 의 한 기둥 일 뿐 이다:

  • Logs무슨 일 이 있었나 (이벤트·에러의 상세). 카디널리티 높음, 비쌈.
  • Metrics얼마나 (요청수·지연·에러율). 싸고 집계 친화적, 상세 없음.
  • Traces어디서 (요청이 서비스들 을 관통한 경로). 병목·분산 디버깅.

셋 은 대체 가 아니라 보완 이다. 흔한 실수 는 메트릭 으로 충분한 걸 로그 로 세는 것 — “요청 수” 를 로그 count 로 뽑으면 비싸고 느리다. 그건 메트릭 의 일. 로그 는 메트릭 이 이상 을 알리면 그 원인 을 파는 자리 다.


7. 수집 그다음 — 파싱·enrich·알림

수집 은 끝 이 아니라 시작 이다. 걷은 로그 에 가치 를 더하는 단계:

  • 파싱 — 비구조 로그 를 필드 로 쪼갬(grok/정규식)
  • enrich — k8s 메타(namespace·pod·node), 지오IP, 서비스 태그 를 붙임
  • 라우팅 — 감사 로그 는 오래, 디버그 는 짧게 — 인덱스 를 분리
  • 알림 — 로그 에서 이상 을 능동 감지. 내 클러스터 는 5분 마다 ES 를 쿼리 해 level:ERROR/FATAL 을 찾으면 텔레그램 으로 쏘는 크론(log-error-alerter) 이 돈다. 대시보드 는 볼 때만 보이지만, 이건 터지면 온다.

마무리 — 수집 은 시작 일 뿐

정리 하면, 로그 수집 은 여섯 축 의 결정 이다:

어디서(지점) · 어떻게(파이프라인·유실) · 무엇을(구조화) · 함정(버퍼·PII·볼륨) · 얼마에(비용·티어) · 그다음(파싱·알림).

내 홈랩 은 이걸 fluent-bit(수집) → Logstash(가공) → ES hot/warm/cold(저장·티어) → Kibana(조회) + log-error-alerter(알림) 으로 엮었다. 하지만 도구 는 부차적 이다 — 핵심 은 “장애 났을 때 3분 안 에 원인 로그 를 검색 할 수 있나”“그걸 감당 가능한 비용 으로 하나” 다.

로그 를 많이 모으는 건 쉽다. 필요한 로그 를, 검색 가능 하게, 감당 가능한 비용 으로 모으는 게 엔지니어링 이다. 수집 은 시작 일 뿐, 진짜 게임 은 검색가능성 과 비용 에서 난다.