概述
APISIX是动态、实时、高性能的API网关,可以提供丰富的流量管理能力,可以作为K8s Ingress控制器。本文介绍安装Ingress-APISIX后,通过灵活的路由能力,在无需修改业务代码的情况下,实现全链路灰度能力。
背景信息
在微服务架构下,一次需求可能会同时修改多个微服务应用。在发布应用时,通常将这些应用划分为同一个分组,使灰度流量始终在灰度应用中流转。当上游有灰度流量时,会通过引流的方式将灰度流量引导至灰度分组,在此次链路调用过程中,如果存在一些微服务没有灰度环境,那这些请求在下游时依然能回到灰度环境中,以此实现全链路灰度。
本文将APISIX提供的灵活的路由能力和微服务治理中心的全链路灰度能力相结合,在无需修改业务代码的情况下,轻松实现全链路灰度能力。
本文的演示应用由Ingress-APISIX和Spring Cloud应用组成,Spring Cloud应用有3个,分别是app-a、app-b和app-c,服务之间通过Nacos注册中心实现服务注册与发现,部署完成后,通过Ingress-APISIX配置路由规则,来访问后端服务。
前提条件
- 用户已开通微服务治理中心企业版。
- 用户已开通云容器引擎。
- 用户已开通注册配置中心Nacos。
准备工作
部署Spring Cloud应用
- 登录云容器引擎,选择左侧菜单“集群”,再点进目标集群。
- 在集群管理页面,点击工作负载->无状态。
- 选择命名空间,点击“新增YAML资源”。
- 本文部署app-a、app-b和app-c三个应用,每个应用分别部署一个基线版本和一个灰度版本,同时开通一个注册配置中心Nacos,用于实现服务注册与发现。
部署app-a应用基线版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
image: "镜像仓库域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-a应用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "镜像仓库域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-b应用基线版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
image: "镜像仓库域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-b应用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "镜像仓库域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-c应用基线版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
image: "镜像仓库域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
部署app-c应用灰度版本YAML:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "镜像仓库域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
在云容器引擎中安装Ingress APISIX
自主安装apisix、apisix-ingress-controller和Dashboard组件。具体安装步骤可参考:Apache APISIX Helm Chart 参考安装。
对app-a进行网络配置
针对入口应用app-a,分别为基线版本和灰度版本配置两个K8s Service,用于Ingress APISIX转发流量。
- 登录云容器引擎,选择左侧菜单“集群”,再点进目标集群。
- 在集群管理页面,点击网络->服务。
- 选择命名空间,点击“新增YAML”,依次创建下面两个YAML资源。
app-a-base-service对应app-a应用的基线版本:
apiVersion: "v1"
kind: "Service"
metadata:
name: "app-a-base-service"
namespace: "default"
spec:
ports:
- name: "http"
port: 26160
protocol: "TCP"
targetPort: 26160
selector:
name: "app-a"
app-a-base-service对应app-a应用的基线版本:
apiVersion: "v1"
kind: "Service"
metadata:
name: "app-a-gray-service"
namespace: "default"
spec:
ports:
- name: "http"
port: 26160
protocol: "TCP"
targetPort: 26160
selector:
name: "app-a-gray"
- 登录APISIX控制台,点击“上游”,在上游管理页面中点击创建,分别为app-a和app-a-gray配置上游服务,配置详情如下:
app-a:
app-a-gray:
创建全链路灰度泳道和泳道组
完成准备工作后,即可在微服务治理中心控制台创建全链路灰度泳道和泳道组。
- 登录微服务治理中心控制台,点击全链路灰度。
- 在全链路灰度页面点击“创建泳道组及泳道”。
- 设置泳道组名称,选择入口类型为“Ingress/自建网关”,选择“泳道组涉及所有应用”:app-a、app-b和app-c。
- 点击创建第一个分流泳道,设置“泳道名称”,选择标签“gray”,点击创建泳道。
通过 APISIX配置路由规则, 按照指定请求参数进行路由,实现全链路灰度
通过在Ingress-APISIX设置路由规则,将请求携带参数tag=gray的流量路由到app-a-gray。根据全链路灰度规则,携带参数tag=gray的请求路径是Ingress-APISIX > app-a-gray > app-b-gray > app-c-gray。
配置APISIX路由规则
- 登录APISIX控制台,点击左侧菜单“路由”,然后点击“创建”。
- 在创建路由页面,首先设置路由信息,分别设置app-a-route和app-a-gray-route的路由名称、路由版本和高级匹配条件。
app-a-route:设置路由名称为app-a-route,路由版本1.0.0,设置优先级为0,点击下一步,选择上游服务app-a-base,点击下一步,创建app-a-route路由。
{
"uri": "/*",
"name": "app-a-base",
"methods": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
"CONNECT",
"TRACE",
"PURGE"
],
"upstream_id": "49xxxxxxxxxx937",
"labels": {
"API_VERSION": "1.0.0"
},
"status": 1
}
app-a-gray-route:设置路由名称为app-a-gray-route,路由版本1.0.0,设置优先级为1,创建高级匹配条件,设置规则请求参数tag==gray,点击下一步,选择上游服务app-a-base,点击下一步,创建app-a-route路由。
{
"uri": "/*",
"name": "app-a-gray-route",
"priority": 1,
"methods": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
"CONNECT",
"TRACE",
"PURGE"
],
"vars": [
[
"arg_tag",
"==",
"gray"
]
],
"upstream_id": "498xxxxxxxxxx009",
"labels": {
"API_VERSION": "1.0.0"
},
"status": 1
}
结果验证
说明:192.168.xx.xx:9080为Ingress-APISIX的ip和端口。
不携带参数访问接口:curl http://192.168.xx.xx:9080/callA,路由到基线环境。
curl http://192.168.xx.xx:9080/callA
a__192.168.xx.xx -> b__192.168.xx.xx -> c__192.168.xx.xx
携带参数访问接口:curl http://192.168.xx.xx:9080/callA?tag=gray,路由到灰度环境。
curl http://192.168.xx.xx:9080/callA?tag=gray
a_gray_192.168.xx.xx -> b_gray_192.168.xx.xx -> c_gray_192.168.xx.xx