基于openresty的后端应用健康检查-upstaream动态上下线

古冷 2018-05-26 1694人围观 openrestyupstareamnginx

当我们的nginx的upstream多个情况下,并且应用服务发布期间不间断用户的请求维护更新,并且OPS管理台能看到某个应用各个Server结点的健康状态,我们需要后端节点的健康检查功能,用openresty来控制upstream后端的realserver的动态上下线。

openresty本身就是个nginx服务,目录层级跟nginx一样。


安装:网上看很多人用源码安装,这里用yum安装方式。

    http://openresty.org/cn/linux-packages.html
    
    cd /etc/yum.repos.d/
        rm -rf *
        curl -O http://mirrors.aliyun.com/repo/Centos-7.repo
        curl -O http://mirrors.aliyun.com/repo/epel-7.repo
        curl -O https://openresty.org/package/rhel/openresty.repo
        
    yum install yum-utils c++ gcc gcc-c++  
    yum clean all
    yum makecache
    
    yum install openresty openresty-doc openresty-openssl openresty-openssl-devel openresty-opm openresty-pcre openresty-pcre-devel openresty-resty openresty-zlib openresty-zlib-devel 
    
    ##进来查看跟nginx一样的


openresty的nginx的配置文件配置

vim nginx.conf

#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}

##节点上下线功能块儿
##该功能利用了lua-upstream-nginx-module模块来实现,提供REST分割API来获取upstream的信息,上下线指定upstream下的server。
# 配置 upstream管理地址
http {
    include       mime.types;
    default_type  application/octet-stream;
    lua_package_path "/usr/local/openresty/lualib/resty/?.lua;/usr/local/openresty/lualib/resty/upstream/?.lua;;";

##定义upstream块儿
    upstream tomcat {
        server 127.0.0.1:48080;
        server 127.0.0.1:58080;
    }

     upstream tomcat2 {
        server 127.0.0.1:8080;
        server 127.0.0.1:8180;
    }

    upstream tomcat3 {
        server 127.0.0.1:8090;
        server 127.0.0.1:8280;
    }

##健康检查块儿
##实现健康检查,可以通过lua-resty-upstream-healthcheck来实现。
##以下是配置
    
    ## 指定共享内存
    lua_shared_dict healthcheck 1m;
    ## 在worker初始化过程中,启动定时器,进行后端结点的检查
    lua_socket_log_errors off;
    init_worker_by_lua_block {
        local hc = require "resty.upstream.healthcheck"
        local ok, err = hc.spawn_checker {
            -- shm 表示共享内存区的名称
            shm = "healthcheck",
            -- upstream 指定 upstream 配置的名称
            upstream = "tomcat,tomcat2,tomcat3",
            -- type 表示健康检查的类型, HTTP or TCP (目前只支持http)
            type = "http",
            -- 如果是http类型,指定健康检查发送的请求的URL
            http_req = "GET /index.html HTTP/1.0\r\nHost: tomcat\r\n\r\n",
            http_req = "GET /index.html HTTP/1.0\r\nHost: tomcat2\r\n\r\n",
            http_req = "GET /index.html HTTP/1.0\r\nHost: tomcat3\r\n\r\n",
            -- 请求间隔时间,默认是 1 秒。最小值为 2毫秒
            interval = 2000,
            -- 请求的超时时间。 默认值为:1000 毫秒
            timeout = 5000,
            -- 失败多少次后,将节点标记为down。 默认值为 5
            fall = 3,
            -- 成功多少次后,将节点标记为up。默认值为 2
            rise = 2,
            -- 返回的http状态码,表示应用正常
            valid_statuses = {200, 302},
            -- 并发度, 默认值为 1
            concurrency = 1,
        }
        if not ok then
            ngx.log(ngx.ERR, "=======> failed to spawn health checker: ", err)
            return
        end
    }
    sendfile        on;
    keepalive_timeout  65;

    server {
       listen       80;
       server_name  localhost;

       location / {
          proxy_pass http://tomcat;
       }
 
## 配置监控检查访问页面
location /server/status {
  access_log off;
  default_type text/plain;
  content_by_lua_block {
      local hc = require "resty.upstream.healthcheck"
      ngx.say("Nginx Worker PID: ", ngx.worker.pid())
      ngx.print(hc.status_page())
  }
}

location /upstreams {
  default_type text/plain;
  content_by_lua_file /usr/local/openresty/nginx/conf/lua/upstream.lua;


}
}
    server {
       listen       81;
       server_name  localhost;

       location / {
          proxy_pass http://tomcat2;
       }
}
    server {
       listen       82;
       server_name  localhost;

       location / {
          proxy_pass http://tomcat3;
       }
}
include vhost/*.conf;


}


配置lua脚本文件:upstreams http API lua 代码

vim  /usr/local/openresty/nginx/conf/lua/upstream.lua

local cjson = require "cjson"
local hc = require "resty.upstream.healthcheck"

local concat = table.concat
local upstream = require "ngx.upstream"
local get_servers = upstream.get_servers
local get_upstreams = upstream.get_upstreams
local get_primary_peers = upstream.get_primary_peers
local get_backup_peers = upstream.get_backup_peers
local set_peer_down = upstream.set_peer_down


-- 字符串分隔方法
function string:split(sep)  
	local sep, fields = sep or ":", {}  
	local pattern = string.format("([^%s]+)", sep)  
	self:gsub(pattern, function (c) fields[#fields + 1] = c end)  
	return fields  
end 

-- get all upstream config block 
local function get_all_upstream()
	local us = get_upstreams()
	local upstreams = {}
	
	for _, u in ipairs(us) do
		local srvs = get_servers(u)
		upstreams[u] = srvs
	end		
	
	return upstreams	
end

-- 获取某个upstream下的所有Server
local function get_servers_by_upstream_name(upstream_name)
	local up = get_all_upstream()[upstream_name]
	if not up then
		return {}
	end

	return up	
end

-- 获取所有的 peer
local function get_all_peers(upstream_name)
	local primary_peers = get_primary_peers(upstream_name)
--	local backup_peers = get_backup_peers(upstream_name)
--	
--	local primary_cnt = table.getn(primary_peers)
--	local backup_cnt = table.getn(backup_peers)
--		
--	local total = table.getn(primary_peers) + table.getn(backup_peers)
--	local all_peers = {}
--	
--	for i = primary_cnt + 1, total do
--		backup_peers[i - primary_cnt]["backup"] = true
--		table.insert(primary_peers, i, backup_peers[i - primary_cnt])
--	end
--	
	return primary_peers
end

-- 操作Server节点上下线
local function op_server(upstream_name, server_name, op)
	-- ngx.say(cjson.encode(get_all_peers(upstream_name)))
	local all_peers = get_all_peers(upstream_name)
	for i, peer in ipairs(all_peers) do 
		if peer["name"] == server_name then
			target_peer = peer
			break
		end
	end 
	
	if target_peer == nil then
		ngx.say(cjson.encode({code = "E00001", msg = "error peer name"}))	
	else
		if op == "down" then
			down_value = true
		else
			down_value = false
		end
		
		local is_back_up = target_peer["backup"] or false
		set_peer_down(upstream_name, is_back_up, target_peer["id"], down_value)		
		ngx.say(cjson.encode({code = "A00000", msg = "Success"}))	
	end
end

local http_method = ngx.req.get_method()
local sub_uris = ngx.var.uri:split("/")

-- 节点上下线
if table.getn(sub_uris) == 4 then
	op_server(sub_uris[2], sub_uris[3], sub_uris[4])
end

-- 获取master or backup nodes
if table.getn(sub_uris) == 3 then
	if sub_uris[3] == "primary" then
		ngx.say(cjson.encode(get_primary_peers(sub_uris[2])))
	else
		ngx.say(cjson.encode(get_backup_peers(sub_uris[2])))
	end
	ngx.exit(ngx.HTTP_OK)
end

-- 获取所有upstream or 指定名称的upstream
if sub_uris[2] then
	ngx.say(cjson.encode(get_all_peers(sub_uris[2])))	
else
	ngx.say(cjson.encode(get_all_upstream()))
end


到这里差不多配置完了!!!


启动多个realserver方法:
python -m SimpleHTTPServer 8080   #8080是指定端口,更改既可以

获取所有的upstream的信息

http://localhost/upstreams

查看服务的状态信息地址

http://localhost/server/status

获取指定upstream的信息

http://localhost/upstreams/{upstream_name}

获取指定upstream下指定server_name的节点信息

http://localhost/upstreams/{upstream_name}/{server_name}

指定upstream下指定server_name节点上下线

http://localhost/upstreams/{upstream_name}/{server_name}/down
http://localhost/upstreams/{upstream_name}/{server_name}/up

有了这些个API,就有了在OPS控制台远程控制节点上下线的能力。通过和发布系统的配合,在节点发布时,先进行下线操作,指定时间间隔(处理完目前的请求,无流量后进行发布)后再发布。






请发表您的评论
152文章数 1评论数
请关注微信公众号
微信二维码
Powered By Z-BlogPHP