上一章已经讲解了配置文件灰度发布、应用版本灰度发布、API网关灰度发布实现,但如果用户这时候在代理层如何做灰度发布呢?
代理层灰度发布分析
用户无论访问应用服务还是静态页,都要经过Nginx代理层,我们可以在Nginx这里做灰度发布,这里的灰度发布可以分为静态页灰度发布和应用动态发布,
如上图:动态服务灰度发布IP/ID切流:
- zhangsan、wangwu使用A应用
- zhaoliu使用B应用
- 192.168.211.1/192.168.211.2 IP的用户使用A应用
- 192.168.211.3 IP的用户使用B应用
静态资源灰度发布IP/ID切流:
- zhangsan、wangwu访问静态页,使用/usr/bj目录下的静态页
- zhaoliu访问静态页,使用/usr/tj目录下的静态页
- 192.168.211.1/192.168.211.2 IP的用户使用/usr/sz目录下的静态页
IP分流灰度发布
我们为了测试系统,通常把公司内部IP设置为测试IP,也就是使用灰度系统的IP,此时内部员工测试直接访问服务器即可,但是访问服务器又分为静态资源访问和动态服务访问,都有不同的实现策略。在代理层实现灰度发布,可以采用Nginx+Lua实现。
动态服务灰度发布
IP切流动态服务灰度发布方式的实现要借助Nginx+Lua和Redis了,我们可以先把公司内部的IP全部存储在Redis缓存中,当用户访问系统的时候,先通过Lua脚本根据IP查询Redis缓存,如果Redis缓存数据存在就表明使用灰度系统,如果没有缓存则使用稳定版系统。
IP切流步骤如上图:
- IP校验
在服务器上创建lua脚本文件/usr/local/openresty/nginx/lua/loadip.lua:
bash
--客户端
IPlocal headers=ngx.req.get_headers()
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or
ngx.var.remote_addr or "0.0.0.0"
--local ip = "192.168.211.1"
--引入redis依赖
local redis = require("resty.redis");
local red = redis:new()
red:set_timeout(2000)
red:connect("192.168.211.142", 6379)
--执行查询
local grayresult=red:get(ip);
red:close()
if grayresult==nil or grayresult==nil or grayresult==ngx.null then
--没有数据则查询主服务
ngx.var.upstream = "sys"
else
--如果有数据,则查询灰度服务
ngx.var.upstream = "gray"
end
- Nginx控制配置:
修改nginx.conf配置如下:
上图代码如下:
bash
#灰度系统负载均衡池
upstream gray {
server 192.168.211.1:18082;
}
#稳定系统负载均衡池
upstream sys {
server 192.168.1.5:18081;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#所有动态请求
location / {
#设置负载均衡池
set $upstream '';
#查找负载均衡池
access_by_lua_file /usr/local/openresty/nginx/lua/loadip.lua;
#反向代理
proxy_pass http://$upstream;
}
}
- 效果测试
在Redis中添加IP 192.168.211.1,访问时会走灰度发布系统,把IP删除会走稳定系统。
静态资源灰度发布
我们把稳定静态资源和灰度静态页资源分别放在了 /usr/local/openresty/nginx/static1/ 和 /usr/local/openresty/nginx/static2/目录下,我们要求公司员工IP访问灰度发布的静态页, 非公司员工IP访问稳定版本页面,这里又需要用到指定IP查询了。
创建 home.lua脚本如下:
bash
--客户端IP
local headers=ngx.req.get_headers()
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
--local ip = "192.168.211.1"
--引入redis依赖
local redis = require("resty.redis");
local red = redis:new()
red:set_timeout(2000)
red:connect("192.168.211.142", 6379)
--执行查询
local grayresult=red:get(ip);
red:close()
if grayresult==nil or grayresult==nil or grayresult==ngx.null then --没有数据则查询主服务
ngx.var.homepath = "/usr/local/openresty/nginx/static1/"
else
--如果有数据,则查询灰度服务
ngx.var.homepath = "/usr/local/openresty/nginx/static2/"
end
nginx.conf配置如下:图片、静态页、样式、脚本,我们都进行灰度发布,如下配合
bash
#静态资源
location ~* \.(jpg|png|html|css|js|woff|ttf|woff2) {
#静态资源路径设置
set $homepath '';
#lua脚本校验使用静态资源
access_by_lua_file /usr/local/openresty/nginx/lua/home.lua; #静态资源root配置
root $homepath;
}
ID切流灰度发布
现在的项目大多数已经是微服务项目,而微服务项目几乎都是前天传递JWT令牌到后台,要想实现ID切 流,我们需要在代理层识别用户身份(JWT令牌),我们需要引入 lua-resty-jwt模块,是用于 ngx_lua 和 LuaJIT 的 Lua 实现库,在该模块能实现Jwt令牌生成、Jwt令牌校验,依赖库的地址:http s://github.com/SkyLothar/lua-resty-jwt。
JWT令牌识别库安装
将下载好了该依赖库 lua-resty-jwt-master.zip,我们将该库文件上传到服务器上,并解压,当然,我 们也可以使用opm直接安装 lua-resty-jwt,配置 lua-resty-jwt之前,我们需要先安装resty和 opm。
安装仓库管理工具包:
bash
yum install yum-utils
添加仓库地址:
bash
yum-config-manager --add-repo
https://openresty.org/package/centos/openresty.repo
安装resty:
bash
yum install openresty-resty
安装opm:
bash
yum install openresty-opm
安装Jwt组件:
bash
opm get SkyLothar/lua-resty-jwt
此时 lua-resty-jwt安装好了,可以直接使用了。
ID识别
我们先利用 lua-resty-jwt生成令牌,再解析令牌 创建 make.lua用于生成令牌:
bash
ngx.header.content_type="application/json;charset=utf8" local cjson = require "cjson"
local jwt = require "resty.jwt"
--生成令牌
--lua-resty-jwt为秘钥
local jwt_token = jwt:sign(
"lua-resty-jwt",
{
header={typ="JWT", alg="HS256"},
payload={name="zhangsan"}
}
)
ngx.say(jwt_token)
创建 token.lua用户校验令牌:
bash
local cjson = require "cjson"
local jwt = require "resty.jwt"
--获取请求头中的令牌数据
local auth_header = ngx.var.http_Authorization
local jwt_obj = jwt:verify("lua-resty-jwt", auth_header) ngx.say(cjson.encode(jwt_obj))
nginx.conf配置如下:
bash
#校验令牌
location /check {
content_by_lua_file /usr/local/openresty/nginx/lua/token.lua; }
#生成令牌
location /token {
content_by_lua_file /usr/local/openresty/nginx/lua/make.lua; }
生成令牌:http://192.168.211.142/token
bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.VxhQcGihWyHuJeHhpUiq2FU7 aW2s_3ZJlY6h1kdlmJY
校验令牌:http://192.168.211.142/check
ID切流实现
结合上面JWT令牌识别,可以编写一个ID切流的流程,我们这里假设是zhangsan用户就访问灰度版本, 其他用户访问稳定版本,创建id.lua如下代码:
bash
local cjson = require "cjson"
local jwt = require "resty.jwt"
--获取请求头中的令牌数据
local auth_header = ngx.var.http_Authorization
local jwt_obj = jwt:verify("lua-resty-jwt", auth_header) --解析的用户名
local username = jwt_obj["payload"]["name"]
if username=="zhangsan" then
--没有数据则查询主服务
ngx.var.idpath = "/usr/local/openresty/nginx/static1/"
else
--如果有数据,则查询灰度服务
ngx.var.idpath = "/usr/local/openresty/nginx/static2/"
end
nginx.conf修改配置如下:
bash
#ID切流
location =/1.html {
#静态资源路径设置
set $idpath '';
#lua脚本校验使用静态资源
access_by_lua_file /usr/local/openresty/nginx/lua/id.lua; #静态资源root配置
root $idpath;
}