본문으로 바로가기

RABBITMQ HA,클러스터 구성방법

category 나의 주니어 개발 일기/RabbitMQ 2024. 3. 5. 10:00
728x90
반응형
SMALL

RabbitMQ 클러스터 구성시

모든 노드는 hostname 기반으로 통신한다.

rabbitmqctl cluster_status 명령어를 실행하면 rabbit@호스트네임 을 확인할 수 있다.

img

클러스터를 위한 준비사항

  • 서버끼리의 RabbitMQ 버전을 통일화 한다.
  • 클러스터 된 노드들 끼리 hostname 으로 찾을수 있어야 한다.
  • /etc/hosts 파일에서 세팅해주자.
  • 포트 설정
  • 4369, 25672, 5672, 5671, 15672 포트가 열려있는지, 방화벽에 막히지는 않는지 확인하자.
  • .erlang.cookie 값은 모드 노드가 같은 값을 갖고 있어야 한다.

 

클러스터 시작

상황

img

물리적으로 다른 서버 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 인스턴스를 실행하면 다음과 같이 확인할 수 있다

img

 

도커 기반으로 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 파일을 만들어서 컨테이너에 바인딩 해야한다.

 

 

https://stackoverflow.com/questions/71726192/docker-rabbitmq-doesnt-work-with-rabbitmq-default-user-and-rabbitmq-default-pas

참고하면 좋은 정보

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

728x90
반응형
LIST