RabbitMQ 클러스터 구성시
모든 노드는 hostname 기반으로 통신한다.
rabbitmqctl cluster_status
명령어를 실행하면 rabbit@호스트네임
을 확인할 수 있다.
클러스터를 위한 준비사항
- 서버끼리의 RabbitMQ 버전을 통일화 한다.
- 클러스터 된 노드들 끼리 hostname 으로 찾을수 있어야 한다.
/etc/hosts
파일에서 세팅해주자.- 포트 설정
- 4369, 25672, 5672, 5671, 15672 포트가 열려있는지, 방화벽에 막히지는 않는지 확인하자.
- .erlang.cookie 값은 모드 노드가 같은 값을 갖고 있어야 한다.
클러스터 시작
상황
물리적으로 다른 서버 192.168.0.1(master)
, 192.168.0.2(slave)
를 클러스터링 해야한다.
192.168.0.1(master) 의 rabbit 노드는 rabbit@rabbit1
, 192.168.0.2(slave) 의 rabbit 노드는 rabbit@rabbit4
이다.
1.각각의 서버에 /etc/hosts 값을 설정해주자.
통신을 위해 타켓서버의 IP와 HOSTNAME을 매핑해주자
master의 /etc/hosts
$ vi /etc/hosts
192.168.0.2 rabbit4
slave의 /etc/hosts
$ vi /etc/hosts
192.168.0.1 rabbit1
2. .erlang.cookie 값을 서로 똑같이 맞춰주자.
master mq의 cookie값을 적용해주면 된다
$ cat /var/lib/rabbitmq/.erlang.cookie
ESFDFWEFDSDFGESRFWEGERW
$ vi /var/lib/rabbitmq/.erlang.cookie
3. Slave 서버에서 Master 에게 등록작업을 하자
slave서버
## 1.무조건 mq는 종료후에 작업해야한다.
rabbitmqctl stop_app
## 2.기존설정 리셋
rabbitmqctl reset
## 3.master cluster에 조인하자
rabbitmqctl join_cluster rabbit@rabbit1
## 4.재기동
rabbitmqctl start_app
4. Master 서버에서 HAProxy구성을 하자.
HAProxy는 여러 서버에 대해 요청을 확산시키는 TCP 및 HTTP 기반 애플리케이션들을 위해 고가용성 로드밸런서와 리버스 프록시를 제공하는 자유-오픈 소스 소프트웨어이다
모든 큐 미러링 적용
rabbitmqctl set_policy ha-all ".*" '{"ha-mode":"all"}' --priority 1
특정 큐만 미러링 적용
rabbitmqctl set_policy ha-all "^큐이름\." '{"ha-mode":"all"}'
- 정책의 이름 : ha-all
- 매칭할 Queue 이름 : ^ha.은 정규식으로 표현된 미러링할 queue 이름으로 ha.로 시작하는 모든 queue
- 정책 : 모든 노드를 미러링
HA 구성을 하면 다른 MQ서버가 다른MQ서버가 대체해주기 때문에 문제없이 운영할 수 있다.
5. 각 서버에 모니터링 옵션을 켜주자.
rabbitmq-plugins enable rabbitmq_management
6. 결과
사전에 준비된 2개의 mq 인스턴스를 실행하면 다음과 같이 확인할 수 있다
도커 기반으로 RABBITMQ 클러스터 구성
- RabbitMQ 클러스터 노드는 내부 데이터베이스 테이블을 메모리에만 저장할지 디스크에도 저장할 지 결정할 수 있다. RAM_NODE = true일 경우, 해당 정보는 메모리에만 저장된다. 다만 저장 대상은 메타 데이터 뿐으로, 메시지나 인덱스, 큐 데이터나 노드 상태 정보 등은 여기에 포함되지 않는다. RAM_NODE 여부는 클러스터의 노드 별로 지정할 수 있다. 예를 들면 하나의 디스크와 두 개의 RAM 노드가 있는 클러스터를 구성하는 식이다.
- 현재 나는 둘다 디스크에 저장하도록 RAM_NODE = false 로 해두었다.
version: "3"
services:
rabbit1:
image: rabbitmq:management
container_name: rabbit1
hostname: rabbit1
ports:
- "5673:5672"
- "15673:15672"
- "4369:4369"
- "25672:25672"
environment:
- RABBITMQ_DEFAULT_USER=${e_RABBITMQ_DEFAULT_USER:-admin}
- RABBITMQ_DEFAULT_PASS=${e_RABBITMQ_DEFAULT_PASS:-admin}
- RABBITMQ_ERLANG_COOKIE=mycookie
volumes:
- /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
- /apps/rabbitmq:/var/lib/rabbitmq
rabbit2:
image: rabbitmq:management
container_name: rabbit2
hostname: rabbit2
links:
- rabbit1
environment:
- CLUSTERED=true
- CLUSTER_WITH=rabbit1
- RAM_NODE=false
- RABBITMQ_ERLANG_COOKIE=mycookie
volumes:
- /apps/cluster-entrypoint.sh=/usr/bin/cluster-entrypoint.sh
- /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
- /apps/rabbitmq:/var/lib/rabbitmq
ports:
- "5674:5672"
- "15674:15672"
rabbit3:
image: rabbitmq:management
container_name: rabbit3
hostname: rabbit3
links:
- rabbit1
- rabbit2
environment:
- CLUSTERED=true
- CLUSTER_WITH=rabbit1
- RAM_NODE=false
- RABBITMQ_ERLANG_COOKIE=mycookie
ports:
- "5675:5672"
volumes:
- /apps/cluster-entrypoint.sh=/usr/bin/cluster-entrypoint.sh
- /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
- /apps/rabbitmq:/var/lib/rabbitmq
RABBITMQ_ERLANG_COOKIE= mycookie
이유는 3.8 버전 이상 부터는 도커에서 지정하는 환경변수에 대해 지원을 안한다.
그러므로 따로 config 파일을 만들어서 컨테이너에 바인딩 해야한다.
참고하면 좋은 정보
RabbitMQ 클러스터의 한계
RabbitMQ 클러스터는 "Queue를 제외"한 모든 정보를 모든 노드가 공유한다고 했던것을 기억하시나요? 따라서 결국 모든 클러스터에는 하나의 Unique한 Queue만 존재합니다. 따라서 만약 하나의 노드가 Fail 하는 경우, 다른 노드와 connection을 맺어서 클러스터와의 연결을 유지할 수는 있지만 해당 노드에 속해있던 Queue에 저장된 Message는 유실이 됩니다. 따라서 이같은 Message 유실을 최소화하기 위해 나온 기법이 바로 Queue Mirroring 입니다.
Queue 미러링(Mirroring) 이란?
이름 그대로 Queue를 거울에 비춘것처럼 클러스터 내에서 다수의 RabbitMQ 노드에 똑같은 Queue를 생성하고 관리한다 뜻입니다. 위 그림을 살펴보면 기존의 RabbitMQ 클러스터랑은 조금 다른 점을 찾아볼 수 있는데요. 보면 Queue A가 모든 노드에 존재합니다. 따라서 queue 미러링을 하게되면 Exchange 가 하나의 queue에 message를 넣는 것이아니라 미러링된 모든 Queue에 같은 메시지를 쌓습니다. 즉 Queue 미러링을 사용하면 하나의 노드가 죽어도 해당 노드에 존재하는 Queue에 대한 정보가 유실되지 않고 계속해서 Message Consuming이 진행될 수 있는 HA를 구성할 수 있습니다.
클러스터에 같은 Queue가 여러개인건 알겠는데 Master-Slave는 뭔가요??
아무리 같은 Queue가 여러 노드에 존재해도 결국 Consumer와 연결되는 Queue는 Master Queue(원본 Queue) 입니다. 이처럼 실제로 consuming 되는 Queue는 Master 이고, Fail에 대비하여 Master와 같은 메시지 정보를 계속해서 보관하면서 대기하고 있는 Queue가 Slave Queue가 됩니다. 따라서 Master Queue와 Slave Queue의 관계는 1:N이며 각 Master Queue 마다 Slave Queue를 몇개로 할지 설정할 수 있습니다.
미러링(Mirroring) 과정
기본적으로 Master에 모든 동작이 먼저 실행되고, Slave에 정보가 전파됩니다. Producer는 Master에 message를 전송하고 해당 message는 Slave에 전파됩니다. 그리고 Master에서 message가 consume됐다는 ack를 받게되면 Slave도 message를 drop하면서 모든 Master와 Slave가 동기화됩니다. 중요한건 위에서도 말했듯 아무리 Slave가 많아도 결국 consume이 되는건 Master이기 때문에 Mirroring은 메시지 처리량을 향상시켜주지 않고 오직 HA를 위한 가용성만 높여줍니다.
모든 Master-Slave Queue는 항상 동기화가 되나요?
아닙니다! 예를들어 클러스터 구동중에 새로운 노드가 추가되어서 해당 노드에 새로운 Slave가 생기면, Slave는 비어있는 상태에서 Master에서 전파되는 새로운 정보만 들어오게 됩니다.
Master가 이미 100개의 message를 가지고 있었다면 새로운 Slave에 기존에 있던 100개의 message가 옮겨지는 것이 아닙니다. 이렇게 되면 Master와 Slave가 가진 정보는 서로 다르게 되고, 이를 Unsynchronized 상태라고 부릅니다. 하지만 이 상태가 계속 지속되는 것은 아닙니다. Master에서 Queue를 소비하다보면 결국 Unsyncronized 상태였던 Slave와 동일하게되어 Synchronized 상태가 됩니다.
Master Queue가 있는 노드가 죽으면 어떻게 되나요?
이 상황이 바로 Mirroring이 필요한 상황입니다. Master가 죽게되면 기본적으로 가장 오랫동안 생성돼있던 Slave가 Master로 승격이 됩니다. 이때 기본적으로 Unsynchronized 상태인 Queue는 승격에서 제외됩니다. (파라미터 설정을 통해 Unsynchronized 한 Queue도 승격시킬 수 있지만, Message 유실은 피할 수가 없습니다.
승격되는 과정에서 생기는 message 유실은 없습니다. 만약 새로운 Master가 선출되기 전에 message가 들어온다면 Slave queue에 저장되었다가 승격이 완료되면 Master에 모두 추가됩니다.
상황에 따라 같은 message가 re-consuming 될 가능성도 존재합니다. 만약 message가 consuming 되던중 갑자기 연결이 끊어지게 되면 message가 client에서 consume은 됐지만 받았다는 것을 확인해주는 ack를 못받은상태가 될 수도 있습니다. 따라서 ack를 못받은 message들은 requeue(다시 큐에 집어넣기)가 이루어져서 client 입장에서는 이미 받은 message를 다시 받게 될 수도 있습니다.
참고자료
https://backtony.github.io/spring/2021-09-21-spring-rabbitmq-1/#%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0
https://coe.gitbook.io/guide/messaging/rabbitmq
https://backtony.github.io/spring/2021-09-21-spring-rabbitmq-1/
https://jonnung.dev/rabbitmq/2019/08/08/rabbitmq-cluster/#gsc.tab=0
'나의 주니어 개발 일기 > RabbitMQ' 카테고리의 다른 글
RABBITMQ 에서 Queue에서 Consume 하고 있는 대상을 명확히 식별하고 싶을때 (0) | 2024.03.18 |
---|---|
RABBITMQ 심화속성 정보 (0) | 2024.03.06 |
Rabbit MQ Queue 정보를 다른 Rabbit MQ 서버로 이전하는 방법 (0) | 2024.02.20 |
NGINX 로 RabbitMQ 로드밸런싱 하기 (0) | 2023.07.12 |
haproxy 로 RabbitMQ서버 로드밸런싱 하기 (0) | 2023.07.11 |