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

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

https://www.processon.com/diagraming/5b532fb8e4b053a09c0f7e16

https://www.processon.com/diagraming/5b532471e4b0f8477d8d901c

灰度发布

整体架构


 图为整体架构图,分为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.  实时交互系统AIClass
     划分最小切割单元,以班级为单位为基础,以用户维度进行路由

AIClass负载均衡架构


 如下图

客户端流程


  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/chenyunnan/apps/lua/ailua”替换为ailua的发布路径,
      4. 配置日志路径。并配置各个xxx.ngx.conf里的access_log和error_log为不同的路径
      5. 配置默认API服务。配置中的default_aiclass_upstream里的server配置为supervisor起的aiclassapi的其中一台服务地址,做为默认路由服务器
      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/chenyunnan/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 AICLASS_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://aiclass.knowbox.cn$request_uri permanent;
}

server {
listen 80;
server_name nginxip1;
set $site_home_fe /data/www/aiclassfe/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/aiclassapi/aiclass.knowbox.cn_access.log grayscale;
error_log /data/logs/nginx/aiclassapi/aiclass.knowbox.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_aiclass_upstream {
server 10.9.84.249:8100;
}

log_format aiclass '$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/aiclassapi/aiclassapi_stable_access.log aiclass;
error_log /data/logs/nginx/aiclassapi/aiclassapi_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://aiclass.knowbox.cn$request_uri permanent;
}

server {
listen 80;
server_name nginx2;
set $site_home_fe /data/www/aiclassfe/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/aiclassapi/aiclass.knowbox.cn_access.log grayscale;
error_log /data/logs/nginx/aiclassapi/aiclass.knowbox.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_aiclass_upstream {
server grayip_online:8200;
}

log_format aiclass '$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/aiclassapi/aiclassapi_gray_access.log aiclass;
error_log /data/logs/nginx/aiclassapi/aiclassapi_gray_error.log;
}

grayscaler用于管理

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

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

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

发布了45 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_42498050/article/details/81484939