灰度发布详细架构图&测试方案

灰度发布详细架构图&测试方案

ProcessOn Flowchart

ProcessOn Flowchart

灰度发布

整体架构


 图为整体架构图,分为fe部署块、api部署块、分流策略、升级服务、管理后台

客户端灰度流程


  1.  浏览器流程
  2. 客户端流程                                                                                                                                
                                  
  3. 请求参数
    a) 登陆接口增加上传版本
    b) 连接websocket增加上传版本和sid(用classId填充)

服务端灰度流程


  1.  老师端点击上课接口返回灰度信息,如果对应的字段为空表示不需要灰度
  2.  学生端登陆接口跟增加返回班级列表信息并添加默认选中班级

PC端升级服务


流程如下

分流策略


  1.  策略管理。
    通过后台管理如下功能
    1. 版本管理。包括版本信息、升级地址、升级方案、是否全量发布。其中升级方案包括热更新及官网更新,全量发布以最新的全量版本为准
    2. 灰度用户管理。班级对应的灰度版本关系维护,如果对应的版本过期则不生效(即配置的灰度版本小于等于当前的最大全量版本)
    3. 版本灰度策略。管理对应版本的灰度控制,如在哪个实例灰度,灰度时间等
  2. 服务部署
    1. 灰度服务器组。每个服务器组管理一个版本,正式服务器组和灰度服务器组等价,每个服务器组内部由nginx根据sid路由
    2. 版本灰度。在每个服务器组前加一层代理,用来控制版本路由

客户端灰度流程


  1.  浏览器流程
  2. 客户端流程
                                  
  3. 请求参数
    a) 登陆接口增加上传版本
    b) 连接websocket增加上传版本和sid(用classId填充)

服务端灰度流程


  1.  老师端点击上课接口返回灰度信息,如果对应的字段为空表示不需要灰度
  2.  学生端登陆接口跟增加返回班级列表信息并添加默认选中班级

PC端升级服务


流程如下

分流策略


  1.  策略管理。
    通过后台管理如下功能
    1. 版本管理。包括版本信息、升级地址、升级方案、是否全量发布。其中升级方案包括热更新及官网更新,全量发布以最新的全量版本为准
    2. 灰度用户管理。班级对应的灰度版本关系维护,如果对应的版本过期则不生效(即配置的灰度版本小于等于当前的最大全量版本)
    3. 版本灰度策略。管理对应版本的灰度控制,如在哪个实例灰度,灰度时间等
  2. 服务部署
    1. 灰度服务器组。每个服务器组管理一个版本,正式服务器组和灰度服务器组等价,每个服务器组内部由nginx根据sid路由
    2. 版本灰度。在每个服务器组前加一层代理,用来控制版本路由

管理系统

灰度版本步骤


1.客户端client及服务端server已存在稳定版本A,现准备发布灰度版本B,上线步骤: 
1.QA部署版本Bserver端代码至灰度服务器 
2.OP后台操作:

  • (1)新增版本->填写版本B的信息:包括clientUrl, 班级白名单,分流服务ip+port
  • (2)点击·上线 ·按钮,此时版本B client端状态为上线,且ip+port注册到分流服务

2.假设版本B灰度顺利,准备修改为全量上线 
1.OP后台操作: 点击版本B的.全量.按钮,此时版本B类型变更为全量,删除白名单 
2.QA部署代码至稳定版服务器集群 
3.OP后台操作:点击版本B的.上线.按钮,此时删除分流服务中的ip+port映射,默认走default配置,即指向稳定服务器集群

3.假设版本B灰度不顺利,准备下线 
1.OP后台操作:点击版本B的下线按钮,此时版本B状态变更下线,删除白名单,删除分流服务ip+port

负载均衡

基本概念


  1.  传统多机路由方式
     包括iphash、轮流访问、随机访问等方式,以达到后端服务负载均衡
  2.  实时交互系统AI
     划分最小切割单元,以班级为单位为基础,以用户维度进行路由

AI负载均衡架构


 如下图

客户端流程


  1. 老师端
    登陆-->班级课程列表–>开始上课–>使用登陆token和classId做为routing key进行长链接建立 -->上课
  2. 学生端
    登陆-->班级列表-->选择班级->使用登陆token和classId做为routing key进行长链接建立 --> 初始化场景

服务路由


  1.  api服务在启动的时候将自己的服务地址注册到nginx内存,即上图的cache,nginx会定时检查健康状态,每5秒检查一次(请求status接口),如果检查失败每秒再检查一次直到成功或则重试三次失败,失败则剔除该注册api服务
  2. 客户端用路由key请求api服务,请求到nginx后,检查请求中有没有上传路由key字段,没有则走默认后端路由,如果上传则检查cache中有无对应key的服务映射,有映射直接代理到对应服务,否则采用取模hash选择一个有效服务,代理过去并且存储映射到cache
  3. cache中的映射只保留到晚上凌晨1点

部署步骤

单台服务器部署


  • nginx配置步骤
    1. 服务器组nginx配置,包括正式线上API组,灰度1API组,灰度2API组.....,每个API组对应一套nginx配置以及进程,多个提供API的go服务挂载到某个nginx下为一个API组,组内班级负载均衡策略
      1. 新建灰度nginx配置目录{dir}
      2. 将ailua项目下loadbalance目录中的balancer.ngx.conf拷贝到{dir}多份,分别命名为balancer_stable.ngx.conf、balancer_gray_1.ngx.conf、balancer_gray_2.ngx.conf......,其中stable配置一定要有,分别对应一个API组
      3. 配置ailua路径。将b中的所有配置中的路径“/Users/xx/apps/lua/ailua”替换为ailua的发布路径,
      4. 配置日志路径。并配置各个xxx.ngx.conf里的access_log和error_log为不同的路径
      5. 配置默认API服务。配置中的default_ai_upstream里的server配置为supervisor起的aiapi的其中一台服务地址,做为默认路由服务器
      6. 配置server_name和listen。server_name配置为部署机器的内网ip,listen为端口号,b中的每个配置的listen不同
      7. 到openresty默认配置路径下将nginx.conf拷贝多份,跟2中的xxx.ngx.conf对应,命名为nginx_balancer_stable.conf、nginx_balancer_gray_1.conf、nginx_balancer_gray_2.conf......,
      8. 将g中conf文件中的include改为对应b中xxx.ngx.conf的绝对路径
      9. 用openresty -c  xxx.conf启动g的各个conf服务
    2. 流量入口nginx配置,用来接收客户端请求的入口,也是灰度策略的入口,可以部署多个机器做流量负载均衡
      1. 新建灰度nginx配置目录{dir},可以复用1-a中建立的目录{dir}
      2. 将ailua项目下grayscale目录中的grayscale.ngx.conf拷贝到{dir},命名为grayscale_1.ngx.conf,grayscale_2.ngx.conf......,可以只复制一个,即单服务模式,配置多个即多机服务模式
      3. 配置ailua路径。将b中的所有配置中的路径“/Users/xx/apps/lua/ailua”替换为ailua的发布路径,
      4. 配置日志路径。并配置各个xxx.ngx.conf里的access_log和error_log为不同的路径
      5. 配置默认API服务。配置中的default_gray_upstream里的server配置为1步骤配置stable nginx监听的server_name:port
      6. 配置server_name和listen。server_name为对外提供的域名,端口i为https的端口
      7. 到openresty默认配置路径下将nginx.conf拷贝多份,命名nginx_grayscale_1.conf,nginx_grayscale_2.conf
      8. 将g中conf文件中的include改为对应b中xxx.ngx.conf的绝对路径,并在main里增加env AI_GRAYSCALE_MODE=xxx;指定对应环境dev|pre|prod
      9. 用openresty -c  xxx.conf启动g的各个conf服务
  • GO项目API启动步骤
    1. 启动一个服务前,配置appconf.toml,新增如下配置,host配置为nginx配置中1所述的nginx server_name:port,意思就是划分到哪个API组
      [loadbalance]
      Host = "127.0.0.1:9100"
    2. 新增启动命令参数,如./frontlistener -port=8088 -lbs=127.0.0.1:8081,其中port表示监听的端口,lbs为配置的nginx api组

多台服务器部署


               参照单台服务器配置,只是将nginx分散到不同的服务器,配置对应的server、server_name等参数

配置协议

灰度管理


  • 灰度服务器管理

    1.  添加upstream
      1. 请求:   /grayscale/upstream/update?ups=xxx&&desc=xxxx
      2. 参数:ups表示灰度的服务实例地址host:port,desc为实例描述名称
      3. 返回:{"code":0,"message":"success"},code非零表示失败,message为错误信息
    2.  删除upstream
      1. 请求:   /grayscale/upstream/delete?ups=xxx
      2. 参数:ups表示灰度的服务实例地址host:port
      3. 返回:{"code":0,"message":"success"},code非零表示失败,message为错误信息
    3. 查询upstream
      1. 请求:   /grayscale/upstream/list
      2. 参数:无
      3. 返回:{"code":0,"message":"success","data":{"list":[{"ups":"xxxx","desc":"xxxx"}]}},code非零表示失败,message为错误信息,list为当前的服务器列表
  • 版本映射管理

    1. 添加版本映射
      1. 请求:   /grayscale/version/update?version=xxxx&ups=xxx
      2. 参数:ups表示灰度的服务实例地址host:port,version表示要灰度到这个服务器的版本
      3. 返回:{"code":0,"message":"success"},code非零表示失败,message为错误信息
    2. 删除版本映射
      1. 请求:   /grayscale/version/delete?version=xxxx
      2. 参数:version表示要删除的灰度版本
      3. 返回:{"code":0,"message":"success"},code非零表示失败,message为错误信息
    3. 查询版本映射
      1. 请求:   /grayscale/version/list
      2. 参数:无
      3. 返回:{"code":0,"message":"success","data":{"list":[{"version":"xxxx","ups":"xxxx"}]}},code非零表示失败,message为错误信息,list为所有的版本映射关系

本次针对灰度测试重点如下:

1. 灰度API组的添加、删除、查询(对应提测接口)

2. 灰度版本与API组的映射的添加、删除、查询(对应提测接口)

3. 灰度分流是否正常,在配置了1和2的路由后,对应版本的请求是否正确路由到指定后段API组(可通过API组的日志确认),默认走stable API组流程是否正确(即没有配置任何灰度的时候,是否所有版本都路由到stable API)

4. API组内部(包括stable API)根据班级取模策略路由到不同的API实例机器,比如,如果stable API组有三台API机器服务,classId=100、101和102的班级,上课的时候这三个班级应该会分散路由到三台服务器上

5. 服务器故障摘除稳定性验证,4中的API组入锅摘除非默认实例,正在这个实例上课的班级是否可以重连到另外一台机器正常上课

6. 客户端接入的流量是否均匀分散在不同nginx服务器上

nginx1配置为

grayscaler.ngx.conf     配置内容如下

map $host $lua_dir {
default "/data/www/ailua";
}
lua_package_path "/data/www/ailua/?.lua";

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

map $scheme $upstream_scheme {
default http;
'https' http;
'wss' ws;
}

lua_shared_dict _gray_ups_zone 4m;
lua_shared_dict _gray_ver_zone 4m;

upstream default_gray_upstream {
server nginx1ip:9100;
}

log_format grayscale '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$proxy_host" $upstream_addr $upstream_scheme';

server {
listen 81;
server_name nginx1ip;
rewrite ^(.*) https://xx.xx permanent;
}

server {
listen 80;
server_name nginxip1;
set $site_home_fe /data/www/aife/dist/final;
server_tokens off;

location ~ ^/grayscale/upstream/([-_a-zA-Z-0-9]+) {
access_by_lua_file $lua_dir/grayscale/access_check.lua;
content_by_lua_file $lua_dir/grayscale/upstream/$1.lua;
}

location ~ ^/grayscale/version/([-_a-zA-Z-0-9]+) {
access_by_lua_file $lua_dir/grayscale/access_check.lua;
content_by_lua_file $lua_dir/grayscale/version/$1.lua;
}

location ~ /api/ {
set $identify '';
set_by_lua_file $ups $lua_dir/grayscale/proxy_version.lua;
proxy_next_upstream off; 
proxy_set_header Host $host; 
proxy_http_version 1.1; 
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass $upstream_scheme://$ups;
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
rewrite ^/api/(.*)$ /$1 break;
}

location ~ .*\.(js|css|png|jpg)$ {
root $site_home_fe;
expires 1d;
}

location ~ .*\.(html)$ {
root $site_home_fe;
expires 30;
}

location / {
root $site_home_fe;
index student.html;
}
access_log /data/logs/nginx/aiapi/ai.xx.cn_access.log grayscale;
error_log /data/logs/nginx/aiapi/ai.xx.cn_error.log;
}

balancer_stable.ngx.conf配置内容如下

map $host $lua_dir {
default "/data/www/ailua";
}
lua_package_path "/data/www/ailua/?.lua";
init_worker_by_lua_file /data/www/ailua/loadbalance/nginx_init.lua;

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

lua_shared_dict _balance_ups_zone 4m;
lua_shared_dict _balance_proxy_zone 128m;

upstream default_ai_upstream {
server 10.xx.xx.xx:8100;
}

log_format ai '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$proxy_host" $upstream_addr';
server {
listen 9100;
server_name nginxip1;

location ~ ^/loadbalance/upstream/([-_a-zA-Z0-9]+) {
access_by_lua_file $lua_dir/loadbalance/access_check.lua;
content_by_lua_file $lua_dir/loadbalance/upstream/$1.lua;
}

location / {
set $identify '';
set_by_lua_file $ups $lua_dir/loadbalance/proxy_ups.lua;
proxy_next_upstream off; 
proxy_set_header Host $host; 
proxy_http_version 1.1; 
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; 
proxy_pass $scheme://$ups;
}

access_log /data/logs/nginx/aiapi/aiapi_stable_access.log ai;
error_log /data/logs/nginx/aiapi/aiapi_stable_error.log;
}

nginx2配置

balancer_gray.ngx.conf      配置内容

map $host $lua_dir {
default "/data/www/ailua";
}
lua_package_path "/data/www/ailua/?.lua";

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

map $scheme $upstream_scheme {
default http;
'https' http;
'wss' ws;
}

lua_shared_dict _gray_ups_zone 4m;
lua_shared_dict _gray_ver_zone 4m;

upstream default_gray_upstream {
server ngixnip1:9100;
}

log_format grayscale '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$proxy_host" $upstream_addr $upstream_scheme';

server {
listen 81;
server_name nginxip2;
rewrite ^(.*) https://xx permanent;
}

server {
listen 80;
server_name nginx2;
set $site_home_fe /data/www/aife/dist/final;
server_tokens off;

location ~ ^/grayscale/upstream/([-_a-zA-Z-0-9]+) {
access_by_lua_file $lua_dir/grayscale/access_check.lua;
content_by_lua_file $lua_dir/grayscale/upstream/$1.lua;
}

location ~ ^/grayscale/version/([-_a-zA-Z-0-9]+) {
access_by_lua_file $lua_dir/grayscale/access_check.lua;
content_by_lua_file $lua_dir/grayscale/version/$1.lua;
}

location ~ /api/ {
set $identify '';
set_by_lua_file $ups $lua_dir/grayscale/proxy_version.lua;
proxy_next_upstream off; 
proxy_set_header Host $host; 
proxy_http_version 1.1; 
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass $upstream_scheme://$ups;
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
rewrite ^/api/(.*)$ /$1 break;
}

location ~ .*\.(js|css|png|jpg)$ {
root $site_home_fe;
expires 1d;
}

location ~ .*\.(html)$ {
root $site_home_fe;
expires 30;
}

location / {
root $site_home_fe;
index student.html;
}
access_log /data/logs/nginx/aiapi/ai.xx.cn_access.log grayscale;
error_log /data/logs/nginx/aiapi/aix.xx.cn_error.log;
}

grayscaler.ngx.conf      配置内容

map $host $lua_dir {
default "/data/www/ailua";
}
lua_package_path "/data/www/ailua/?.lua";
init_worker_by_lua_file /data/www/ailua/loadbalance/nginx_init.lua;

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

lua_shared_dict _balance_ups_zone 4m;
lua_shared_dict _balance_proxy_zone 128m;

upstream default_ai_upstream {
server grayip_online:8200;
}

log_format ai '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$proxy_host" $upstream_addr';
server {
listen 9200;
server_name nginx2;

location ~ ^/loadbalance/upstream/([-_a-zA-Z0-9]+) {
access_by_lua_file $lua_dir/loadbalance/access_check.lua;
content_by_lua_file $lua_dir/loadbalance/upstream/$1.lua;
}

location / {
set $identify '';
set_by_lua_file $ups $lua_dir/loadbalance/proxy_ups.lua;
proxy_next_upstream off; 
proxy_set_header Host $host; 
proxy_http_version 1.1; 
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; 
proxy_pass $scheme://$ups;
}

access_log /data/logs/nginx/aiapi/aiapi_gray_access.log aiapi;
error_log /data/logs/nginx/aiapi/aiapi_gray_error.log;
}

grayscaler用于管理

grayscale有2台,当客户端发起请求的时候,通过version,进行不同的机器集群映射。

grayscale就是记录version=>集群的映射关系。

grayscale就是管理一个版本,比如1.5.0,1.5.1是映射到stable,还是gray

  • 1
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东方狱兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值