LLM WikiAccess-protected knowledge portal
← 스터디 홈
161편 · 약 14분

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):

  • 실제 오브젝트 스토리지와의 인터페이스
  • RemoteStorageManager Java 인터페이스를 구현해야 함
  • 커뮤니티 플러그인: Aiven Open Source S3 플러그인, Conduktor 플러그인 등
  • Confluent Platform: 공식 상용 플러그인 포함
쓰기 경로 (프로듀서 → 브로커)
프로듀서: 메시지 전송
브로커: 로컬 세그먼트 append
↓ 세그먼트 닫힘(roll)
RemoteLogManager: 업로드 감지
↓ 업로드 성공
Remote Tier (S3/GCS/ADLS)
↓ local.retention.ms 만료
로컬 세그먼트 삭제
읽기 경로 (컨슈머 → 브로커)
컨슈머: Fetch(offset=X)
브로커: RLMM 조회 → 계층 판단
Hot Tier에 있으면
로컬 디스크에서 즉시 반환 (<1ms)
Remote Tier에 있으면
S3 GET → 브로커 캐시 → 반환 (10~200ms)
RemoteLogMetadataManager
__remote_log_metadata 토픽으로 세그먼트 위치 추적
Apache Kafka Tiered Storage 아키텍처: 쓰기·업로드·읽기 경로

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할 때 브로커는 다음 단계를 수행한다:

  1. RLMM에서 해당 오프셋이 속한 Remote 세그먼트 식별
  2. Remote Storage에서 세그먼트 인덱스 파일(.index, .timeindex) 다운로드
  3. 인덱스에서 정확한 파일 내 위치(byte offset) 계산
  4. 실제 데이터 파일을 범위(range) GET으로 다운로드
  5. 컨슈머에게 표준 Fetch Response로 반환

중요한 운영 함의: Remote Fetch는 브로커 CPU, 메모리, 아웃바운드 네트워크를 사용한다. 대규모 히스토리 리플레이가 발생하면(예: 데이터 재처리 파이프라인이 한 달 전 데이터를 처음부터 읽는 경우) 브로커 부하가 급증할 수 있다.

Remote Fetch 전용 스레드 풀을 별도로 관리해 일반 프로듀서-컨슈머 트래픽에 영향을 주지 않도록 한다:

# Remote Fetch 스레드 수 (기본 10, 워크로드에 따라 조정)
remote.fetch.min.bytes=1
remote.fetch.max.wait.ms=500

7. 운영 모니터링: 핵심 메트릭과 알림

업로드 건강도:

메트릭설명알림 기준
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=RemoteBytesInPerSecRemote에서 읽어온 바이트급증 시 히스토리 재처리 확인
kafka.server:type=RemoteLogFetchMetrics,name=FetchLatencyMsRemote Fetch 평균 지연(ms)p99 > 500ms 지속 시 S3 연결 점검

RLMM 상태:

# 내부 메타데이터 토픽 컨슈머 그룹 지연 확인
kafka-consumer-groups.sh --bootstrap-server broker:9092 \
  --describe --group __remote_log_metadata_consumer_group

RLMM 내부 컨슈머의 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.mslog.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