
生产预发灰度流量方案
Prerequisite
首先我们需要把我们的流量网关从nginx换成apisix
然后我们需要从以前的单套生产环境,增加到两套环境,生产+预发
Then
然后,我们就可以有了这么一个结构图
我们此时有了两套环境,一套预发,一套生产,其中预发的流量通过具体的路由规则配置,目前暂且支持手机号区分。
Advantage
权衡过多种方案,这种方案做的话,优点:
- 隔离性强,从网关层直接一分为二。后续环境2套
- 便于实施,配置文件迁移即可,无需修改
- 一鱼二吃,既可以生产+预发的灰度,又可以做php+java的灰度
- 降级优雅,降级时只需要将灰度控制的filter下掉即可
- 语言友好,java开发的组件总比在nginx上用lua脚本或者c++写脚本要好
缺点:
-
直接切网关,第一次上线风险较大
-
apisix作为新引入的中间件,虽然已经经过大规模实践并且有良好的社区,但是我们对这个中间件还不够熟悉。
Apisix
在apissix上,我们要提供什么功能呢?
默认的灰度
首先是默认的灰度,幸运的是apisix的底层是nginx,所以他天然就支持nginx配置,也就是说,我们可以通过配置达到这样一个效果:

看起来比较怪。实际上,apisix支持nginx的原生配置,也就是说,我们可以通过在其中配置2个nginx配置文件来监听2个端口,来达到对原来整体nginx集群修改尽可能少的效果。
然后我们通过apisix的插件,来进行对2个nginx入口的灰度,所以就会看起来比较奇怪:一个apisix暴露了3个端口。
实际上,下面的2个ng可以单独部署,不集成在apisix中。这样有好处也有坏处,好处就是不集成的话可以减少性能开销,并且apisix层可以比较简单的拿掉。坏处就是路由多了一层,要管理的中间件变多了,要管理的配置也变多了。
最终实际的执行顺序如下:
apisix->ext-plugin(我们的自定义插件)->traffic-split(根据规则做2个upstreams的转发)->具体的nginx->nginx上的upstream
Nginx的配置
apisix->config.yaml
nginx_config: user: root http_configuration_snippet: | server { listen 9999; server_name localhost; location /test5 { alias /tmp/folder1/test1; index index.html; } } server { listen 8888; server_name localhost; location /test5 { alias /tmp/folder2/test1; index index.html; } }apisix创建路由
curl http://127.0.0.1:9180/apisix/admin/routes/5 \-H 'X-API-KEY: Wailiyebang' -X PUT -d '{ "uri": "/test5/", "plugins": { "ext-plugin-pre-req":{ "_meta": { "priority": 10000 }, "conf":[ { "name":"GrayFilter", "value":"{\"validate_header\":\"token\",\"validate_url\":\"https://www.sso.foo.com/token/validate\",\"rejected_code\":\"403\"}" } ] }, "file-logger": { "_meta": { "priority": 1000 }, "path": "logs/file.log" }, "traffic-split": { "_meta": { "priority": -2000 }, "rules": [ { "match": [ { "vars": [ ["http_x-api-id","==","1"] ] } ], "weighted_upstreams": [ { "upstream": { "name": "upstream-A", "type": "roundrobin", "nodes": { "127.0.0.1:9999":1 } }, "weight": 3 } ] }, { "match": [ { "vars": [ ["http_x-api-id","==","2"] ] } ], "weighted_upstreams": [ { "upstream": { "name": "upstream-B", "type": "roundrobin", "nodes": { "127.0.0.1:8888":1 } }, "weight": 3 } ] } ] } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:9999":1 } }}'插件demo逻辑
@Component@Slf4jpublic class GrayFilter implements PluginFilter { public static int INDEX=1;
@Override public String name() { return "GrayFilter"; }
@Override public void filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) { //灰度逻辑在此可以全部自定义逻辑,只需要打上这个x-api-id的header标即可,这里只是简单模拟一次生产一次灰度,以查看是否达到效果 request.setHeader("x-api-id", String.valueOf(INDEX % 2+1)); log.info("GrayFilter filter, index: {}", INDEX); INDEX++; chain.filter(request, response); }}实际效果

demo已部署在测试环境lyb-sass-test
插件最终逻辑(插件todo)

tars
tars之间微服务之间调用的问题,我们区分环境通过tars的set功能做区分:
https://doc.tarsyun.com/#/dev/tars-idc-set.md

这样可以保证生产以及预发之间微服务的隔离
演进过程
迭代方式
java应用承接迭代,系统与php1:1,初次承接需要发布新java应用,后续同步更新java应用或许在java应用中修改新增接口即可
测试环境
测试环境按照预发环境配置,作为预发的前置验证,测试环境中在不验证灰度插件功能的情况下,可以不配置灰度插件,默认流量全走php+java混合的环境
发布流程
发布流程需要做一些调整。
原先我们直接发布到生产环境,现在我们迭代需要先发布到预发环境,在预发环境验证后,再发布到生产环境。
后续我们需要对于发布步骤进行记录(开发需要提供发布操作步骤,其中需要动到线上库的需要评估生产不发的情况下对生产的影响),预发由产品+客成+指定的灰度流量验证过后,通过发布操作步骤再次发布到生产。
预发到生产的时间可以视迭代或者具体情况具体再定,比如纯php修改可以只在预发验证下就发布到生产,包含java应用新上或者java修改的,最好在预发进行充分验证
回滚策略
- 生产发现因为发布导致的问题,将应用以及灰度配置回滚到上个版本即可
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 831 天
部分内容可能已过时