使用Haproxy+lua代理Mongodb副本集

使用Haproxy+lua代理Mongodb副本集
一般情况下,使用mongo客户端,或者其他语言的mongo客户端驱动程序连接mongodb副本集的时候,只需要指定副本集名称,就可以实现当mongodb副本集主备切换时的高可用目标。

但是总有一些特殊的场合,连接到副本集的客户端与副本集所在的网络是隔离的,只能通过副本集所在网络的代理访问副本集,比如说,mongodb副本集被部署到k8s上,当k8s集群外的客户端想访问副本集时,只能通过代理进行访问,如通过haproxy访问副本集。

那么问题来了,客户端只知道代理的ip地址或者url,当副本集主备切换时,客户端通过副本集名称是不会连接到副本集的主实例的,这里提供一个haproxy+lua代理mongo副本集的方式可以解决这个问题。

关于haproxy和lua的使用方法和说明,请参考如下链接:
https://www.arpalert.org/haproxy-lua.html

首先,创建一个简易的mongodb副本集(同一主机端口不同,没有用户名密码等安全设置):

# 通过以下shell创建一个简易的mongodb副本集
​
# 杀掉已存在的mongo实例
ps -ef | grep mongod | grep -v grep | awk '{print $2}' | xargs kill -9 ;
​
# 清理并创建目录供副本集使用
rm -rf /data/47017/* &&
rm -rf /data/47018/* &&
rm -rf /data/47019/* &&
rm -f rm -rf /data/47017log &&
rm -f rm -rf /data/47018log &&
rm -f rm -rf /data/47019log &&
mkdir -p /data/47017/ &&
mkdir -p /data/47018/ &&
mkdir -p /data/47019/ &&
​
# 获取本机IP地址
localip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6| grep -v 172|awk '{print $2}'|tr -d "addr:"`
echo "ETH0 IP is ${localip}"
​
# 下面几行注释掉的命令是创建mongodb的用户名和密码,暂时不要
#/home/mongodb/bin/mongod --fork --bind_ip 0.0.0.0 --port 47017 -dbpath "/data/47017" -logpath /data/47017log &&
#/home/mongodb/bin/mongo --host ${localip} --port 47017 --eval "db.getSiblingDB('admin').createUser({user: 'aa', pwd: 'aa', roles: [ { role: 'root', db: 'admin' }]})" &&
#ps -ef | grep mongod | grep -v grep | awk '{print $2}' | xargs kill && sleep 5 &&
​
# 如mongodb的主目录是/home/mongodb的话,直接使用如下命令创建副本集即可
/home/mongodb/bin/mongod --replSet myrep --fork --bind_ip 0.0.0.0 --port 47017 -dbpath "/data/47017" -logpath /data/47017log &&
/home/mongodb/bin/mongod --replSet myrep --fork --bind_ip 0.0.0.0 --port 47018 -dbpath "/data/47018" -logpath /data/47018log &&
/home/mongodb/bin/mongod --replSet myrep --fork --bind_ip 0.0.0.0 --port 47019 -dbpath "/data/47019" -logpath /data/47019log &&
/home/mongodb/bin/mongo --host ${localip} --port 47017 --eval "db.getSiblingDB('admin'); rs.initiate({'_id':'myrep','members':[{'_id':0,'host':'${localip}:47017',priority:3},{'_id':1,'host':'${localip}:47018',priority:2},{'_id':2,'host':'${localip}:47019',priority:0,slaveDelay:86400}]})"

然后,就可以着手安装haproxy和lua的相关环境了,由于我们需要探测mongodb副本集主实例位于哪个节点,因此还需要安装mongo-c-driver,这个过程比较费时间,可以直接使用docker容器简化这一过程,参考:
https://hub.docker.com/r/pengtaoman/haproxy2.1.0-lua-mongodriver
https://github.com/pengtaoman/haproxy2.1.0-lua-mongodriver

github上有dockerfile源码,可以查看环境安装的过程,以及配置文件的示例,这里简要说明如下:
haproxy.cfg

global
  log 127.0.0.1 local0 debug
  log 127.0.0.1 local1 notice
  stats timeout 30s
  user haproxy
  group haproxy
  # 这里载入我们做成的lua文件
  lua-load /etc/haproxy/conf/mongo-backend.lua
  daemon
​
defaults
  log global
  mode tcp
  option tcplog
  option dontlognull
  timeout connect 5000
  timeout client 50000
  timeout server 50000
# 通过frontend设置的端口访问副本集
frontend mongo_front
  bind *:37017
  use_backend %[lua.backend_select]
# m1 m2 m3是副本集的三个实例,
# 我们将通过lua程序选择使用哪一个实例,从而实现主备切换时的高可用
# 10.211.55.21即为上一步创建简易副本集的主机的ip
backend m1
  balance roundrobin
  log 127.0.0.1 local0
  server mongorep0 10.211.55.21:47017 check
​
backend m2
  balance roundrobin
  log 127.0.0.1 local0
  server mongorep1 10.211.55.21:47018 check
​
backend m3
  balance roundrobin
  log 127.0.0.1 local0
  server mongorep2 10.211.55.21:47019 check
# 打开haproxy自带监控服务
listen admin_stats
  mode http
  bind 0.0.0.0:8888
  stats uri /stats
  stats realm Global\ statistics

mongo-backend.lua


# 向haproxy注册一个fetch
core.register_fetches("backend_select", function(txn)
    for k,v in pairs(core.backends) do
      local servs = v.servers
      for sk,sv in pairs(servs) do
        core.Debug(sk)
        local svAddr = sv.get_addr(sv)
        local isMaster = checkMongo(svAddr)
        if (isMaster)
        then
      core.Debug("###### Now primary instance is:"..svAddr)
          return k
        end
      end
    end
  end)
​
  checkMongo = function(mongosvr)
    core.Debug("###### checkMongo mongo address ::"..mongosvr)
    local mongo = require 'mongo'
    local client = mongo.Client('mongodb://'..mongosvr)
    local isp = client:command('admin','{ "isMaster": "1" }')
    local bson = mongo.BSON{}
    pcall(function()
        bson:concat(isp)
      end)
    local ispri=bson:find('ismaster')
    if unexpected_condition then error() end
    return ispri

将haproxy.cfg和mongo-backend.lua放到自己指定的目录下如/home/cfg下,就可以启动容器了,如下:

docker run -d -p 37017:37017 -v /home/cfg:/etc/haproxy/conf pengtaoman/haproxy2.1.0-lua-mongodriver:0.0.1

通过mongo客户端去访问代理:

docker run -it --rm mongo:4.0.14 mongo --host 10.211.55.2 --port 37017

使用docker logs可以查看我们在lua文件中打印的日志:

docker logs a1ced56a7444
[NOTICE] 029/131117 (1) : New worker #1 (7) forked
[debug] 029/131228 (7) : mongorep1
[debug] 029/131228 (7) : ###### checkMongo mongo address ::10.211.55.21:47018
[debug] 029/131228 (7) : mongorep0
[debug] 029/131228 (7) : ###### checkMongo mongo address ::10.211.55.21:47017
[debug] 029/131228 (7) : ###### Now primary instance is:10.211.55.21:47017

可以看到当前primary实例的端口是47017,我们把47017的实例停止后看看发生了什么:

mongo --port 47017
​
use admin
db.shutdownServer();

再次使用mongo客户端连接代理后,查看docker logs如下:

docker logs a1ced56a7444
[NOTICE] 029/131117 (1) : New worker #1 (7) forked
[debug] 029/131228 (7) : mongorep1
[debug] 029/131228 (7) : ###### checkMongo mongo address ::10.211.55.21:47018
[debug] 029/131228 (7) : mongorep0
[debug] 029/131228 (7) : ###### checkMongo mongo address ::10.211.55.21:47017
[debug] 029/131228 (7) : ###### Now primary instance is:10.211.55.21:47017
[WARNING] 029/132201 (7) : Server m1/mongorep0 is DOWN, reason: Layer4 connection problem, info: "Connection refused", check duration: 101ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
[ALERT] 029/132201 (7) : backend 'm1' has no server available!
[debug] 029/132224 (7) : mongorep1
[debug] 029/132224 (7) : ###### checkMongo mongo address ::10.211.55.21:47018
[debug] 029/132224 (7) : ###### Now primary instance is:10.211.55.21:47018

副本集的primary节点已经切换到端口47018上了,则配置成功。
使用Haproxy+lua代理Mongodb副本集

使用Haproxy+lua代理Mongodb副本集使用Haproxy+lua代理Mongodb副本集 阿里斯吐 发布了12 篇原创文章 · 获赞 4 · 访问量 1万+ 私信 关注
上一篇:L1-029 是不是太胖了 (5分)


下一篇:[Web 前端] 029 jQuery 元素的“节操”