RabbitMQ 브로커 하나가 갑자기 죽었을 때, 그 안에 있던 수만 건의 메시지는 어디로 가는 걸까?

클러스터링 기초

왜 클러스터링이 필요한가

지금까지 다뤘던 RabbitMQ 기능들 — Durable Queue, Persistent 메시지 포함 — 은 모두 단일 노드 기준이었습니다. 메시지를 디스크에 저장하더라도 그 디스크가 있는 서버 자체가 죽으면 결국 서비스가 중단됩니다.

단일 노드의 한계를 정리하면 다음과 같습니다.

  • ** 단일 장애점(SPOF)**: 노드 하나가 곧 전체 시스템의 장애점
  • ** 처리량 한계 **: CPU, 메모리, 네트워크 대역폭이 한 서버에 묶임
  • ** 유지보수 불가 **: 패치나 업그레이드 시 서비스 중단이 불가피

클러스터링은 이 세 가지를 동시에 해결합니다. 여러 노드가 하나의 논리적 브로커로 동작하면서 ** 고가용성(HA)**과 ** 수평 확장 **을 모두 달성할 수 있습니다.


클러스터 기본 구조

노드 구성과 Erlang Cookie

RabbitMQ는 Erlang/OTP 위에서 동작합니다. 클러스터를 구성하려면 모든 노드가 ** 동일한 Erlang Cookie**를 공유해야 합니다. Erlang Cookie는 노드 간 통신의 인증 토큰 역할을 합니다.

BASH
# Erlang Cookie 위치 (Linux 기준)
cat /var/lib/rabbitmq/.erlang.cookie

# 모든 노드에 동일한 Cookie 값을 설정해야 한다

클러스터 구성 명령은 간단합니다.

BASH
# node2에서 node1의 클러스터에 합류
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app

# 클러스터 상태 확인
rabbitmqctl cluster_status

노드 간 공유 정보

클러스터를 구성하면 모든 노드가 다음 정보를 ** 자동으로 공유 **합니다.

공유되는 것공유되지 않는 것
Exchange 정의** 메시지 데이터** (기본적으로)
Queue 메타데이터 (이름, 속성)Queue의 실제 메시지
Binding 정보-
Vhost 정보-
사용자/권한 정보-

클러스터를 구성했다고 해서 메시지가 자동으로 복제되는 건 아닙니다. 메타데이터만 공유될 뿐, 메시지 복제는 별도로 설정해야 합니다.

이 점을 놓치면 "클러스터 구성했으니까 고가용성 확보됐겠지"라고 착각하기 쉽습니다.

클러스터 아키텍처

PLAINTEXT
┌─────────────────────────────────────────────────┐
│              RabbitMQ Cluster                    │
│                                                 │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐   │
│  │  Node 1   │  │  Node 2   │  │  Node 3   │   │
│  │ (Disc)    │  │ (Disc)    │  │ (RAM)     │   │
│  │           │  │           │  │           │   │
│  │ Queue-A   │  │ Queue-B   │  │ Queue-C   │   │
│  │ [메시지]  │  │ [메시지]  │  │ [메시지]  │   │
│  └─────┬─────┘  └─────┬─────┘  └─────┬─────┘   │
│        │              │              │          │
│        └──────────────┼──────────────┘          │
│            메타데이터 동기화 (자동)               │
│            메시지 복제 (별도 설정 필요)            │
└─────────────────────────────────────────────────┘

클라이언트는 어떤 노드에 연결하든 클러스터 전체의 Exchange와 Queue에 접근할 수 있습니다. 다만 메시지가 실제로 존재하는 노드가 아닌 다른 노드에 연결하면, 내부적으로 노드 간 프록시가 발생하여 약간의 오버헤드가 생깁니다.


Disc Node vs RAM Node

공부하다 보니 여기서 많이 헷갈렸습니다. "Disc Node는 메시지를 디스크에, RAM Node는 메모리에 저장한다"고 생각하기 쉬운데, 이건 ** 잘못된 이해 **입니다.

실제 차이점

Disc/RAM의 구분은 ** 메타데이터 저장 위치 **에 관한 것입니다.

구분Disc NodeRAM Node
** 메타데이터 저장**디스크 + 메모리메모리에만
** 메시지 저장**설정에 따름 (동일)설정에 따름 (동일)
** 재시작 시**메타데이터 자체 복원다른 Disc Node에서 동기화
** 성능**안정적라우팅 연산이 약간 빠름

Disc Node든 RAM Node든 Persistent 메시지는 디스크에 저장됩니다. 차이는 큐 정의, Exchange, 바인딩 같은 ** 메타데이터 **를 어디에 보관하느냐입니다.

최소 요구사항

RabbitMQ 클러스터는 ** 최소 1개의 Disc Node**가 반드시 필요합니다. 모든 노드가 RAM이면 클러스터 전체 재시작 시 메타데이터를 복원할 수 없기 때문입니다.

실무에서는 대부분 ** 모든 노드를 Disc Node로 구성 **합니다. RAM Node의 성능 이점이 크지 않고, 안정성이 더 중요하기 때문입니다.

BASH
# RAM Node로 클러스터에 합류
rabbitmqctl join_cluster --ram rabbit@node1

# 나중에 Disc Node로 변경
rabbitmqctl change_cluster_node_type disc

Classic Mirrored Queue (레거시)

동작 방식

Classic Mirrored Queue는 RabbitMQ 초기부터 제공된 고가용성 방법입니다. ** 마스터 큐 **가 하나의 노드에 존재하고, 다른 노드에 ** 미러(복제본)**를 생성하는 구조입니다.

PLAINTEXT
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│    Node 1    │    │    Node 2    │    │    Node 3    │
│              │    │              │    │              │
│ ┌──────────┐ │    │ ┌──────────┐ │    │ ┌──────────┐ │
│ │ Master Q │─┼────┼▶│ Mirror Q │ │    │ │ Mirror Q │ │
│ │ (원본)   │─┼────┼─┼──────────┼─┼────┼▶│ (복제본) │ │
│ └──────────┘ │    │ └──────────┘ │    │ └──────────┘ │
└──────────────┘    └──────────────┘    └──────────────┘
         │                 ▲                    ▲
         │       비동기 복제 (async replication)  │
         └────────────────────────────────────────┘

ha-policy 설정

BASH
# 모든 큐를 모든 노드에 미러링
rabbitmqctl set_policy ha-all ".*" '{"ha-mode":"all"}'

# 특정 큐를 2개 노드에 미러링
rabbitmqctl set_policy ha-two "^order\." '{"ha-mode":"exactly","ha-params":2}'

# 동기화 모드 설정
rabbitmqctl set_policy ha-sync ".*" \
  '{"ha-mode":"all","ha-sync-mode":"automatic"}'

한계점

Classic Mirrored Queue는 여러 심각한 문제가 있어서 RabbitMQ 3.13부터 **더 이상 사용이 권장되지 않습니다 **.

  1. ** 비동기 복제 **: 마스터에 메시지가 쓰인 후 미러에 복제되기까지 시간 차가 있습니다. 마스터가 복제 전에 죽으면 메시지가 유실됩니다.

  2. ** 동기화 지연 **: 새 미러가 추가되거나 노드가 복구되면 기존 메시지를 전부 동기화해야 합니다. 이 과정에서 큐가 블로킹되어 서비스에 영향을 줍니다.

  3. **split-brain 취약성 **: 네트워크 파티션 발생 시 양쪽 파티션 모두 자신이 마스터라고 판단하는 split-brain 상태가 발생할 수 있습니다.

  4. ** 성능 오버헤드 **: ha-mode: all로 설정하면 모든 노드에 복제해야 하므로 노드 수가 늘어날수록 쓰기 성능이 저하됩니다.


Quorum Queue — Classic의 대체자

Raft 합의 알고리즘

Quorum Queue는 Raft 합의 알고리즘 을 기반으로 동작합니다. Raft는 분산 시스템에서 데이터 일관성을 보장하기 위한 합의 프로토콜로, 리더 선출과 로그 복제를 핵심 메커니즘으로 사용합니다.

PLAINTEXT
                    메시지 발행


┌──────────────────────────────────────────────┐
│               Quorum Queue                    │
│                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  Node 1  │  │  Node 2  │  │  Node 3  │   │
│  │ (Leader) │  │(Follower)│  │(Follower)│   │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘   │
│       │              │              │        │
│       │   1. 메시지 복제 요청        │        │
│       ├─────────────▶│              │        │
│       ├──────────────┼─────────────▶│        │
│       │              │              │        │
│       │   2. ACK 응답 (과반수)       │        │
│       │◀─────────────┤              │        │
│       │              │     (2/3 OK) │        │
│       │                                      │
│       │   3. 커밋 확정 → Producer에 Confirm   │
│       │                                      │
└──────────────────────────────────────────────┘

핵심 동작 원리

  1. **리더/팔로워 구조 **: 각 Quorum Queue는 하나의 리더와 여러 팔로워로 구성됩니다.
  2. ** 과반수 커밋 **: 메시지는 과반수 노드(quorum)에 저장되어야 커밋이 확정됩니다. 3노드 클러스터라면 최소 2노드에 저장되어야 합니다.
  3. ** 리더 재선출 **: 리더 노드가 죽으면 남은 팔로워 중 가장 최신 로그를 가진 노드가 자동으로 새 리더로 선출됩니다.

Quorum Queue 선언

JAVA
// Spring AMQP에서 Quorum Queue 선언
@Bean
public Queue orderQueue() {
    return QueueBuilder.durable("order.queue")
        .quorum()            // Quorum Queue로 선언
        .deliveryLimit(5)    // 최대 재전송 횟수
        .build();
}
BASH
# CLI로 Quorum Queue 선언
rabbitmqadmin declare queue name=order.queue \
  durable=true arguments='{"x-queue-type":"quorum"}'

Classic Mirrored Queue vs Quorum Queue

비교 항목Classic Mirrored QueueQuorum Queue
** 복제 방식**비동기 (마스터→미러)Raft 합의 (과반수 커밋)
** 데이터 일관성**유실 가능성 있음과반수 노드에 보장
** 리더 선출**수동 / 불명확Raft 자동 선출
** 네트워크 파티션**split-brain 취약과반수 파티션만 동작
** 동기화**전체 메시지 재동기화 필요로그 기반 증분 동기화
Non-durable 지원가능불가 (항상 Durable)
** 우선순위 큐**지원미지원
** 권장 노드 수**제한 없음3 또는 5 (홀수)

Quorum Queue는 항상 Durable입니다. Non-durable Queue나 Exclusive Queue가 필요한 경우에는 Classic Queue를 사용해야 합니다. 다만 고가용성이 필요한 대부분의 상황에서는 Quorum Queue가 정답입니다.

왜 Quorum Queue가 Classic을 대체하는가

핵심은 ** 데이터 일관성 보장 수준 **의 차이입니다.

Classic Mirrored Queue는 마스터에 메시지가 쓰인 시점에 Producer에게 Confirm을 보냅니다. 미러에 복제되기 전에 마스터가 죽으면 메시지가 유실되어도 Producer는 이미 "전송 성공"이라고 알고 있습니다.

Quorum Queue는 ** 과반수 노드에 메시지가 저장된 후에야** Confirm을 보냅니다. 리더가 죽더라도 최소 하나의 팔로워에 메시지가 남아 있으므로 데이터 유실이 발생하지 않습니다.


네트워크 파티션 전략

분산 시스템에서 네트워크 파티션은 피할 수 없는 현실입니다. RabbitMQ는 파티션 발생 시 세 가지 전략을 제공합니다.

전략 비교

전략동작 방식장점단점
ignore파티션을 무시하고 계속 동작가용성 최대split-brain 위험
pause-minority소수 파티션의 노드를 일시 정지split-brain 방지소수 파티션 서비스 중단
autoheal파티션 복구 시 자동으로 소수 파티션 재시작자동 복구소수 파티션의 데이터 유실 가능

각 전략의 동작

PLAINTEXT
네트워크 파티션 발생!
Node1, Node2  ←──── X ────→  Node3

[ignore]
  Node1, Node2: 계속 동작 (자신이 정상이라고 판단)
  Node3: 계속 동작 (자신이 정상이라고 판단)
  → split-brain 발생! 양쪽에서 같은 큐를 독립적으로 운영

[pause-minority]
  Node1, Node2: 계속 동작 (과반수 = 2/3)
  Node3: 자동 일시 정지 (소수 = 1/3)
  → split-brain 방지, 파티션 복구 시 Node3 자동 재개

[autoheal]
  파티션 중: 양쪽 모두 동작
  파티션 복구 시: 소수 파티션(Node3)을 자동으로 재시작
  → 소수 파티션의 미동기화 데이터는 유실될 수 있음

설정 방법

INI
# rabbitmq.conf
cluster_partition_handling = pause_minority

실무에서는 pause-minority 가 가장 많이 사용됩니다. split-brain을 확실히 방지하면서도 과반수 파티션은 정상 동작을 유지하기 때문입니다. 다만 2노드 클러스터에서는 양쪽 모두 소수가 되어 전체 중단이 발생하므로, 최소 3노드 이상의 홀수 구성이 권장됩니다.

Quorum Queue를 사용하면 네트워크 파티션 전략의 중요도가 낮아집니다. Raft 합의 자체가 과반수 기반이므로 소수 파티션의 리더는 자연스럽게 동작을 멈추기 때문입니다.


노드 장애 시 메시지 처리

노드 하나가 장애났을 때의 동작은 큐 타입에 따라 완전히 다릅니다.

Classic Queue

PLAINTEXT
[정상 상태]
  Node1: Queue-A (메시지 100건)
  Node2: Queue-B (메시지 50건)
  Node3: (큐 없음)

[Node1 장애 발생]
  Queue-A: 접근 불가 ❌
  Queue-B: 정상 동작 ✅

  → Node1이 복구될 때까지 Queue-A의 메시지 100건에 접근할 수 없음
  → Node1이 복구되면 메시지 복원 (Durable + Persistent인 경우)
  → Durable하지 않았다면 메시지 영구 유실

Quorum Queue

PLAINTEXT
[정상 상태]  (3노드, Quorum Queue)
  Node1: Queue-A Leader  + Queue-A 데이터
  Node2: Queue-A Follower + Queue-A 데이터
  Node3: Queue-A Follower + Queue-A 데이터

[Node1 장애 발생]
  Node2 또는 Node3이 새 Leader로 선출
  Queue-A: 정상 동작 ✅ (과반수 2/3 생존)

  → 서비스 중단 없이 계속 메시지 발행/소비 가능
  → Node1 복구 시 자동으로 Follower로 재합류, 누락 로그 동기화

이 차이가 Quorum Queue를 선택하는 가장 실질적인 이유입니다. Classic Queue는 해당 노드가 복구될 때까지 메시지에 접근조차 할 수 없지만, Quorum Queue는 과반수 노드가 살아 있는 한 **서비스가 계속됩니다 **.


모니터링

클러스터를 운영하려면 각 노드의 상태를 지속적으로 모니터링해야 합니다.

핵심 명령어

BASH
# 클러스터 전체 상태 확인
rabbitmqctl cluster_status

# 특정 Quorum Queue의 상태 확인
rabbitmqctl list_queues name type leader members \
  --formatter=pretty_table

# 노드 간 연결 상태 확인
rabbitmq-diagnostics check_port_connectivity

핵심 모니터링 메트릭

Management Plugin이나 Prometheus + Grafana 조합으로 다음 메트릭을 추적해야 합니다.

메트릭설명주의 기준
rabbitmq_cluster_size클러스터 내 노드 수예상 노드 수와 불일치 시
rabbitmq_queue_messages큐에 쌓인 메시지 수지속적 증가 = 소비 지연
rabbitmq_channel_consumers소비자 수0이면 메시지 적체
raft_term_totalRaft 선출 횟수빈번한 증가 = 리더 불안정
raft_log_commit_indexRaft 커밋 인덱스노드 간 차이가 크면 동기화 지연

raft_term_total이 자주 올라간다면 리더가 계속 바뀌고 있다는 뜻입니다. 네트워크 불안정이나 노드 리소스 부족을 의심해야 합니다.

Management Plugin 활용

BASH
# Management Plugin 활성화
rabbitmq-plugins enable rabbitmq_management

# 기본 접속 URL: http://localhost:15672
# 기본 계정: guest / guest (로컬만 접근 가능)

Management UI에서는 각 노드의 상태, 큐의 리더/팔로워 분포, 메시지 rate, 커넥션 수 등을 시각적으로 확인할 수 있습니다.


클러스터 구성 시 체크리스트

실무에서 클러스터를 구성할 때 놓치기 쉬운 포인트를 정리합니다.

  1. ** 노드 수는 홀수로 **: 3노드 또는 5노드. 짝수면 파티션 시 과반수를 결정할 수 없는 상황이 발생합니다.
  2. **Erlang Cookie 통일 **: 모든 노드에 동일한 Cookie 파일을 배포합니다.
  3. **Erlang/OTP 버전 통일 **: 노드 간 버전이 다르면 클러스터 구성이 실패할 수 있습니다.
  4. ** 호스트명 해석 **: 모든 노드가 서로의 호스트명을 해석할 수 있어야 합니다 (/etc/hosts 또는 DNS).
  5. ** 방화벽 포트 **: 4369 (epmd), 25672 (노드 간 통신), 5672 (AMQP), 15672 (Management)를 열어야 합니다.
  6. **Quorum Queue를 기본으로 **: 새 프로젝트에서는 Classic Mirrored Queue 대신 Quorum Queue를 사용합니다.

정리

개념핵심 포인트
클러스터 기본메타데이터만 자동 공유, 메시지는 별도 설정 필요
Disc vs RAM** 메타데이터** 저장 위치 차이 (메시지가 아님!)
Classic Mirrored Queue비동기 복제, split-brain 취약, 레거시
Quorum QueueRaft 합의, 과반수 커밋, 데이터 일관성 보장
네트워크 파티션pause-minority가 실무에서 가장 보편적
노드 장애Classic은 접근 불가, Quorum은 리더 재선출로 계속 서비스

클러스터링은 단일 노드의 한계를 넘어서는 첫 번째 단계입니다. 하지만 같은 데이터센터 내의 클러스터만으로는 IDC 자체의 장애나 지리적으로 분산된 서비스를 커버할 수 없습니다. 다음 편에서는 Federation과 Shovel 을 활용한 멀티 IDC 구성을 다룹니다.

댓글 로딩 중...