Docker swarm結合Openresty部署rabbitmq集群


 

Docker swarm結合Openresty部署rabbitmq集群

 

  大家好,年底了,年味兒越來越濃了.2019年的寒冬被定義為未來10年中最好的一年,對於這一說法悲觀的人和樂觀的人的理解是不一樣的.但是不管是寒冬還是盛夏,我們都應該堅持不斷的積累和主動的思考.擁抱變化,堅定信心.

  

        簡單描述一下我們的需求,就是通過docker 來搭建一套rabbitmq的集群,用於接受業務傳來的數據,然后把數據寫到消息隊列中,然后消費者消費消息,生成日志文件,接着大數據采集系統定時來采集數據.這樣做的一個好處就是,我們的服務可以直接部署到業務系統所在的服務器集群中.     

 

  年末的時候,在忙完了各種活動項目之后,接到了一個新的項目,數據打點項目.需要在各指定機房搭建數據采集服務,然后有數據中心定時去拉取數據.為此我們采用的解決方案是 基於docker 搭建rabbitmq,利用Openresty 來進行數據的生產和nginx代理.來,讓我們一起了解一些概念.

 

       以下內容是從官網搬來的:

  OpenResty® 是一個基於 Nginx 與 Lua 的高性能 Web 平台,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建能夠處理超高並發、擴展性極高的動態 Web 應用、Web 服務和動態網關。

  OpenResty® 通過匯聚各種設計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發),從而將 Nginx 有效地變成一個強大的通用 Web 應用平台。這樣,Web 開發人員和系統工程師可以使用 Lua 腳本語言調動 Nginx 支持的各種 C 以及 Lua 模塊,快速構造出足以勝任 10K 乃至 1000K 以上單機並發連接的高性能 Web 應用系統。

  OpenResty® 的目標是讓你的Web服務直接跑在 Nginx 服務內部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對 HTTP 客戶端請求,甚至於對遠程后端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應。

       

  之所以選擇OpenResty,是看中了其中nginx服務和對lua腳本的支持,nginx可以做服務代理,lua腳本我們可以用來編寫消息生產者的腳本.好了,開始我們的部署之旅吧(再羅嗦一句,你需要提前了解docker,同時保證部署環境已經安裝了docker,為了部署成功,請保證有倆台服務器,或者虛擬機也行,同時保持倆台服務器或者虛擬機的ip在統一網段)!

  

  一.搭建swarm集群

  swarm集群的角色包含有leader,node 在swarm集群搭建中,重要的是保證 token的一致

       1.swarm manager(初始化swarm leader): docker swarm init --advertise-addr 192.168.7.201             

         提示:這一步會產生token,token為swarm集群的唯一標識

    2.swarm group(成員加入集群)

    docker swarm join --token SWMTKN-1-35ucts3e9bg5onl1pyqwh03j1z1micdb88ziq78m4pfr1zulhf-70w2bdzpjyr8xqc1p77mue04r  192.168.7.201:2377

         token是在初始化swarm manager時返回的.

    注意:記得為集群成員追加label,便於rabbitmq綁定相應的節點,bi-tool-02是節點名稱

    docker node update --label-add rabbitmq=master bi-tool-02

    docker node update --label-add rabbitmq=slave1 bi-tool-01

        這里的label是我們給swarm集群中各節點起的一個別名,以 docker node update --label-add rabbitmq=master bi-tool-02 為例,是給節點bi-tool-02增加標簽,標簽為: rabbitmq=master  ,我們可以通過 docker node inspect bi-tool-02 來查看節點配置內容.可以看到的內容如下:

   "Labels": {

      "rabbitmq": "master"

   },

  追加標簽的目的是為了我們在搭建rabbitmq集群的時候,方便rabbitmq節點的綁定.

 

  二.啟動network服務

  network 用於提供容器間通許

       命令:docker network create --driver overlay rabbitmq-network

 

  三.搭建rabbitmq集群

        1.創建鏡像

   docker build -t stomp-rabbitmq:latest .  

        創建鏡像所需要的dockerfile文件見附件 (博客園不能上傳附件嗎????等我咨詢完了,我再補充這個附件)

        放在了百度網盤上:

   https://pan.baidu.com/s/1PI1nL6TL9pJxsWUaTBleig       

        提取碼:sg9p            

 

        2.創建rabbitmq master隊列

   命令(master):sudo docker service create --name stomp-rabbitmq-master --hostname="stomp-rabbitmq-master" --network rabbitmq-network -p 5772:5672 -p 15772:15672 -p 12345:12345 --mount type=bind,target=/var/lib/rabbitmq/,source=/home/agent/rabmq/ --constraint node.labels.rabbitmq==master -e RABBITMQ_CLUSTER_NODES='rabbit@stomp-rabbitmq-slave1' -e "AUTOCLUSTER_CLEANUP=true" -e "CLEANUP_WARN_ONLY=false" -e "RABBITMQ_ERLANG_COOKIE=thisissecretkey"  stomp-rabbitmq:latest

  注意:要確保/home/agent/rabmq/ 路徑的存在

  備注:

  --network 為設置網絡環境,rabbitmq-network是創建好的docker network,類型為overlay。

  -p 為將容器的端口暴露到宿主機的端口上,這樣可以通過宿主機也就是服務器的端口訪問到容器對應的端口,前方前為宿主機端口,后方為容器端口。其中5672為amqp通信端口,15672為管理界面端口,12345為stomp通信端口。

  --mount 為將容器內的目錄映射到宿主機上(/var/lib/rabbitmq/ 保存了隊列與交換機等的信息,而且保存了持久化的隊列里的消息),這樣當容器出現問題時,啟動新容器時由於已經掛載到了宿主機上持久化,關鍵信息可以不丟失,新容器相當於和舊容器一樣。前方為容器,后方為宿主機目錄。

  --constraint 為創建service時將service指定在某台機器上創建,本次使用的是通過lable指定,在之前我已經對這三台服務器進行了label指定。

  -e 為指定容器內的環境變量,比如其中的    "RABBITMQ_ERLANG_COOKIE=thisissecretkey"

 

       3.創建rabbitmq slave 隊列

  命令(slave):sudo docker service create --name stomp-rabbitmq-slave1 --hostname="stomp-rabbitmq-slave1" --network rabbitmq-network -p 5773:5672 -p 15773:15672 -p 23456:12345 --mount type=bind,target=/var/lib/rabbitmq/,source=/home/agent/rabmq/ --constraint node.labels.rabbitmq==slave1 -e RABBITMQ_CLUSTER_NODES='rabbit@rabbitmq-master' -e "AUTOCLUSTER_CLEANUP=true" -e "CLEANUP_WARN_ONLY=false" -e "RABBITMQ_ERLANG_COOKIE=thisissecretkey" stomp-rabbitmq:latest

        在執行完 sudo docker service create --name stomp-rabbitmq-slave1 后,其實就相當於啟動了一個容器,可以通過 docker ps 看到,如果看不到,那說明服務啟動失敗,通過進程守護進入容器用到的 id 就是 此處看到的id

   (將分支1加入到集群),通過守護進程進入到容器內部執行

        (進入容器)命令:docker exec -it 36383eefcf87 /bin/sh

                                    docker exec -it  /bin/sh

   rabbitmqctl stop_app
   rabbitmqctl join_cluster rabbit@stomp-rabbitmq-master
   rabbitmqctl start_app


 4.創建Openresty(nginx+lua)

   docker build -t openresty-product:0.0.1 .

        命令:docker service create  --name openresty-product-service --mode global -p 8080:80 --mount type=bind,target=/var/log/nginx/,source=/home/agent/openresty-file/var/log/nginx --mount type=bind,target=/opt/,source=/home/agent/openresty-file/openrestyFile --mount type=bind,target=/etc/nginx/conf.d,source=/home/agent/openresty-file/etc/nginx/conf.d --mount type=bind,target=/usr/local/openresty/nginx/conf,source=/home/agent/openresty-file/usr/local/openresty/nginx/conf openresty-product:0.0.1 

 

        用於外網訪問下載數據文件

        命令:docker service create  --name openresty-file-server --constraint node.labels.rabbitmq==master -p 80:80 --mount type=bind,target=/var/log/nginx/,source=/home/agent/finalDataFile/var/log/nginx --mount type=bind,target=/opt/,source=/home/agent/finalDataFile --mount type=bind,target=/etc/nginx/conf.d,source=/home/agent/finalDataFile/etc/nginx/conf.d openresty/openresty:xenial

 

        nginx 的相關配置,我們就不在此細說了,我可以給大家看個例子

       

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/local/openresty/nginx/html;
        index  index.html index.htm;
    }


    location /hello {
#        root   /usr/local/openresty/nginx/html;
#        index  index.html index.htm;

        default_type 'text/html';
        content_by_lua 'ngx.say("hello world")';
    }

    location /file {
       proxy_pass http://192.168.7.201:8090/;
       access_log  /var/log/nginx/file.access.log  main;
       error_log  /var/log/nginx/file.error.log;
    }

    location /dataagent/v1/test {
#        deny all;
         default_type 'text/html';
#        lua_code_cache off;
#        content_by_lua 'ngx.say("test.test.com access")';
        access_log  /var/log/nginx/chaoshen.access.log  main;
        error_log  /var/log/nginx/chaoshen.error.log;
        content_by_lua_file /opt/openresty-docker/openresty/lua-dataagent/chaoshen_yuenan.lua;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/openresty/nginx/html;
    }

}

       

配置中 /dataagent/v1/test content_by_lua_file 對應的腳本文件就是用來生產消息的,我們也直接來看代碼
package.path = '/opt/openresty-docker/openresty/lua-dataagent/lib/resty/?.lua;'
local rabbitmq = require "rabbitmqstomp"

local opts = {
    username = 'guest',
    password = 'guest',
    vhost = '/'
}
local mq, err = rabbitmq:new(opts)

if not mq then
    ngx.log(4,'cannot new mq')
    ngx.log(4,err)
    return
end

mq:set_timeout(60000)

-- connect to the rabbitmq on local machine
local ok, err = mq:connect('192.168.7.201', 12345)

if not ok then
    -- connect to other rabbitmq in cluster
    ok, err = mq:connect('192.168.7.200', 23456)
    if not ok then
        -- connect to last rabbitmq in cluster
        -- put messages into errorLog
        ngx.log(4,'cannot connect mq')
        ngx.log(4,err)
        return
    end 
end

--  local msg = "d.g.test.com/gp.do?ac=s_publishgame&action=login&appId=133214321432&serverId=bj_server1&channel=test_gamecenter&accountId=q1132143214&playerId=q1132143214&tm=1458874365&first=1"
local msg = string.sub(ngx.var.request_uri,22,-1)
local result = string.gsub(msg,".*/","d.g.test.com/",1)

local send_receipt_id = ngx.now()*1000
local headers = {}
headers["destination"] = "/exchange/statistical/statistical.test"
headers["receipt"] = send_receipt_id
headers["app-id"] = "luaresty"
headers["persistent"] = "true"
headers["content-type"] = "application/plian"

local ok, err = mq:send(result, headers)
if not ok then
    ngx.log(4,'cannot send mq')
    ngx.log(4,err)
    return
end

local ok, err = mq:set_keepalive(10000, 100)
if not ok then
    ngx.log(4,'cannot set_keepalive mq')
    ngx.log(4,err)
    return
end

-- ngx.say('success: ',msg)

 

   

        5.配置RabbitMq

    RabbitMq比較好的一個資料站點:https://www.jianshu.com/p/124cda48e4ea

                     https://www.jianshu.com/p/61a90fba1d2a

               交換機,statistical
               路由規則, statistical.test
               隊列,test
               在rabbitmq中建好,就能往里發消息了

    添加 "交換機","路由規則","消息隊列"

    //聲明交換機

    rabbitmqctl eval 'rabbit_exchange:declare({resource, <<"/">>, exchange, <<"statistical">>}, topic, true, false, false, []).'

    //聲明消息隊列
    rabbitmqctl eval 'rabbit_amqqueue:declare({resource, <<"/">>, queue, <<"test">>}, true, false, [], none).'

    //綁定交換機,路由規則,消息隊列
    rabbitmqctl eval 'rabbit_binding:add({binding, {resource, <<"/">>, exchange, <<"statistical">>}, <<"statistical.test">>, {resource, <<"/">>, queue, <<"test">>}, []}).'

 

  6.測試消息生產

   curl -i "http://192.168.7.200:8080/dataagent/v1/test/topic-sendtime-id/d.g.test.com/gp.do"

 

  重點提示:

  1.首先要理解集群的架構

  2.保證打點日志文件的存儲空間足夠大

  3.了解openresty

   openresty:是一個基於 Nginx 與 Lua 的高性能 Web 平台,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建能夠處理超高並發、擴展性極高的動態 Web 應用、Web 服務和動態網關。

  4.要了解rabbitmq的相關知識,保證交換機,路由,消息隊列的正常創建

  5.注意容器中配置文件和物理路徑的映射關系

  6.lua腳本中,rabbitMq的地址一版是內網(局域網)地址

  7.swarm集群中,注意個集群節點 label 的自定義

     上面都有關於此問題的描述

 

  問題排查思路:

  1.查看nginx 日志來排查nginx異常日志

  2.理解架構途中消息的傳遞路徑,順着路近一步步追查

  

  好了,就寫道這里,我是jerry百

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM