灰度发布系统简介
灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。ABtest 就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
或者对系统某模块功能做了改变, 希望能通过用户实际的请求数据比对一下新旧版本的效果, 以便于决策产品两个版本孰优孰略.
工作流程
1. 定义目标
区分用户,辅助数据统计,保证灰度发布过程中用户体验的连贯性, 避免用户在新旧版本中跳变。
匿名Web应用可采用IP、Cookie等,
登录的应用可直接采用应用的帐号体系。
2、目标用户选取策略
即选取哪些用户先行体验新版本, 可考虑的因素很多,包括但不限于地理位置、用户终端特性(如分辨率、性能)、用户自身特 点(性别、年龄、忠诚度等)。
3、部署系统
部署新系统、部署用户行为分析系统、设定分流规则、运营数据分析、分流规则调整。
4、分析, 总结, 完善
分析数据, 差异, 进行总结, 对问题点进行完善.
策略
- 依据 IP 配置文件: 通过配置文件中的 IP 地址或者 IP 段, 将特定的用户引至新版本, 否则进入旧版本.
- 依据 IP 动态分配: 动态分配至不同的版本, 需要数据库做配合, 首次访问的时候写入记录, 后续的访问保持该记录.
- 依据 cookie 识别: 动态分配至不同的版本, 不同的版本可以通过cookie进行区分, 首次访问动态将用户导入不同的版本, 后续通过Cookie 进行判断该用户前往的版本.
- 根据用户信息分配: 需要有账户体系配合, 根据用户的身份特征, 结合具体的业务需求, 将不同的用户导入不同的版本.
实验
思路
- 后端服务器 2 台: serverA, ServerB, ServerA 为主服务器, ServerB 为辅服务器
- 设置比率: 默认走 serverA, ServerB 给予 x% 的概率.
- 存储使用Redis: prifix 为 upstreamByIP_ , key like upstreamByIP_127.0.0.1
- 请求过来之后, 获取请求的IP地址: clientIP, 根据 clientIP 去 redis 中查询看该 IP 是否有对应的 value, 如果有 server 为该 value, 如果没有 getRandomServer, 存储到 redis 中, 为了统计方便, 存储的同时, 加入以 value 为 key 的集合.
- 根据 最终的 server 值, 将该 client 分发到 对应的 @server 去.
注意事项
- 是否能准确获取用户的 clientIP, 因为 可能有 来自上游的 IP 地址, 这一点得测试一下, 需要能获取到用户的真是的 IP
地址才行, 这是该方案能得以实施的前提条件. - 配置后端的时候, 需要注意 proxy header 等 属性的转发设置, 不要丢掉重要的 client 传过来的信息.
- 要全方位的做好测试工作.
代码
nginx
user www-data;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 102400;
pid /run/nginx/nginx.pid;
events {
worker_connections 10240;
accept_mutex off;
multi_accept on;
use epoll;
}
error_log /data/logs/nginx/nginx_error.log;
http {
server_tokens off;
sendfile on;
tcp_nodelay on;
tcp_nopush on;
charset utf-8;
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
keepalive_timeout 75;
keepalive_requests 32768;
proxy_next_upstream error timeout;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_x_forwarded_for;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 180;
proxy_send_timeout 180;
proxy_read_timeout 180;
proxy_buffer_size 8k;
proxy_buffers 8 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
client_max_body_size 100m;
client_body_buffer_size 256k;
#lua_code_cache off;
access_log /data/logs/nginx/nginx_access.log;
server {
listen 80 default;
return 404;
}
include vhosts/* ;
include conf.d/* ;
}
lua
-- for test
-- ngx.say('hello')
local redis = require "resty.redis"
local getClientIP = function()
if ngx.req.get_headers()["X-Real-IP"] then
return ngx.req.get_headers()["X-Real-IP"]
elseif ngx.req.get_headers()["x_forwarded_for"] then
return ngx.req.get_headers()["x_forwarded_for"]
else
return ngx.var.remote_addr
end
end
local clientIP = getClientIP()
-- for test
-- ngx.say(clientIP)
local getRandomServer = function()
local serverA = 'server1'
local serverB = 'server2'
local rate = 30
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
local random = math.random(1,100)
if random <= rate then
return serverB
else
return serverA
end
end
local red = redis:new()
red:set_timeout(1000) -- 1 sec
--local ok, err = red:connect("127.0.0.1", 6379)
local ok, err = red:connect("unix:/tmp/redis.sock")
if not ok then
ngx.say("failed to connect: ", err)
return
end
-- ngx.say(getRandomServer())
local res, err = red:get(clientIP)
if not res then
ngx.say("failed to get clientIP", err)
return
end
-- if the ip not in redis, then get random server
-- and set it to redis, else set res to server
if res == nil or res == ngx.null then
server = getRandomServer()
-- ngx.say(server)
red:set(clientIP, server)
red:sadd(server,clientIP)
else
server = res
end
-- ngx.say(server)
ngx.exec("@" .. server)
-- put it into the connection pool of size 100,
-- with 10 seconds max idle time
local ok, err = red:set_keepalive(10000, 100)
if not ok then
ngx.say("failed to set keepalive: ", err)
return
end
部署过程
1. 部署设备 nginx_proxy :
主要的服务为 openresty , redis, 分别需要下载源码, 编译安装, 设置开机启动脚本, 设置配置文件
编译的需要, 需要安装 libreadline-dev, libncurses5-dev, libpcre3-dev ,libssl-dev perl make build-essential, tcl. 代码管理的需要, 需要安装 git-core
服务管理的需要, 需要安装 sysv-rc-con
防火墙需要配置, ubuntu 防火墙规则持久化的需要, 需要安装 iptables-persistent
shell 操作的需要, 安装了vim, zsh
流量分析的需要, 安装了 nload
2. 部署测试代码
使用已经在本地测试好的 nginx 配置文件和 lua 脚本, 拉取到线上.
在线上配置好后端机器, 首先满足不加载 lua 脚本的时候可以正常的访问.
开启 lua 脚本,进行调试, 调试的过程中, 使用 8080 端口.
测试无问题之后, 将调试成功的代码部署到 /etc/nginx 目录下.
3. 线上试运行
pending...
附: 可选方案
1、商用的通过集成sdk实现
2、开源的需要结合自己公司情况搭建
https://github.com/SinaMSRE/ABTestingGateway
3. 简易配置方案
依据 ip 配置, 结合配置文件
http://jokerzhang.cn/2015/08/13/nginxlua实现灰度发布/
依据 ip 配置, 结合memcache
http://www.cnblogs.com/wenbiao/p/3227998.html
相关文章
- nginx 性能优化配置
http://www.oschina.net/translate/nginx-setup - nginx accept_mutex
http://huoding.com/2013/08/24/281