前端灰度发布与可监控、可回滚策略相结合,形成了一个强大的系统稳定性保障机制。通过这三个策略的协同工作,可以确保前端应用在不断迭代和更新的同时,保持高性能和高稳定性,本文介绍如何通过配置MSE云原生网关实现前端灰度。
端到端全链路灰度实现
在微服务场景中,应用间的调用是随机的。当您部署的Spring Cloud应用或Dubbo应用存在升级版本时,可能会导致无法将具有一定特征的流量路由到应用的目标版本。通过MSE提供的云原生网关实现全链路灰度,配合前端灰度方案,即可实现端到端的全链路灰度。
前端用户每一次的请求都经过云原生网关,经过权限系统验证后,所有请求的Cookie中都带上了用户的唯一标识,比如 userid: 001
。
网关挂载了一个 frontend-gray
插件,通过配置插件规则,将灰度流量进行映射并传递。
前提条件
Ingress类型服务实现前端灰度
安装MSE Ingress Controller,通过MSE Ingress访问容器服务,详情可参见MSE Ingress访问容器服务。
步骤一:使用容器服务部署应用
应用部署的具体操作,请参见创建无状态工作负载Deployment。
步骤二:MSE控制台配置灰度插件
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在左侧导航栏,单击插件市场。
在插件市场页面,搜索
frontend-gray
关键字,然后单击插件frontend-gray
卡片。选择插件配置 > 域名级插件规则,单击新建规则,在新建规则页面配置如下规则,详细配置可参见配置规则。
grayKey: userid rules: - name: beta-user grayKeyValue: - "00000002" - "00000003" baseDeployment: version: base grayDeployments: - name: beta-user version: gray enabled: true
步骤三:结果验证
登录容器服务控制台,在左侧导航栏选择集群列表,在集群列表页面单击所创建的容器服务集群,进入集群详情页,在左侧导航栏选择网络 > 路由,查看公网访问端点。
访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录admin/ice
账号,访问主版本,用户ID为 00000001。访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录普通用户user/ice
,访问灰度版本,用户ID为 00000002。
ACK容器服务实现前端灰度
步骤一:使用容器服务部署应用
应用部署的具体操作,请参见创建无状态工作负载Deployment。
步骤二:MSE控制台配置灰度插件
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在左侧导航栏,单击插件市场。
在插件市场页面,搜索
frontend-gray
关键字,然后单击插件frontend-gray
卡片。选择插件配置 > 域名级插件规则,单击新建规则,在新建规则页面配置如下规则,详细配置可参见配置规则。
grayKey: userid rules: - name: beta-user grayKeyValue: - "00000002" - "00000003" baseDeployment: version: base grayDeployments: - name: beta-user version: gray enabled: true
步骤三:添加网关服务来源
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在左侧导航栏,选择路由管理,然后选择来源页签。
单击创建来源。在创建来源面板,选择来源类型为容器服务,ACK/ASK/ACS 集群选择所创建的容器服务集群, 然后单击确定。
步骤四:创建服务
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,选择路由管理,然后选择服务页签。
单击创建服务。在创建服务面板,配置服务相关参数,然后单击确定。
步骤五:创建base路由
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在左侧导航栏,单击路由管理,然后在路由页签单击创建路由。
在创建路由页面,配置如下相关项,然后单击保存并发布。
步骤六:创建gray路由
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在左侧导航栏,单击路由管理,然后在路由页签单击创建路由。
在创建路由页面,配置相关配置项,然后单击保存并发布。
说明gray
代表灰度版本,和frontend-gray
插件配置中grayDeployments.version
对应。
步骤七:结果验证
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在基本概览页面,单击接入点页签找到网关入口,查看公网访问端点。
访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录admin/ice
账号,访问主版本,用户ID为 00000001。访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录普通用户user/ice
,访问灰度版本,用户ID为 00000002。
ECS类型服务实现前端灰度
步骤一:ECS分别部署两个前端应用
基线应用地址为:
120.***.137.243:80
灰度应用地址为:
120.***.137.243:8081
步骤二:创建服务
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,选择路由管理,然后选择服务页签。
单击创建服务。在创建服务面板,服务来源为固定地址,配置服务相关参数,然后单击确定。
步骤三:创建base路由
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,单击路由管理,然后在路由页签单击创建路由。
在创建路由页面,配置相关配置项,然后单击保存并发布。
步骤四:创建gray路由
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,单击路由管理,然后在路由页签单击创建路由。
在创建路由页面,配置相关项,然后单击保存并发布。
说明gray
代表灰度版本,和frontend-gray
插件配置中grayDeployments.version
对应。
步骤五:MSE控制台配置灰度插件
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,单击插件市场。
在插件市场页面,搜索
frontend-gray
关键字,然后单击插件frontend-gray
卡片。选择插件配置 > 域名级插件规则,单击新建规则,在新建规则页面配置如下规则,详细配置可参见配置规则。
grayKey: userid rules: - name: beta-user grayKeyValue: - "00000002" - "00000003" baseDeployment: version: base grayDeployments: - name: beta-user version: gray enabled: true
步骤六:结果验证
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在基本概览页面,单击接入点页签找到网关入口,查看公网访问端点。
访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录admin/ice
账号,访问主版本,用户ID为 00000001。访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录普通用户user/ice
,访问灰度版本,用户ID为 00000002。
CDN/OSS类型服务实现前端灰度
步骤一:OSS文件规划
步骤二:创建服务
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,选择路由管理,然后选择服务页签。
单击创建服务。在创建服务面板,服务来源为DNS域名,域名列表填写OSS地址,然后单击确定。
重要如果OSS的和网关在同一个Region,建议填写OSS的内网地址,如果不在一个Region,请填写公网地址。
步骤三:创建路由
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,单击路由管理,然后在路由页签单击创建路由。
在创建路由页面,配置相关项,然后单击保存并发布。
步骤四:MSE控制台配置灰度插件
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏选择云原生网关 > 网关列表。
在网关列表页面,单击目标网关名称。
在左侧导航栏,单击插件市场。
在插件市场页面,搜索
frontend-gray
关键字,然后单击插件frontend-gray
卡片。选择插件配置 > 域名级插件规则,单击新建规则,在新建规则页面配置如下规则,详细配置可参见配置规则。
grayKey: userid rules: - name: beta-user grayKeyValue: - "00000002" - "00000003" rewrite: host: xx.oss-cn-shanghai.aliyuncs.com ##OSS 地址 indexRouting: "/app1": "/project-a/app1/{version}/index.html" #首页(html)路径重写 fileRouting: "/app1": "/project-a/app1/{version}" #资源(css/js/images)路径重写 baseDeployment: version: dev grayDeployments: - name: beta-user version: 0.0.1 enabled: true
步骤五:结果验证
登录MSE网关管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在基本概览页面,单击接入点页签找到网关入口,查看公网访问端点。
访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录admin/ice
账号,访问主版本,用户ID为 00000001。访问公网端点
nlb-qv04p*******cn-hangzhou.nlb.aliyuncsslb.com
,登录普通用户user/ice
,访问灰度版本,用户ID为 00000002。
FAQ
前端灰度是否还可以配置重写策略?
如果服务来源是非CDN/OSS ,可以搭配重写策略生效。
如果服务来源是CDN/OSS的前端灰度是不可以搭配重写策略,因为相关的重写策略在frontend-gray灰度插件中实现了,如果配置重写策略,会发生冲突,返回403等一些异常情况。
是否可以往HTML首页注入一些全局变量?
可以在html的<head> 标签中(一般是CSS样式等属性),或者<body>标签的头部和尾部注入一些全局JavaScript脚本。
通过injection往HTML首页注入代码,可以在head标签注入代码,也可以在body标签的first和last位置注入代码。
grayKey: userid
rules:
- name: inner-user
grayKeyValue:
- '00000001'
- '00000005'
baseDeployment:
version: base
grayDeployments:
- name: beta-user
version: gray
enabled: true
weight: 80
injection:
head:
- <script>console.log('Header')</script>
body:
first:
- <script>console.log('hello world before')</script>
- <script>console.log('hello world before1')</script>
last:
- <script>console.log('hello world after')</script>
- <script>console.log('hello world after2')</script>
灰度版本生效时机?
假设A用户现在的前端版本是0.0.1
, 这时候发布前端版本 0.0.2
, 并且A客户命中了灰度规则,是否能立即生效?
是不会立即生效,出于下面几点原因考虑:
如果发布版本需要实时生效,这时候就需要由后端来动态控制版本。首先无法实现前后端发布解耦,其次页面的稳定性强依赖这个接口,无法做CDN加速。
假设版本能够实时生效,客户在使用某个功能的时候,可能出现上一秒还在使用某个按钮,下一秒这个按钮就不见了的情况。体验非常糟糕。
什么时机刷新页面?
一般网站是有设置Session超时重新登录,一段时间没有访问后,需重新登录页面。
用户通过登录页面登录到应用中。
所以,在登录页面的时候,需要刷新页面,以获取最新的灰度信息。前端登录示例代码如下:
async function handleLogin(values: LoginParams) {
try {
const result = await login(values);
if (result.success) {
message.success('登录成功!');
await updateUserInfo();
const urlParams = new URL(window.location.href).searchParams;
window.location.href = `${urlParams.get('redirect') || '/'}`;
return;
}
console.log(result);
// 如果失败去设置用户错误信息,显示提示信息
setLoginResult(result);
} catch (error) {
message.error('登录失败,请重试!');
console.log(error);
}
}