RabbitMQ Docker集群一鍵部署
目的
- 允許消費者和生產者在RabbitMQ節點崩潰的情況下繼續運行,即保證高可用。
- 通過添加更多的節點來線性擴展消息通信吞吐量,即保證性能線性可擴展。
前提
集群中的隊列
在集群中創建隊列,集群只會在單個節點而不是所有節點上創建完整的隊列信息。所有其他非所有者節點只知道隊列的元數據和指向該隊列所在節點的指針。因此當集群節點崩潰時,該節點的隊列相關的元數據,消息,新消息都將丟失。(注:鏡像隊列可同步所有信息)
為什么RabbitMQ默認不講隊列內容和狀態復制到所有集群節點呢? 原因有二:
- 存儲空間 如果每個集群節點都擁有所有隊列的完整拷貝,那么添加新節點不會給你帶來更多存儲空間。
- 性能 消息的發布需要將消息復制到每一個集群節點。對于持久化消息來說,每一條消息都會觸發磁盤活動。 每次新增節點,網絡和磁盤負載都會增加,最終只能保持集群性能的平穩。
所以通過設置集群中的唯一節點來負責任何特定隊列,只有該負責節點才會因隊列消息而遭受磁盤活動的影響。所有其他節點需要將接受到的該隊列的消息傳遞給該隊列的所有者節點。因此,往RabbitMQ集群添加更多的節點意味著你將擁有更多的節點來傳播隊列,這些新增節點為你帶來了性能的提升。
Erlang cookie
因為RabbitMQ采用Erlang開發,Erlang通過認證Erlang cookie的方式來允許節點間相互通信。所以RabbitMQ需要相互通信的節點必須使用相同的Erlang cookie.
磁盤節點 vs 內存節點
內存節點將所有元數據(隊列、交換器、綁定、用戶、權限、VHost)存儲在內存中. 節點關閉,異常退出后,這些元數據會丟失.
磁盤節點將所有元數據存儲于磁盤中。
使用內存節點可使得元數據的聲明之類的操作更加快速.對元數據的存取內存節點比磁盤節點更快速.
RabbitMQ要求集群中至少有一個磁盤節點.
集群部署
使用Docker官方RabbitMQ鏡像部署不能成功
直接從官方鏡像創建后,加入集群提示的錯誤(Error: unable to connect to nodes [rabbitmq1@rabbitmq1]...), 節點無法直接與另一節點直接通訊 ,增加--link也失敗。假如增加--link可行,那必須將所有集群中的節點都使用--link進行鏈接,這樣就存在一個問題,后來新增的節點怎么辦?
后來想到直接修改hosts文件,不過這種方式也比較麻煩,需要逐個修改。
最后本人在網絡中找到一個使用DNS的方式,這種方式應該更加合理。本人也做了一些簡單修改,從最新鏡像創建容器.
使用方法:git clone項目后,只需運行其中的launch.sh腳本,即可一鍵創建集群,默認3個節點。也可閱讀后面的步驟,逐步手工創建。
步驟1. 創建DNS
docker run --name dns \-v /var/run/docker.sock:/docker.sock \
--restart='always' \
-d \
phensley/docker-dns --domain rabbit.com
步驟2. 創建節點
shelldocker run -d \
--name=rabbitmq1 \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_NODENAME=rabbitmq1 \
-e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' \
-h rabbitmq1.rabbit.com \
--dns $(docker inspect -f '{{.NetworkSettings.IPAddress}}' dns) \
--dns-search rabbit.com \
rabbitmq:3.5-management
步驟3. 加入集群
rabbitmq2 為磁盤節點,rabbitmq3 為內存節點
```shell
docker exec rabbitmq2 bash -c \
"rabbitmqctl stop_app && \
rabbitmqctl reset && \
rabbitmqctl join_cluster rabbitmq1@rabbitmq1 && \
rabbitmqctl start_app"
docker exec rabbitmq3 bash -c \
"rabbitmqctl stop_app && \
rabbitmqctl reset && \
rabbitmqctl join_cluster --ram rabbitmq1@rabbitmq1 && \
rabbitmqctl start_app"
```
步驟4. 設置集群高可用
置除了amq開頭的交換器、隊列為鏡像隊列. 生成環境不建議這種處理, 應該設置部分隊列為鏡像隊列。
shelldocker exec rabbitmq1 rabbitmqctl set_policy HA '^(?!amq\.).*' '{"ha-mode": "all"}'
步驟5. 退出集群
shelldocker exec rabbitmq3 bash -c \
"rabbitmqctl stop_app && \
rabbitmqctl reset && \
rabbitmqctl start_app"
參考資料
> 1. RabbitMQ Doc - Clustering Guide
2. RabbitMQ Doc - Highly Available Queues
3. RabbitMQ 實戰 - 第五章 集群并處理失敗