본문 바로가기
1. 개발/1.8. ActiveMQ

ZooKeeper 기반의 'Replicated LevelDB'가 사장된 이유는?

by 엉짱 2026. 4. 8.
반응형

ZooKeeper 기반의 'Replicated LevelDB'가 사장된 이유는?

엔터프라이즈 환경에서 ActiveMQ의 고가용성(HA) 아키텍처 역사상 가장 화려하게 등장했다가, 가장 뼈아픈 실패로 기록되며 커뮤니티에서 공식적으로 버려진(Deprecated) 스토리지 엔진이 있습니다. 바로 아파치 주키퍼(ZooKeeper)를 등에 업고 탄생했던 'Replicated LevelDB'입니다.

과거 고가의 공유 스토리지(SAN/NAS) 없이도 로컬 디스크만으로 완벽한 데이터 복제와 자동 페일오버(Failover)를 구현해 주겠다는 이 혁신적인 아키텍처는 수많은 인프라 엔지니어들의 가슴을 설레게 했습니다. 하지만 프로덕션 환경에 투입된 지 얼마 지나지 않아 수많은 재앙적인 버그를 쏟아내며 결국 ActiveMQ 5.14 버전부터 공식적인 사형 선고를 받게 됩니다.

이 가이드에서는 한때 인프라의 '은불환'으로 여겨졌던 Replicated LevelDB가 왜 프로덕션 환경에서 치명적인 시한폭탄으로 전락했는지, 그 아키텍처적 결함과 사장될 수밖에 없었던 3가지 결정적인 원인을 상세히 해부합니다.


1. 화려했던 등장: 'Shared-Nothing' HA의 꿈

이 엔진이 왜 사장되었는지 이해하려면 먼저 왜 도입되었는지를 알아야 합니다.
기존의 ActiveMQ HA는 KahaDB를 SAN이나 NAS와 같은 공유 파일 시스템에 올려놓고 파일 락(OS Lock)을 잡는 방식이었습니다. 하지만 NAS 장비는 비쌌고, 네트워크 파일 시스템 특유의 '고스트 락(Ghost Lock)' 현상으로 인해 마스터가 죽어도 슬레이브가 승격하지 못하는 고질적인 문제가 있었습니다.

이에 대한 완벽한 대안으로 제시된 것이 Replicated LevelDB였습니다.
구글(Google)이 만든 초고속 Key-Value 저장소인 LevelDB를 로컬 스토리지 엔진으로 사용하고, 노드 간의 상태 동기화와 마스터 선출(Quorum)을 분산 코디네이터의 절대 강자인 ZooKeeper에게 맡긴다는 설계였습니다. 공유 디스크 없이 각 서버의 로컬 디스크만으로 동작하는 'Shared-Nothing' 구조는 클라우드 가상화 시대에 완벽히 부합하는 듯 보였습니다.


2. 사장의 결정적 원인 1: JNI 기반 Native 라이브러리의 태생적 불안정성

Replicated LevelDB의 가장 뼈아픈 패착은 핵심 엔진인 LevelDB 자체의 호환성에 있었습니다.

  • Java와 C++의 불협화음: ActiveMQ는 100% Java 기반의 애플리케이션입니다. 하지만 구글의 LevelDB는 C++로 작성된 네이티브(Native) 라이브러리였습니다. 이를 자바에서 돌리기 위해 JNI(Java Native Interface)를 래핑한 라이브러리(Fusesource LevelDB JNI)를 사용해야 했습니다.
  • 커널 패닉과 JVM 크래시: 트래픽이 폭주하여 JVM의 메모리 관리가 한계에 달했을 때, JNI 계층에서 메모리 누수(Memory Leak)가 발생하거나 세그멘테이션 폴트(Segmentation Fault) 에러를 뿜으며 자바 가상 머신 자체가 통째로 죽어버리는(Crash) 끔찍한 사태가 빈번하게 발생했습니다.
  • 복구 불가능한 인덱스 오염: OS 전원이 갑자기 차단되는 하드 크래시(Hard Crash) 상황이 발생하면, LevelDB 고유의 SSTable 파일 구조나 인덱스가 깨져버려 브로커를 영영 다시 기동할 수 없는 데이터 오염(Corruption) 이슈가 끊이지 않았습니다.

3. 사장의 결정적 원인 2: ZooKeeper 종속성으로 인한 운영 복잡도 폭발

HA를 쉽게 하려고 도입한 솔루션이 오히려 인프라 운영 난이도를 안드로메다로 보내버렸습니다.

  • 배보다 배꼽이 더 큰 인프라: 브로커 2대로 HA를 구성하려 했을 뿐인데, ZooKeeper의 정족수(Quorum)를 맞추기 위해 최소 3대의 ZK 노드를 추가로 구축하고 관리해야 했습니다. 관리 포인트가 2대에서 5대로 폭증한 것입니다.
  • 네트워크 파티션의 민감도: ZooKeeper는 네트워크 지연(Latency)과 타임아웃에 극도로 민감합니다. 메인 네트워크 스위치가 1초만 흔들려도 ZooKeeper 클러스터가 널뛰기를 시작하고, 멀쩡히 데이터를 잘 받고 있던 마스터 브로커가 순식간에 권한을 박탈당하며 엉뚱한 슬레이브가 승격하는 불필요한 페일오버(False Failover)가 쉴 새 없이 발생했습니다.

4. 사장의 결정적 원인 3: 최악의 재난, '동기화 깨짐(Split-brain)' 방어 실패

메시지 브로커 HA의 핵심은 "데이터가 양쪽 디스크에 완벽하게 동일하게 존재하는가?"입니다. 하지만 Replicated LevelDB는 이 대원칙을 지켜내지 못했습니다.

  • 복제 파이프라인의 취약성: 마스터 브로커가 메시지를 받아 로컬 LevelDB에 쓰고, 이를 슬레이브로 복제(Replicate)하는 과정에서 네트워크 단절이 발생하면 시스템은 끔찍한 상태에 빠졌습니다.
  • 수동 복구의 악몽: 한 번 마스터와 슬레이브 간의 데이터 동기화 오프셋(Offset)이 어긋나버리면, 아키텍처 스스로 이 간극을 회복하지 못했습니다. 결국 인프라 엔지니어가 한밤중에 장애가 난 서버에 접속하여 기존 슬레이브의 디스크 폴더를 통째로 rm -rf로 날려버리고, 마스터의 LevelDB 폴더 수십 기가바이트를 수동으로 rsync 복사해서 맞춰주어야만 다시 클러스터가 맺어지는 원시적인 운영을 강요받았습니다.

5. 아키텍처의 결론: 사장 이후의 대안 (Best Practices)

이러한 수많은 치명적 결함과 유지보수 인력(Maintainer)의 부재로 인해, 아파치 커뮤니티는 Replicated LevelDB를 완전히 포기했습니다. 그렇다면 현대 인프라에서는 이 로컬 기반 복제(Shared-Nothing)의 꿈을 어떻게 실현하고 있을까요?

A. 차세대 브로커, ActiveMQ Artemis의 등장
ActiveMQ의 차세대 엔진인 Artemis는 이 모든 문제를 완벽하게 해결했습니다. 외부 의존성(ZooKeeper)이나 네이티브 C++ 라이브러리(LevelDB)를 일절 쓰지 않습니다.
Artemis는 100% 순수 Java로 구현된 내장 저널(Journal) 스토리지와, 브로커 자체 내장 통신망을 통한 'Native Data Replication' 아키텍처를 도입하여 추가적인 솔루션 구축 없이 로컬 디스크만으로 완벽한 마스터-슬레이브 복제를 이뤄냈습니다.

B. 레거시 환경에서의 회귀: KahaDB + SAN/NAS
Artemis로 넘어갈 수 없는 기존 ActiveMQ Classic 환경이라면, 모험을 접고 다시 기본으로 돌아가야 합니다. AWS의 EFS나 최신 고성능 SAN 스토리지 장비들은 과거의 불안정했던 NFS 고스트 락 문제를 대부분 하드웨어 레벨에서 해결했습니다. 검증되고 안정적인 Shared File System (KahaDB) 방식으로 회귀하는 것이 가장 현명한 인프라 방어선입니다.

결론적으로 'Replicated LevelDB'의 몰락은 분산 시스템에서 너무 많은 이질적인 기술(Java, C++, ZooKeeper)을 무리하게 접합했을 때 시스템의 결합도(Coupling)가 얼마나 위태로워질 수 있는지를 보여주는 역사적인 아키텍처 반면교사입니다. 화려한 스펙에 현혹되지 말고, 통제 가능하고 검증된 스토리지 아키텍처를 선택하는 엔지니어링의 기본을 되새겨야 할 시점입니다.

반응형