快速建立開發環境的 RabbitMQ Cluster - 使用 bat 執行檔的方式

一般來說建立 RabbitMQ Cluster  最快的方式就是透過 Docker 建立,看你是要直接下指令或是編寫 Docker Compose 檔案後再執行都可以,但是會遇到一個問題就是叢集環境建立完成後,還需要再進入 RabbitMQ Management 裡去逐一建立 vhost, User 等等的設定,好一點的話就是有事先匯出一份 definitions.json,這麼一來只需要匯入這個定義檔就可以完成了。

但如果是對一個 Docker 不熟悉更不用說去輸入一條條的指令和一連串步驟的新人來說,上面所說的對他們都是充滿著挑戰性,於是就將建立開發環境 RabbitMQ Cluster 的過程都寫成一個 bat  檔案,新人只要在自己電腦裡安裝好 Docker  環境後只需要執行這麼一個 bat  檔案就可以完成 RabbitMQ Cluster 的建立,裡面所有的設定也都準備好,馬上就可以應用在專案開發上。

這邊指的開發環境是 Windows,如果是其他作業系統環境的話,就自行研究吧!

其實開發環境的 RabbitMQ 不需要建立 Cluster,只需要一個 node 也是可以作為本機開發之用,不過我一開始就是直接建立 Cluster 來用,所以也沒有想過要改。

匯出 definitions.json 檔案

先從一個已經建立好並且有完整設定的 RabbitMQ Cluster 裡把定義檔給匯出來

匯出來的檔案先改名為 definitions.json

建立 RabbitMQ Dockerfile 檔案

將剛才所匯出來的 RabbitMQ 定義檔 預先包含在 RabbitMQ Docker Image 裡,這樣當 RabbitMQ Container 就會使用預先準備好的 配置文件或定義檔來啟動 RabbitMQ 容器,我這邊所使用的 RabbitMQ 版本是 RabbitMQ 3.7.28,以下是我的 Dockerfile 內容

FROM rabbitmq:3.7-management

# 將定義檔複製到容器中
COPY definitions.json /etc/rabbitmq/

我們可以執行以下的指令先行建立 Docker Image

docker build -t my-custom-rabbitmq .

然後執行以下的指令來啟動 RabbitMQ  容器,先確認所啟動的容器是否可執行以及是否有使用預先配置的定義檔

docker run -d --name my-rabbitmq -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=mypassword my-custom-rabbitmq

建立完成後,開啟瀏覽器並且進入 RabbitMQ Managament (127.0.0.1:15672)  進行確認。

 

準備好 node 加入 cluster 的 rabbitmq_cluster_setup.sh 檔案

如果你是一個一個 node 單獨啟動後再手動執行指令逐一將 各個 node 加入 cluster 的話,那麼執行的指令就會是以下的內容

/// node2
docker exec -it rabbitmq2 /bin/bash

rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app
exit


/// node3
docker exec -it rabbitmq3 /bin/bash

rabbitmqctl stop_app
rabbitmqctl join_cluster --ram rabbit@node1
rabbitmqctl start_app
exit

因為我們是希望執行一次 bat 檔案就可以將所有的動作全部完成,所以上面的指令也是要處理,我選擇的方式是將 node 加入 cluster 的指令給寫在 rabbitmq_cluster_setup.sh 檔案裡,然後在所有 RabbitMQ Node 容器都建立完成並且服務狀態也完成後,再將 rabbitmq_cluster_setup.sh 檔案複製到容器裡去執行,這麼一來就可以完成 RabbitMQ Cluster 的建立了。

rabbitmq_cluster_setup.sh

#!/bin/bash
rabbitmqctl stop_app
rabbitmqctl join_cluster --ram rabbit@node1
rabbitmqctl start_app

以上的內容就是我會把 node2, node3  以 ram node  形式加入 node1 的 cluster

完整的 init_rabbitmq_cluster.bat  內容

一開始會先檢查是否已經拉取過 rabbitmq:3.7-management 映象檔,如果沒有就拉取、有的話就不拉取,再來檢查是否已經建立過 my-custom-rabbitmq:3.7-management  映象檔,如果沒有就建立。然後分別建立三個 RabbitMQ 容器,建立完成後會等待個 30 秒(這個時間可以自己調整),再來就是建立 RabbitMQ Cluster,叢集建立完成後再檢查三個 RabbitMQ Node 的 cluster_status,最後出現「Press any key to continue . . .」就大功告成。

當然全部完成後,你還是要登入 127.0.0.1:15672 進去 RabbitMQ Management UI  去做確認。

@echo off
chcp 65001 > nul

:: 檢查是否存在 rabbitmq:3.7-management 映像
docker images | findstr "rabbitmq:3.7-management" > nul
if %errorlevel% neq 0 (
    echo "rabbitmq:3.7-management 映像不存在,正在拉取..."
    docker pull rabbitmq:3.7-management
) else (
    echo "rabbitmq:3.7-management 映像已存在,不需要重新拉取。"
)

:: 檢查是否存在 my-custom-rabbitmq:3.7-management 映像
docker images | findstr "my-custom-rabbitmq:3.7-management" > nul
if %errorlevel% equ 0 (
    echo "my-custom-rabbitmq:3.7-management 映像已存在,不需要重新構建。"
) else (
    :: 建立 RabbitMQ Docker 映像
    echo "建立 RabbitMQ Docker 映像..."
    docker build -t my-custom-rabbitmq:3.7-management .
)

:: 啟動 Docker 容器 1
echo "啟動 Docker 容器 1"
docker run -d --restart=always --name rabbitmq1 --hostname node1 --log-opt max-size=10m --log-opt max-file=3 -v "%CD%\data1:/var/lib/rabbitmq:z" -p "4369:4369" -p "5671:5671" -p "5672:5672" -p "15671:15671" -p "15672:15672" -p "25672:25672" -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=mypassword -e RABBITMQ_ERLANG_COOKIE=mysecretcookie -e RABBITMQ_NODENAME=rabbit my-custom-rabbitmq:3.7-management

:: 啟動 Docker 容器 2
echo "啟動 Docker 容器 2"
docker run -d --restart=always --name rabbitmq2 --hostname node2 --log-opt max-size=10m --log-opt max-file=3 -v "%CD%\data2:/var/lib/rabbitmq:z" -p "5673:5672" -p "15673:15672" -p "25673:25672" -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=mypassword -e RABBITMQ_ERLANG_COOKIE=mysecretcookie -e RABBITMQ_NODENAME=rabbit --link rabbitmq1:node1 rabbitmq:3.7-management

:: 啟動 Docker 容器 3
echo "啟動 Docker 容器 3"
docker run -d --restart=always --name rabbitmq3 --hostname node3 --log-opt max-size=10m --log-opt max-file=3 -v "%CD%\data3:/var/lib/rabbitmq:z" -p "5674:5672" -p "15674:15672" -p "25674:25672" -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=mypassword -e RABBITMQ_ERLANG_COOKIE=mysecretcookie -e RABBITMQ_NODENAME=rabbit --link rabbitmq1:node1 --link rabbitmq2:node2 rabbitmq:3.7-management

echo "所有 Docker 容器已建立"

:: 等待 RabbitMQ 服務啟動完成
echo "等待 RabbitMQ 服務啟動..."
timeout /t 30 /nobreak

:: 建立 RabbitMQ Cluster
echo "Starting to build rabbitmq cluster."

:: 在 rabbitmq2 容器内執行脚本
docker cp rabbitmq_cluster_setup.sh rabbitmq2:/rabbitmq_cluster_setup.sh
docker exec rabbitmq2 /bin/bash -c '/rabbitmq_cluster_setup.sh'

:: 在 rabbitmq3 容器内執行脚本
docker cp rabbitmq_cluster_setup.sh rabbitmq3:/rabbitmq_cluster_setup.sh
docker exec rabbitmq3 /bin/bash -c '/rabbitmq_cluster_setup.sh'

echo "完成 RabbitMQ Cluster"

:: check cluster status
echo "Check cluster status:"
docker exec rabbitmq1 /bin/bash -c "rabbitmqctl cluster_status"
docker exec rabbitmq2 /bin/bash -c "rabbitmqctl cluster_status"
docker exec rabbitmq3 /bin/bash -c "rabbitmqctl cluster_status"

:: 等待使用者按下任意鍵以關閉視窗
pause

將以上的內容另存為 init_rabbitmq_cluster.bat,並且和 definitions.json, Dockerfile, rabbitmq_cluster_setup.sh  這幾個檔案放在一起,然後在 Cmd  環境下執行 init_rabbitmq_cluster.bat,第一次執行就會看到以下的執行過程

D:\Docker\rabbitmq-cluster\test>init_rabbitmq_cluster.bat
"rabbitmq:3.7-management 映像不存在,正在拉取..."
3.7-management: Pulling from library/rabbitmq
7595c8c21622: Pull complete
d13af8ca898f: Pull complete
70799171ddba: Pull complete
b6c12202c5ef: Pull complete
7c7d7bd9523c: Pull complete
522a04a12a81: Pull complete
465d05ea44a2: Pull complete
b060ed55c3fd: Pull complete
874cbe0acbba: Pull complete
dfbd1f813a51: Pull complete
f17ea2b6ad54: Pull complete
665a6eadb67a: Pull complete
Digest: sha256:b6dd45cc35b36f9fbdb67abca650213998f52e24173f58ceb25c0c29c92105f6
Status: Downloaded newer image for rabbitmq:3.7-management
docker.io/library/rabbitmq:3.7-management

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview rabbitmq:3.7-management
"建立 RabbitMQ Docker 映像..."
[+] Building 0.7s (7/7) FINISHED                                                                                                                                                                  docker:default
 => [internal] load .dockerignore                                                                                                                                                                           0.0s
 => => transferring context: 2B                                                                                                                                                                             0.0s
 => [internal] load build definition from Dockerfile                                                                                                                                                        0.0s
 => => transferring dockerfile: 105B                                                                                                                                                                        0.0s
 => [internal] load metadata for docker.io/library/rabbitmq:3.7-management                                                                                                                                  0.0s
 => [internal] load build context                                                                                                                                                                           0.0s
 => => transferring context: 27.04kB                                                                                                                                                                        0.0s
 => [1/2] FROM docker.io/library/rabbitmq:3.7-management                                                                                                                                                    0.1s
 => [2/2] COPY definitions.json /etc/rabbitmq/                                                                                                                                                              0.5s
 => exporting to image                                                                                                                                                                                      0.0s
 => => exporting layers                                                                                                                                                                                     0.0s
 => => writing image sha256:9c12d0c1a425d2bbc9a48a090150a9930d5d6843b575f33e83b782b3f1a52cbe                                                                                                                0.0s
 => => naming to docker.io/library/my-custom-rabbitmq:3.7-management                                                                                                                                        0.0s

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview
"啟動 Docker 容器 1"
fc1d10c09d60d9eb1aa0a300e242ff1451cc39395041bbd2b85452b8ac6fbd74
"啟動 Docker 容器 2"
589d4efc385c44b17e4e202c3adbb555f63eb9bc5ae6a09e99b5b925cf336d1b
"啟動 Docker 容器 3"
912f57ee5c7a7328e693475585ee4ae3c518d6ad3aec5d6940d204eb67ae2c90
"所有 Docker 容器已建立"
"等待 RabbitMQ 服務啟動..."

Waiting for  0 seconds, press CTRL+C to quit ...
"Starting to build rabbitmq cluster."
Successfully copied 2.05kB to rabbitmq2:/rabbitmq_cluster_setup.sh
Stopping rabbit application on node rabbit@node2 ...
Clustering node rabbit@node2 with rabbit@node1
Starting node rabbit@node2 ...
 completed with 3 plugins.
Successfully copied 2.05kB to rabbitmq3:/rabbitmq_cluster_setup.sh
Stopping rabbit application on node rabbit@node3 ...
Clustering node rabbit@node3 with rabbit@node1
Starting node rabbit@node3 ...
 completed with 3 plugins.
"完成 RabbitMQ Cluster"
"Check cluster status:"
Cluster status of node rabbit@node1 ...
[{nodes,[{disc,[rabbit@node1]},{ram,[rabbit@node3,rabbit@node2]}]},
 {running_nodes,[rabbit@node3,rabbit@node2,rabbit@node1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@node3,{badrpc,nodedown}},
          {rabbit@node2,{badrpc,nodedown}},
          {rabbit@node1,[]}]}]
Cluster status of node rabbit@node2 ...
[{nodes,[{disc,[rabbit@node1]},{ram,[rabbit@node3,rabbit@node2]}]},
 {running_nodes,[rabbit@node3,rabbit@node1,rabbit@node2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@node3,{badrpc,nodedown}},
          {rabbit@node1,[]},
          {rabbit@node2,[]}]}]
Cluster status of node rabbit@node3 ...
[{nodes,[{disc,[rabbit@node1]},{ram,[rabbit@node3,rabbit@node2]}]},
 {running_nodes,[rabbit@node2,rabbit@node1,rabbit@node3]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@node2,[]},{rabbit@node1,[]},{rabbit@node3,[]}]}]
Press any key to continue . . .

當所有過程都完成後,也會在指令檔執行所在的目錄下建立 data1, data2, data3 資料夾,這三個資料夾都是三個 node 的 volume  本地端位置。

在執行 docker run 指令中有使用 --restart=always  這個 flag,所以當電腦重新啟動或是重新啟動 Docker 後,三個 RabbitMQ 容器都會自動重新建立。因為有使用 volume 儲存 RabbitMQ 容器的資料,所以在 RabbitMQ 容器重新建立後,RabbitMQ Cluster  的設定與資料也都有保留,所以 RabbitMQ Cluster 仍然會存在。


總之就是分享這麼一個快速簡單建立開發端 RabbitMQ Cluster 的方式,我也知道網路上有許多種更好的建立 RabbitMQ Cluster 的方式,但我的出發點在於要能夠盡量減少新人在執行上的問題。

過多的指令或步驟都會讓操作者無所適從,更何況是一旦發生了錯誤時,面對錯誤是一頭霧水、手足無措的狀態,這些經歷雖然也是可以是作為開發者養成過程中的經驗,但至少先讓新人在開發環境的準備可以能夠簡單、快速一點,這也可以讓帶領的前輩省下更多的時間,而後新人有時間也有興趣的話再去自己研究其他的建立方式。

以上

純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力