2025. 11. 9. 21:54ㆍ프로그래밍
우리 팀의 역할
우리 팀은 하루 160억 RPD(Request Per Day) 규모의 트래픽을 처리하는 API 게이트웨이 플랫폼 팀이다.
모든 외부 요청이 게이트웨이를 거쳐 들어오며, 여기서 보안, 인증, 세션 검증 등 공통 로직을 처리한 뒤 서비스 서버로 전달된다.
즉, 서비스의 첫 번째 관문이자 로그의 출발점이 바로 우리 팀이다.
이 구조에서 로그의 정확성과 중복 관리가 곧 시스템 신뢰성의 핵심이었다.
Kafka 기반 구조에서 중복 로그 발생
기존 구조
App → Kafka Producer → Kafka Broker → Consumer → S3
Kafka는 스트리밍 파이프라인에 강력하지만, 로그 적재용으로 사용할 경우 중복이 쉽게 발생한다.
Kafka의 전달 보장 모델은 At-Least-Once —
“절대 빠지지 않게 보내는 대신, 같은 메시지가 여러 번 전달될 수 있다.”
중복의 주요 원인
- Producer 재시도
- 네트워크 지연으로 ACK을 받지 못하면 같은 메시지를 다시 전송.
- Consumer 재처리
- S3 업로드 후 offset 커밋 전에 장애가 나면 재시작 시 같은 메시지를 다시 읽음.
- S3의 append-only 구조
- putObject()는 같은 내용을 덮어쓰지 않고 새로운 객체로 저장.
하루 160억 요청 중 단 0.5%만 중복돼도 매일 수백 GB가 낭비됐다.
중복이 싫으면 ACK를 끄면 되는거 아니냐는 생각이 들수도 있을거같다. 하지만 이런 경우 데이터 유실이 생긴다.
Kafka는 기본적으로 “신뢰성 > 정확성” 철학이라 ACK을 꺼버리면 메시지가 일부 손실될 수 있다. 우리 팀의 경우 이 방법을 시도해봤지만 생각보다 많은 양의 데이터 누실이 발생했다.
즉, 중복을 피할 순 있어도 “로그 누락”이라는 더 큰 리스크를 감수해야 한다. 요구사항중 데이터 누락은 최소화 해야한다는 조건이 있었다.
또, 그럼 S3에서 제공하는 KEY를 사용하면 되는거 아니냐는 생각이 들수도 있을거같다.
물론 맞다. RPD가 적은 팀이라면 S3에서 제공하는 KEY를 사용해서 처리할수 있을거같다. 하지만 여러가지 문제점이 발생하게되는데,
1. S3 내부 shard 및 index update 지연 가능
2. 동일 키에 대한 중복 put 요청 증가로 비용 급증 이라는 문제가 발생했다.
메모리가 적은 Fluentbit도 고려했고 POC까지 진행했지만 로깅 포멧 가공이 불가능하다는 단점으로 요구사항에 맞지않아서 사용하지못했다.
Fluentd Sidecar 구조로 전환
Kafka를 제거하고, ECS Task마다 Fluentd Sidecar를 붙였다.
App (stdout/log)
↓
Fluentd Sidecar (in_tail → buffer → out_s3)
↓
S3
↓
Lambda (중복 검증)
Fluentd는 로그 수집에 특화되어 있으며, 각 컨테이너 단위로 동작하기 때문에 장애가 격리된다.
1분 단위 로그 롤링과 pos_file 기반 이어읽기를 조합해 중복 없는 안정적 로그 수집을 구현했다.
Fluentd가 중복을 만들지 않는 이유
Fluentd는 Kafka와 달리 읽기, 버퍼링, 전송, 커밋이 모두 하나의 프로세스 안에서 일관되게 처리된다. 이 덕분에 중복이 발생할 구조가 없다.
1. in_tail의 위치 기반 읽기
- 로그 파일을 라인 단위로 읽고 현재 읽은 위치를 pos_file에 기록한다.
- Fluentd가 재시작하더라도 마지막으로 읽은 지점부터 이어서 읽기 때문에 이미 처리된 로그를 다시 읽지 않는다.
2. 버퍼-전송-커밋이 원자적으로 동작
- 로그는 버퍼에 쌓인 뒤 chunk 단위로 업로드된다.
- 업로드가 성공해야 버퍼가 삭제된다.
- 즉, 업로드 완료 시점이 커밋 시점이며, 재시작하거나 네트워크 오류가 발생해도 동일 chunk가 중복 업로드되지 않는다.
3. Chunk ID 기반 Key 관리
- 같은 chunk가 재시도될 경우 동일한 Chunk ID로 overwrite된다.
- 중복 업로드가 발생하더라도 결과적으로 하나의 object만 남는다.
4. 로그 파일 롤링 정책
- 1분 단위로 로그 파일을 교체하기 때문에 이전 파일은 읽기 완료 후 닫히고, 새 파일만 추적된다.
- 파일 교체 시점이 명확해 중복 읽기 가능성이 사실상 0에 수렴한다.
결과적으로 Fluentd는 업로드 성공 = 커밋 완료 구조를 가지며, Kafka처럼 “읽었지만 커밋되지 않은 상태”가 존재하지 않는다.
이 구조적 차이 덕분에 Fluentd는 본질적으로 중복이 발생하지 않는다.
Lambda 기반 중복 검증 (알림 없이 검증 전용)
Kafka → Fluentd 전환 초기에는 S3에 저장되는 로그의 중복 여부를 검증하기 위해 Lambda를 추가했다.
- S3:ObjectCreated:* 이벤트 발생 시 Lambda 실행
- 새 Object의 traceId / requestId를 DynamoDB에 기록
- 이미 존재하는 키일 경우 중복으로 로깅
- 알림 전송 없이 중복률 집계 및 모니터링용 로그로만 사용
Lambda는 운영 개입 목적이 아닌, Fluentd 적재 체계의 신뢰성을 검증하기 위한 내부 모니터링 도구였다.
시스템 안정화 이후 Lambda는 필요 시 수동 점검용으로만 유지되었다.
gzip 압축 저장 — 다른 압축 방식과 비교 및 선택 이유
Fluentd의 out_s3 플러그인에서는 여러 압축 코덱을 선택할 수 있다. (gzip, snappy, lz4, zstd 등)
우리 팀은 이 중 gzip을 최종 선택했다.
압축 코덱 비교
코덱 압축률 (텍스트 로그 기준) 압축 속도 해제 속도 S3/Athena 호환성 특징
| gzip | 70~90% 절감 | 중간 | 빠름 | 완벽 지원 | 표준적, CPU 부담 적당 |
| snappy | 40~60% 절감 | 매우 빠름 | 매우 빠름 | 일부 제한적 | 속도 위주, 용량 절감 낮음 |
| lz4 | 50~70% 절감 | 빠름 | 빠름 | 일부 호환성 이슈 | 실시간 처리에 적합 |
| zstd | 75~92% 절감 | 느림~보통 | 중간 | 지원 (Glue/Athena) | 높은 압축률, CPU 부하 큼 |
160억 RPD 규모에서의 성과 비교
항목 Kafka 기반 Fluentd 전환 후
| 하루 로그량 | 약 160억 건 | 동일 |
| 중복률 | 0.5~1% | 0.001% 이하 |
| S3 비용 | 급증 | 약 30% 절감 |
| Athena 정확도 | 중복으로 왜곡 | 정확도 향상 |
| 장애 영향 | 전역 리밸런싱 시 전체 영향 | 컨테이너 단위 격리 |
| 중복 감시 | Lambda 필수 | 선택적 검증 |
결론
Kafka는 이벤트 스트리밍에는 탁월하지만, 로그 적재 관점에서는 중복 방지 설계가 약하다.
반면 Fluentd는 로깅 파이프라인에 특화된 구조로, 업로드 성공 시점이 곧 커밋이므로 중복이 원천 차단된다.
160억 RPD 규모의 트래픽에서도 Fluentd Sidecar 구조는 안정적으로 중복 없는 로그 적재를 구현했다.
Kafka 시절의 중복 문제는 Fluentd의 pos_file 기반 tail 로직과 chunk 단위 버퍼링으로 완전히 해소되었다.
비록 약간의 중복이 발생을 하긴하고있지만, 중복량이 급격히 줄어들어 S3 로깅에 사용된 비용이 30퍼가량 줄어들었다.
'프로그래밍' 카테고리의 다른 글
| [Refactoring] 리팩토링 기법 - 작성중 (0) | 2025.10.16 |
|---|---|
| 윈도우 Alt키 <-> Ctrl command 키 위치 변경 - 윈도우 키보드 맥북처럼 사용하기 (10) | 2023.01.28 |
| [JAVA] call by reference vs call by value (0) | 2021.06.30 |
| [C++]2차원 배열 내장 STL 함수로 SORT하기 (0) | 2020.05.20 |