'Symmetric Cluster' 구성 시 모든 노드가 서로를 인식하는 방식?
엔터프라이즈 환경에서 메시지 브로커(ActiveMQ Artemis 등)의 궁극적인 확장성과 고가용성을 달성하기 위한 가장 이상적인 아키텍처는 'Symmetric Cluster(대칭 클러스터)'입니다. 이는 클러스터에 참여하는 모든 브로커 노드가 서로 간에 1:1로 직접 연결되는 완벽한 풀 메쉬(Full Mesh) 형태의 네트워크 토폴로지를 의미합니다.
이러한 구조에서는 어떤 노드에 메시지가 유입되든 가장 최적의 경로를 통해 컨슈머가 대기 중인 노드로 메시지를 즉각 라우팅할 수 있습니다. 하지만 이 거대한 거미줄을 완성하기 위해서는 가장 근본적인 숙제를 먼저 해결해야 합니다. "서버를 막 구동시킨 브로커 노드는, 자신과 함께 클러스터를 구성할 다른 노드들의 IP와 포트를 도대체 어떻게 알아내고 연결을 맺는가?"
이 가이드에서는 브로커들이 서로의 존재를 인지하고 하나의 유기체로 결합하는 두 가지 핵심 메커니즘인 '동적 탐색(Dynamic Discovery)'과 '정적 탐색(Static Discovery)', 그리고 탐색 이후 풀 메쉬가 완성되는 아키텍처적 원리를 상세히 해부합니다.

1. 동적 탐색 (Dynamic Discovery): UDP 멀티캐스트의 메아리
가상 머신(VM)이나 베어메탈 서버 기반의 전통적인 온프레미스(On-premise) 데이터센터 환경에서 가장 널리 쓰이며 강력한 자동화를 제공하는 방식입니다. 브로커의 설정 파일에 다른 서버들의 IP 주소를 일일이 적어둘 필요가 전혀 없다는 것이 최대 장점입니다.
이 방식은 두 가지 핵심 그룹 설정으로 동작합니다.
A. Broadcast Group (방송국 역할)
클러스터 내의 모든 브로커는 자신만의 방송국을 운영합니다. 브로커가 구동되면, 설정된 UDP 멀티캐스트 주소(예: 231.7.7.7)와 포트로 "나는 Node-A이고, 내 접속 주소는 10.0.0.1:61616이다"라는 생존 신고(Heartbeat) 패킷을 주기적으로 방송(Broadcast)합니다.
B. Discovery Group (수신기 역할)
동시에 모든 브로커는 라디오 수신기를 켜두고 동일한 멀티캐스트 주소를 귀 기울여 듣고(Listen) 있습니다. 만약 네트워크에 새로운 Node-B가 추가되어 방송을 시작하면, 기존 브로커들의 수신기가 이 패킷을 낚아챕니다.
패킷을 해독하여 Node-B의 IP와 포트를 알아낸 기존 브로커들은 즉시 Node-B를 향해 TCP 커넥션을 맺으려 시도하며, Node-B 역시 기존 브로커들의 방송을 듣고 연결을 맺어 순식간에 클러스터 망에 편입됩니다.
2. 향상된 동적 탐색: JGroups 프레임워크의 도입
순수한 UDP 멀티캐스트는 구현이 매우 간단하지만, 네트워크 패킷 유실에 취약하고 복잡한 네트워크 분할(Network Partition) 상황을 우아하게 처리하는 데 한계가 있습니다.
이를 극복하기 위해 최신 아키텍처에서는 신뢰할 수 있는 클러스터링 통신의 표준으로 자리 잡은 JGroups 라이브러리를 동적 탐색 엔진으로 활용할 수 있습니다.
JGroups를 사용하면 단순한 UDP 방송을 넘어, 노드 간의 상태를 동기화하고 의사 결정(Quorum)을 내리는 등 훨씬 견고한 분산 시스템의 기반을 다질 수 있습니다. 설정 시 단순 IP가 아닌 JGroups의 XML 설정 파일을 참조하게 하여, 브로커가 네트워크 인프라의 복잡한 멀티캐스트 라우팅 정책을 안전하게 따라갈 수 있도록 지원합니다.
3. 정적 탐색 (Static Discovery): 클라우드 네이티브 시대의 해답
동적 탐색이 완벽해 보이지만, AWS, GCP, Azure와 같은 현대의 퍼블릭 클라우드 환경이나 도커(Docker), 쿠버네티스(Kubernetes) 환경에서는 치명적인 벽에 부딪힙니다. 클라우드 벤더들은 네트워크 대역폭 폭주(Broadcast Storm)를 막고 보안을 유지하기 위해 VPC 내에서의 UDP 멀티캐스트 트래픽을 원천적으로 차단하기 때문입니다.
멀티캐스트가 막힌 환경에서 브로커들을 엮기 위해 고안된 방식이 바로 '정적 탐색(Static Discovery)'입니다.
A. 초기 커넥터(Initial Connectors) 목록의 하드코딩
이 방식에서는 브로커의 설정 파일 내에 클러스터를 구성할 후보 노드들의 접속 주소를 명시적으로 적어주어야 합니다.
<discovery-group name="my-static-group">
<static-connectors>
<connector-ref>node-1-connector</connector-ref>
<connector-ref>node-2-connector</connector-ref>
</static-connectors>
</discovery-group>
B. 토폴로지 전파 (Topology Propagation) 메커니즘
모든 노드의 IP를 다 적어야 한다면 노드가 수십 대일 경우 운영이 불가능할 것입니다. 하지만 놀랍게도 정적 탐색 환경에서는 클러스터 내의 '단 한 대의 브로커'와만 통신이 성공해도 풀 메쉬 클러스터링이 완성됩니다.
- 새로운 Node-D가 구동되며 설정 파일에 적힌 초기 커넥터 목록 중 살아있는 Node-A에게 TCP 연결을 맺습니다.
- 연결이 수립되는 순간, Node-A는 자신이 알고 있는 클러스터의 전체 구조, 즉 '현재 살아있는 모든 브로커(Node-B, Node-C)의 최신 토폴로지 맵'을 Node-D에게 전송해 줍니다.
- 이 토폴로지 지도를 넘겨받은 Node-D는, 설정 파일에 B와 C의 IP가 적혀있지 않더라도 지도를 바탕으로 Node-B와 Node-C에 다이렉트로 TCP 커넥션을 뚫어 스스로 풀 메쉬의 한 축을 담당하게 됩니다.
가십(Gossip) 프로토콜과 유사한 이 전파 메커니즘 덕분에, 쿠버네티스 환경에서도 StatefulSet의 헤드리스 서비스(Headless Service) 주소 몇 개만 초기 커넥터로 지정해 두면, 파드(Pod)들이 늘어나거나 줄어들 때마다 알아서 클러스터 토폴로지가 동적으로 갱신됩니다.
4. 탐색 이후의 과정: Cluster Connection과 인증
동적 탐색이든 정적 탐색이든, 서로의 물리적인 위치(IP와 포트)를 알아내는 것은 1단계에 불과합니다. 노드들이 서로를 신뢰하고 메시지를 주고받는 'Cluster Connection'을 맺기 위해서는 다음과 같은 내부 검증을 거쳐야 합니다.
A. 클러스터 비밀번호 (Cluster Password) 검증
브로커들은 악의적인 서버나 엉뚱한 개발계 서버가 상용 클러스터에 끼어드는 것을 막기 위해, 접속 시 미리 약속된 클러스터 비밀번호(cluster-password)를 교환하여 인증을 수행합니다. 이 암호가 일치하는 노드들끼리만 폐쇄적인 망을 형성합니다.
B. 서버 ID (Node ID) 기반의 루프 방지망 구축
연결이 확정되면, 각 브로커는 디스크에 저장된 고유한 UUID(Node ID)를 서로 교환합니다. 이 Node ID는 이후 브로커 간에 메시지가 이동할 때마다 메시지 헤더에 스탬프처럼 찍혀, 메시지가 클러스터 내부를 무한히 맴도는 핑퐁(Ping-Pong) 현상이나 무한 루프를 커널 레벨에서 차단하는 핵심 라우팅 식별자로 사용됩니다.
결론적으로 'Symmetric Cluster' 아키텍처는 노드 간의 물리적인 위치를 찾아내는 디스커버리(Discovery) 계층과, 찾아낸 노드들 간에 신뢰망을 엮고 토폴로지를 실시간으로 동기화하는 클러스터 커넥션(Cluster Connection) 계층이 완벽하게 분리된 구조를 띱니다. 인프라의 네트워크 제약(멀티캐스트 허용 여부)에 맞춰 탐색 방식을 현명하게 선택함으로써, 어떤 클라우드나 온프레미스 환경에서도 견고하고 자가 치유(Self-healing)가 가능한 메시징 백본을 완성하시기 바랍니다.
'1. 개발 > 1.8. ActiveMQ' 카테고리의 다른 글
| 쿠버네티스 'Headless Service'를 이용한 브로커 피어 발견 방법? (0) | 2026.04.12 |
|---|---|
| 'Static Cluster Nodes' 설정과 'UDP/Multicast Discovery'의 장단점? (1) | 2026.04.11 |
| 클러스터 내에서 'Server ID' 중복 시 발생하는 현상은? (0) | 2026.04.11 |
| 'redistribution-delay' 수치 결정 시 고려해야 할 네트워크 상황? (0) | 2026.04.11 |
| 'Message Redistribution' 기능이 컨슈머가 없는 노드의 메시지를 옮기는 시점? (0) | 2026.04.11 |