우리 환경에서의 처리량과 자원
우리 팀은 2024년 말부터 Fluent Bit을 메인 로그 수집기로 쓰고 있다. 그전에는 Fluentd였고, 메모리 때문에 갈아탄 거였다. 그리고 작년쯤부터 일부 노드 그룹에 Vector를 같이 굴리고 있다. 이유는 좀 단순한데, 특정 워크로드의 로그가 너무 거칠어서 변환 규칙이 복잡해졌고, Fluent Bit의 Lua filter로 이걸 다 처리하기엔 가독성이 너무 떨어졌기 때문이다.
그 상태로 1년 넘게 양쪽을 같이 운영했다. 최근 팀 내부에서 "그냥 한쪽으로 통일하자"는 얘기가 다시 올라와서, 진지하게 다시 비교해봤다. 이 글은 결론이 정해진 비교가 아니다. 솔직히 우리도 아직 한쪽으로 못 정했다.
숫자부터 봤다
우리 EKS 클러스터는 worker 노드가 약 110대 정도, 로그 발생량은 피크 시간대 기준 노드당 평균 8~12k logs/sec다. 둘 다 DaemonSet으로 띄워서 같은 워크로드의 로그를 수집한 뒤 Loki로 보낸다. 같은 노드에서 24시간 굴려본 결과는 대략 이랬다.
- Fluent Bit 3.x: RSS 평균 180MB, p99 약 220MB. CPU 0.15 core 정도.
- Vector 0.41: RSS 평균 320MB, p99 약 380MB. CPU 0.22 core 정도.
최근 외부 벤치마크에서도 비슷한 경향이 나온다. Fluent Bit이 메모리/CPU를 덜 쓴다. Vector가 처리량 ceiling이 좀 더 높긴 한데, 우리 워크로드 수준에서는 둘 다 여유가 있어서 사실상 그 차이가 의미 없다.
근데 재밌는 건 p99 latency 쪽이었다. Vector 쪽이 변환 작업이 무겁게 걸렸을 때 latency가 더 안정적이었다. Fluent Bit은 Lua filter 들어가면 가끔 튀었다. 평소엔 둘 다 ms 단위인데, 로그 폭증 구간에서 Fluent Bit p99가 가끔 700ms 가까이 가는 걸 본 적 있다. Vector는 같은 상황에서 200ms 안쪽이었다.
변환 로직 짤 때
이게 사실 양쪽의 가장 큰 차이라고 생각한다. 우리 팀에서는 Vector의 VRL(Vector Remap Language)을 더 좋아하는 사람이 많다.
Fluent Bit에서 같은 처리를 Lua로 짜면 이런 식이다.
function process(tag, ts, record)
if record["level"] == nil and record["log"] ~= nil then
local m = string.match(record["log"], "^%[(%w+)%]")
if m then record["level"] = string.lower(m) end
end
if record["request_id"] == nil and record["trace_id"] ~= nil then
record["request_id"] = string.sub(record["trace_id"], 1, 16)
end
return 2, ts, record
end
같은 걸 VRL로 쓰면 이렇다.
if !exists(.level) && exists(.log) {
.level = downcase(parse_regex!(.log, r'^\[(?P<lv>\w+)\]').lv) ?? null
}
if !exists(.request_id) && exists(.trace_id) {
.request_id = slice!(.trace_id, 0, 16)
}
길이 차이는 별 거 아닌데, 코드 리뷰할 때 체감이 다르다. VRL은 타입을 강하게 잡고, 잘못된 변환은 컴파일 시점에 실패한다. Lua는 그냥 nil 반환하고 조용히 넘어간다. 그래서 운영 들어간 뒤 "왜 어떤 로그는 level 필드가 없지?" 같은 디버깅이 우리 팀에서는 거의 다 Lua filter 쪽에서 나왔다.
다만 단순한 매칭이나 수정은 Fluent Bit의 parsers.conf + modify filter 조합이 압도적으로 빠르다. 굳이 Lua까지 안 가도 되는 케이스가 70% 정도 된다. 우리도 그래서 단순한 건 Fluent Bit, 변환이 좀 복잡해지는 워크로드 몇 개만 Vector로 빼놨다.
운영성과 안정성
이건 진짜 솔직히 둘 다 안 무너진다. 1년 반 동안 Fluent Bit DaemonSet에서 OOMKill 본 건 두세 번 정도, Vector도 비슷하다. Vector 쪽은 buffer 설정이 좀 헷갈려서 처음 한 달은 디스크 buffer가 자꾸 차서 알람이 울렸다. 결국 disk_v2 buffer로 바꾸고 max_size를 명시적으로 잡은 뒤로는 잠잠하다.
Fluent Bit은 storage.path + storage.backlog.mem_limit 조합이 직관적이다. 문서도 더 잘 정리돼 있고, 무엇보다 한국어로 검색해도 자료가 훨씬 많다. 새로 들어온 동료한테 인계할 때 이게 생각보다 크다.
업데이트 주기도 다르다. Fluent Bit은 안정 릴리스가 자주 나오는데 minor 업그레이드에서 깨지는 일이 거의 없다. Vector는 0.4x 대로 진입하면서 config schema가 한두 군데 바뀐 적이 있어서, 운영 환경에서 마이너 올릴 때 dry-run이 필수가 됐다. 큰 문제는 아니지만 운영 부담이 조금 더 있다.
그래서 우리는 뭘 골랐나
아직 못 골랐다. 진짜로.
다만 팀 내부 논의에서 거의 의견이 모인 부분은 있다. 단순 수집/포워딩 + 가벼운 필드 추가만 한다면 Fluent Bit이다. 자원 적게 먹고, 운영 자료 많고, 안정적이다. 복잡한 변환, 특히 여러 source를 합치고 enrichment까지 해야 한다면 Vector. VRL이 진짜 일을 한다. 둘 다 굴리는 게 운영상 부담이 크진 않다. 우리는 그냥 책임 영역을 나눠놨다.
이게 어쩌면 가장 현실적인 답인 것 같다. "Fluent Bit이 나아요" 또는 "Vector가 나아요"라고 단언하는 글을 보면 대부분 사용 사례가 좁다. 우리 같은 보통 팀은 한쪽이 70%, 다른 쪽이 30% 같은 비율로 쓰고 있다.
다만 만약 신규 팀이 지금부터 시작한다면, 나는 Fluent Bit으로 시작하고 필요할 때 Vector를 끼워 넣는 걸 추천한다. 반대 방향(Vector → Fluent Bit)은 VRL로 짜놓은 코드 옮기기가 좀 힘들다.
혹시 다른 팀에서는 어떻게 쓰고 계신가요? 한쪽으로 통일하신 분들 있으면 그 결정 과정이 궁금합니다.
'IT > 모니터링' 카테고리의 다른 글
| Pyroscope 2.0 + eBPF로 continuous profiling 시작하기 (0) | 2026.05.03 |
|---|---|
| 새벽 2시, Loki 인덱스가 터졌다 (0) | 2026.05.01 |
| OpenTelemetry Collector tail sampling, 사실 내부에선 이렇게 돌아간다 (0) | 2026.04.28 |
| Prometheus native histogram, 이제 써볼 때가 됐다 (0) | 2026.04.25 |