Apache Kafka Tiered Storage: 로컬 디스크와 오브젝트 스토리지를 분리한 무한 로그 보존 운영
요약
Apache Kafka Tiered Storage는 브로커 로컬 디스크(Hot Tier)와 원격 오브젝트 스토리지(Remote Tier, 예: Amazon S3)를 하나의 토픽에 투명하게 결합한다. KIP-405로 제안되어 Kafka 3.6(2023년 9월)에서 정식 기능(GA)이 된 이 기능은 Kafka 4.3(2026년 5월, KRaft 완전 지원)에서 안정성과 성능이 강화됐다. 컨슈머는 기존 Fetch API로 두 계층을 구분 없이 읽는다—브로커가 완전히 중개한다. 이 글은 Tiered Storage의 동작 원리, RemoteLogManager 아키텍처, 핵심 설정값, 운영 모니터링, 그리고 실제 트레이드오프를 상세히 다룬다.
1. 배경: 로컬 디스크만으로 장기 보존이 어려운 이유
전통적인 Kafka 운영에서 보존 기간을 30~90일로 설정하면 무슨 일이 일어나는가?
토픽당 1 TB/일 처리량 × 복제 팩터 3 × 보존 90일 = 270 TB 로컬 스토리지가 필요하다. NVMe SSD 기준 비용은 GB당 약 $0.15~$0.25이고, Amazon S3 Standard는 GB당 $0.023이다—10배 이상 차이다.
비용 외에도 로컬 디스크 기반 장기 보존에는 운영 문제가 있다:
- 브로커 복구 시간: 장애 후 재조인 시 TB 단위 로그를 리더에서 복제해야 한다. 복제 처리량이 1 GB/s라도 100 TB 복구에는 약 28시간이 필요하다.
- 파티션 재배치 비용: 브로커 추가 시 리밸런싱이 수십 TB를 이동한다.
- 디스크 증설 압박: 데이터 양이 증가할 때마다 브로커 노드를 수평 확장해야 한다.
Tiered Storage는 이 구조를 바꾼다. 브로커는 최근 데이터만 로컬에 유지하고, 오래된 세그먼트는 S3 같은 저렴한 오브젝트 스토리지로 자동 이동한다.
2. Tiered Storage 개념 모델
Tiered Storage는 토픽의 로그를 두 계층으로 나눈다.
Hot Tier (로컬 디스크):
- 최근 세그먼트 보관 (
local.retention.ms로 제어) - 브로커 재시작 없이 즉시 서빙 가능한 고속 경로
- 일반적으로 최근 1~7일 분량을 유지
Remote Tier (오브젝트 스토리지):
- 오래된 세그먼트 보관 (S3, GCS, ADLS 등 선택 가능)
log.retention.ms까지 보존 (로컬 + 원격 합산)- 브로커가 요청 시 다운로드해 컨슈머에게 투명하게 서빙
컨슈머는 Fetch API 응답에서 두 계층을 구분할 수 없다. 브로커가 오프셋을 기준으로 어느 계층에서 데이터를 꺼낼지 판단하고 일관된 응답을 반환한다.
3. RemoteLogManager 아키텍처
Tiered Storage의 핵심 구성 요소는 세 가지다.
RemoteLogManager (RLM):
- 각 브로커에 내장된 백그라운드 서비스
- 닫힌(rolled) 로컬 세그먼트를 Remote Storage로 업로드
local.retention.ms가 만료된 로컬 세그먼트를 삭제- 파티션 리더 브로커가 업로드 책임을 가짐(팔로워는 업로드하지 않음)
RemoteLogMetadataManager (RLMM):
- 어느 세그먼트가 어디 업로드됐는지 메타데이터 추적
- 기본 구현: 내부 Kafka 토픽
__remote_log_metadata사용 - 컨슈머 오프셋이 어느 계층에 있는지 판단하는 근거
RemoteStoragePlugin (RSP):
- 실제 오브젝트 스토리지와의 인터페이스
RemoteStorageManagerJava 인터페이스를 구현해야 함- 커뮤니티 플러그인: Aiven Open Source S3 플러그인, Conduktor 플러그인 등
- Confluent Platform: 공식 상용 플러그인 포함
4. 세그먼트 생애 주기
로그 세그먼트 하나의 전체 흐름을 순서대로 설명한다.
① 로컬 쓰기 프로듀서가 메시지를 보내면 브로커가 로컬 세그먼트에 append한다. log.segment.bytes(기본 1 GB) 또는 log.roll.hours(기본 168시간) 조건을 만족하면 세그먼트가 닫힌다(rolled).
② Remote 업로드 세그먼트가 닫히면 RLM이 해당 세그먼트를 Remote Storage에 복사한다. 업로드는 리더 브로커가 담당하며, 성공하면 RLMM에 메타데이터를 기록한다. 업로드 완료 전에는 로컬 세그먼트를 삭제하지 않는다.
③ 로컬 보존 만료 local.retention.ms 이후 로컬 세그먼트가 삭제된다. 이 시점부터 해당 오프셋 범위의 데이터는 Remote Tier에서만 제공된다.
④ 전체 보존 만료 log.retention.ms 이후 Remote 세그먼트도 삭제된다. RLMM 메타데이터도 정리된다. 이 오프셋 이전 데이터는 영구 삭제된다.
5. 핵심 설정값
클러스터 레벨 (server.properties):
# Tiered Storage 전역 활성화 (모든 브로커 동일하게 설정)
remote.log.storage.system.enable=true
# Remote Storage Plugin 구현 클래스 (Aiven 오픈소스 S3 플러그인 예시)
remote.log.storage.manager.class.name=io.aiven.kafka.tieredstorage.RemoteStorageManager
remote.log.storage.manager.class.path=/opt/kafka/plugins/tiered-storage/*.jar
# S3 플러그인 상세 설정 (플러그인마다 다름)
remote.log.storage.manager.impl.prefix=remote.log.storage.manager.s3.
remote.log.storage.manager.s3.bucket.name=my-kafka-tiered-storage
remote.log.storage.manager.s3.region=ap-northeast-2
# RLMM: 기본 구현은 내부 Kafka 토픽 기반
remote.log.metadata.manager.class.name=org.apache.kafka.server.log.remote.metadata.storage.TopicBasedRemoteLogMetadataManager토픽 레벨 (토픽 생성 또는 수정 시):
# 이 토픽에서 Tiered Storage 활성화
remote.log.storage.enable=true
# 로컬 보존 기간: 최근 1일만 로컬 디스크에 유지
local.retention.ms=86400000
# 전체 보존 기간: 30일 (로컬 1일 + 원격 29일)
log.retention.ms=2592000000
# 로컬 보존 크기 상한 (선택, 디스크 용량 제한 시)
local.retention.bytes=10737418240 # 10 GB주의:
local.retention.ms는 반드시log.retention.ms보다 작아야 한다. 같거나 크면 Tiered Storage가 아무 효과도 없다.
6. 읽기 경로: 브로커 중개 방식 상세
컨슈머가 Remote Tier 오프셋을 Fetch할 때 브로커는 다음 단계를 수행한다:
- RLMM에서 해당 오프셋이 속한 Remote 세그먼트 식별
- Remote Storage에서 세그먼트 인덱스 파일(
.index,.timeindex) 다운로드 - 인덱스에서 정확한 파일 내 위치(byte offset) 계산
- 실제 데이터 파일을 범위(range) GET으로 다운로드
- 컨슈머에게 표준 Fetch Response로 반환
중요한 운영 함의: Remote Fetch는 브로커 CPU, 메모리, 아웃바운드 네트워크를 사용한다. 대규모 히스토리 리플레이가 발생하면(예: 데이터 재처리 파이프라인이 한 달 전 데이터를 처음부터 읽는 경우) 브로커 부하가 급증할 수 있다.
Remote Fetch 전용 스레드 풀을 별도로 관리해 일반 프로듀서-컨슈머 트래픽에 영향을 주지 않도록 한다:
# Remote Fetch 스레드 수 (기본 10, 워크로드에 따라 조정)
remote.fetch.min.bytes=1
remote.fetch.max.wait.ms=5007. 운영 모니터링: 핵심 메트릭과 알림
업로드 건강도:
| 메트릭 | 설명 | 알림 기준 |
|---|---|---|
kafka.server:type=BrokerTopicMetrics,name=RemoteBytesOutPerSec | 초당 Remote 업로드 바이트 | 0으로 수렴 시 RLM 장애 의심 |
kafka.server:type=RemoteLogManagerMetrics,name=TaskQueueSize | 업로드 대기 세그먼트 수 | 지속 증가 시 업로드 병목 |
kafka.server:type=RemoteLogManagerMetrics,name=UploadLagBytes | 업로드 미완료 바이트 | local.retention.ms의 두 배 이상이면 위험 |
읽기 건강도:
| 메트릭 | 설명 | 알림 기준 |
|---|---|---|
kafka.server:type=BrokerTopicMetrics,name=RemoteBytesInPerSec | Remote에서 읽어온 바이트 | 급증 시 히스토리 재처리 확인 |
kafka.server:type=RemoteLogFetchMetrics,name=FetchLatencyMs | Remote Fetch 평균 지연(ms) | p99 > 500ms 지속 시 S3 연결 점검 |
RLMM 상태:
# 내부 메타데이터 토픽 컨슈머 그룹 지연 확인
kafka-consumer-groups.sh --bootstrap-server broker:9092 \
--describe --group __remote_log_metadata_consumer_groupRLMM 내부 컨슈머의 Lag이 0이 아니면 오프셋-위치 조회 정확도에 영향을 줄 수 있다. 모든 파티션에서 Lag=0을 유지해야 한다.
8. 한계와 운영 트레이드오프
지연 증가: Remote Tier Fetch 시 S3 API 호출 지연이 더해진다. AWS S3 GET latency는 p50 약 10~30ms, p99 100~300ms다. 로컬 디스크 대비 10~100배 높다. 리얼타임 스트리밍 컨슈머가 히스토리 오프셋을 읽어야 한다면 이 지연을 SLA에 반영해야 한다.
S3 비용 계산: 스토리지 비용은 크게 줄지만 API 호출 비용이 추가된다. 많은 컨슈머가 오래된 데이터를 반복 읽으면 S3 GET 비용이 예상 외로 높아진다. Broker-side Remote 세그먼트 캐시(KIP-891)를 활성화하면 같은 세그먼트를 반복 다운로드하는 것을 방지한다.
Remote Storage 가용성 의존: S3가 다운되면 Remote Tier 데이터를 읽을 수 없다. 로컬 보존 기간 이전 데이터가 필요한 컨슈머는 오류를 받는다. 재처리 파이프라인 설계 시 S3 availability SLA를 반드시 고려해야 한다.
업로드 실패 처리: 일시적 S3 오류 시 RLM이 재시도한다. 장기 S3 장애 시 업로드 큐가 누적되면서 로컬 디스크가 가득 찰 수 있다. UploadLagBytes 알림과 로컬 디스크 사용률 알림을 함께 설정해야 한다.
브로커 재시작 영향: 브로커 재시작 시 RLMM이 내부 토픽을 재구독하고 메타데이터를 재로드한다. 대용량 토픽에서는 이 과정이 수 분 걸릴 수 있으며, 그 동안 Remote Tier Fetch가 차단될 수 있다.
9. Kafka 4.3(2026년 5월)에서의 개선
KRaft 완전 지원: Kafka 4.0에서 ZooKeeper 모드가 제거된 이후 KRaft 환경에서의 RLMM 안정성이 추가 검증됐다. 컨트롤러 쿼럼 리더 변경 시 RLMM 구독이 올바르게 재초기화된다.
RemoteLogManager 성능 개선: 업로드 병렬화 및 메타데이터 일괄 처리가 개선돼 고처리량 토픽에서 업로드 지연이 줄었다. 세그먼트 메타데이터 게시 지연이 감소해 RLMM Lag 문제가 완화됐다.
MirrorMaker 2 연동: 클러스터 간 복제 시 Remote Tier 메타데이터도 복제해 DR 클러스터에서도 히스토리 데이터에 접근할 수 있다.
Share Groups(KIP-932) 호환: Kafka 4.x의 새 컨슈머 모델인 Share Groups에서 Tiered Storage 파티션도 올바르게 처리된다.
10. 도입 체크리스트
Tiered Storage를 프로덕션 도입 전 확인할 항목.
설계 단계:
- [ ] Remote Storage 플러그인 선택(S3, GCS, ADLS) 및 IAM/서비스 계정 설정
- [ ]
local.retention.ms와log.retention.ms값 결정 (비용 vs 지연 트레이드오프 계산) - [ ] 최악의 Remote Fetch 지연을 컨슈머 SLA와 대조
배포 단계:
- [ ] 클러스터 전체에
remote.log.storage.system.enable=true설정 후 롤링 재시작 - [ ] 파일럿 토픽 1~2개에 먼저 활성화 후
UploadLagBytes및 원격 스토리지 객체 수 확인 - [ ] RLMM 내부 토픽(
__remote_log_metadata) Lag 모니터링 설정
운영 단계:
- [ ] Remote Fetch 지연 알림 (p99 > 500ms)
- [ ] UploadLagBytes 알림 (2 × local.retention.ms 초과 시)
- [ ] 로컬 디스크 사용률 알림 (> 80%)
- [ ] 분기별 DR 드릴: S3 장애 시 컨슈머 동작 확인
요점 정리
- Kafka Tiered Storage는 Hot Tier(로컬 디스크)와 Remote Tier(S3 등)를 하나의 토픽에 투명하게 결합해 장기 보존 비용을 10배 이상 줄인다.
local.retention.ms로 로컬 보존 기간을,log.retention.ms로 전체 보존 기간을 독립적으로 제어한다.- RemoteLogManager가 세그먼트를 업로드하고, RemoteLogMetadataManager가 오프셋-위치 매핑을 추적한다.
- Remote Fetch 지연(p50 10~30ms, p99 100~300ms)과 S3 GET 비용을 사전에 계산하고 컨슈머 SLA에 반영해야 한다.
- 업로드 지연(
UploadLagBytes), Remote Fetch 지연, RLMM 컨슈머 Lag 세 지표를 핵심 운영 SLI로 관리한다.
References
- KIP-405 Kafka Tiered Storage 설계 문서: https://cwiki.apache.org/confluence/display/KAFKA/KIP-405%3A+Kafka+Tiered+Storage
- Kafka 4.3 릴리스 노트: https://kafka.apache.org/downloads
- Aiven Tiered Storage for Apache Kafka (오픈소스 S3 플러그인): https://github.com/Aiven-Open/tiered-storage-for-apache-kafka
- KIP-891 브로커 측 Remote 세그먼트 캐시: https://cwiki.apache.org/confluence/display/KAFKA/KIP-891%3A+Broker-side+JBOD+Tiered+Storage+caching
- Confluent Tiered Storage 운영 문서: https://docs.confluent.io/platform/current/kafka/tiered-storage.html
- Kafka 공식 Tiered Storage 문서: https://kafka.apache.org/documentation/#tiered_storage