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百