流量管理
在本节中,我们将继续之前的任务 [在 Istio 环境中部署 Dubbo-go 应用]。
在之前的任务中,我们在集群中部署了一组 Dubbo-go Server 和 Client 应用,并验证了服务发现和调用的成功。在本节中,我们将创建新版本的服务器端应用。通过配置 VirtualService 和 DestinationRule,实现路由管理和流量转移功能
1. 准备工作
- 已安装 dubbo-go cli 工具和依赖工具,grpc_cli(用于本地调试)。
- 已安装 docker、helm 和 kubectl 环境。(arm 机器需要支持 docker buildx)
- 已完成任务 [在 Istio 环境中部署 Dubbo-go 应用]
2. 开发多版本 Dubbo-go 应用。
2.1 使用 dubbogo-cli 创建另一个项目模板
$ dubbogo-cli newApp .
2.2 开发和部署客户端 Dubbo-go 应用 v2
编写业务逻辑
- 修改 package/service/service.go 的实现方法,将版本号返回为 v2.0.0
func (s *GreeterServerImpl) SayHello(ctx context.Context, in *api.HelloRequest) (*api.User, error) {
return &api.User{Name: "Hello " + in.Name, Id: "v2.0.0"}, nil
}
修改以下配置文件,使用 xds 协议作为注册中心,并加载名为 GreeterServerImpl 的服务结构。
conf/dubbogo.yaml
dubbo: registries: xds: protocol: xds address: istiod.istio-system.svc.cluster.local:15010 protocols: triple: name: tri port: 20000 provider: services: GreeterServerImpl: interface: "" # read from stub
至此,应用开发完成。
配置构建和部署参数
指定要构建的镜像
修改 Makefile 中的以下字段,指定要构建的镜像的地址和版本,并将镜像标签更改为 2.0.0。
指定需要通过 helm 安装的名称。
IMAGE = xxx/dubbo-go-server TAG = 2.0.0 HELM_INSTALL_NAME = dubbo-go-server
指定要部署的应用和镜像
修改 chart/app/Chart.yaml 中的以下字段,将当前应用名称指定为
dubbo-go-server
。在我们创建 v1 版本的服务时,我们已经拥有了该应用的服务。在此部署服务期间不会创建模板。apiVersion: v1 name: dubbo-go-server description: dubbo-go-server
修改 chart/app/values.yaml 中的以下字段,将要部署的镜像指定为 2.0.0,并将当前开发的应用版本 dubbogoAppVersion 指定为 v2。
部署的镜像需要与上面构建的镜像一致。当前应用版本用于网格流量规则控制。
image: repository: xxx/dubbo-go-server pullPolicy: Always tag: "2.0.0" # Dubbo-go-mesh version control labels version: labels: dubbogoAppVersion: v2
至此,已指定构建参数和发布参数,准备构建和部署。
使用模板构建和部署 Dubbo-go 应用
构建和推送镜像
$ make build
(本地为 amd64 机器)或
$ make buildx-publish
(本地为 arm64 机器,依赖于 docker buildx 命令)将 Dubbo-go Server v2 发布到集群
$ make deploy NAME: dubbo-go-server-v2 LAST DEPLOYED: Thu Apr 7 12:29:28 2022 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None $ helm list NAME NAMESPACE REVISION UPDATED STATUS CHART dubbo-go-client default 1 2022-04-07 11:49:55.517898 +0800 CST deployed dubbo-go-client-0.0.1 1.16.0 dubbo-go-server-v1 default 1 2022-04-07 11:23:18.397658 +0800 CST deployed dubbo-go-server-0.0.1 1.16.0 dubbo-go-server-v2 default 1 2022-04-07 12:29:28.497476 +0800 CST deployed dubbo-go-client-0.0.1 1.16.0
可以看到,通过 helm 的部署成功,集群中已经有一个 Client 应用和两个版本的 Server。
2.3 验证应用
查看资源部署
查看已部署的部署,服务器包含两个版本。
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dubbo-go-client-v1 1/1 1 1 40m
dubbo-go-server-v2 1/1 1 1 77s
dubbo-go-server-v1 1/1 1 1 67m
查看已部署的服务。两个版本的部署共享同一个服务。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dubbo-go-client ClusterIP 192.168.8.176 <none> 20000/TCP 41m
dubbo-go-server ClusterIP 192.168.216.253 <none> 20000/TCP 67m
检查 Client 应用日志,验证请求是否调用到两个版本的应用。
$ kubectl get pods | grep client | awk '{print $1}' | xargs kubectl logs
...
2022-04-07T05:06:40.384Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:06:41.386Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:06:42.388Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:06:43.389Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:06:44.390Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:06:45.392Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:06:46.393Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:06:47.394Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:06:48.396Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:06:49.397Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
至此,我们已经成功开发和部署了多版本应用。
3. 配置请求路由
3.1 配置目标规则
执行以下命令,创建一个目标规则,将 dubbo-go-server 细分为两个子集。v1 和 v2
$ kubectl apply -f destinationrule.yaml
destinationrule.yaml 内容
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: dubbo-go-server
spec:
host: dubbo-go-server
subsets:
- name: v1
labels:
dubbogoAppVersion: v1 # corresponds to the version label specified in chart/app/values.yaml in the application template
- name: v2
labels:
dubbogoAppVersion: v2
3.2 应用虚拟服务
执行以下命令,创建一个将所有流量路由到 v1 应用的路由。
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml 内容
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: dubbo-go-server
spec:
hosts:
- dubbo-go-server
http:
- route:
-destination:
host: dubbo-go-server
subset: v1
3.3 验证路由是否生效
所有流量都将流向 v1 应用。
$ kubectl get pods | grep client | awk '{print $1}' | xargs kubectl logs
...
2022-04-07T05:40:44.353Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:40:45.354Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:40:46.356Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:40:47.357Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:40:48.359Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:40:49.361Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
4. 基于用户身份的路由
基于上述多版本应用路由的基础,我们可以通过一些策略来实现灵活的流量管理。
4.1 为客户端应用添加用户身份
我们希望具有 user: admin 标识符的流量可以体验新的 v2 版本的应用。
返回之前创建的 dubbo-go-client 项目,修改 cmd/app.go 的 main 函数,添加调用标识符:user: admin
。
func main() {
client := &api. GreeterClientImpl{}
config. SetConsumerService(client)
if err := config.Load(); err != nil {
panic(err)
}
request := &api.HelloRequest{
Name: "Laurence",
}
for {
ctx := context. Background()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]string{
"user":"admin", // Use the context context to add identity to the call
})
if rsp, err := client.SayHello(ctx, request); err != nil{
logger.Errorf("call server error = %s", err)
}else{
logger.Infof("call server response = %+v", rsp)
}
time. Sleep(time. Second)
}
}
构建和推送镜像,覆盖之前的提交。您也可以升级已发布的镜像版本。
$ make build
(本地为 amd64 机器)或
$ make buildx-publish
(本地为 arm64 机器,依赖于 docker buildx 命令)删除 dubbo-go-client 应用
$ make remove helm uninstall dubbo-go-client release "dubbo-go-client" uninstalled
重新发布应用。
$ make deploy
发布后,由于之前的路由配置,验证调用成功。所有流量都流向 v1 版本。
$ kubectl get pods | grep client | awk '{print $1}' | xargs kubectl logs ... 2022-04-07T05:40:44.353Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0" 2022-04-07T05:40:45.354Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0" 2022-04-07T05:40:46.356Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0" 2022-04-07T05:40:47.357Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0" 2022-04-07T05:40:48.359Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0" 2022-04-07T05:40:49.361Z INFO cmd/app.go:29 call server response = name:"Hello laurence" id:"v1.0.0"
4.2 基于用户身份创建路由
执行以下命令修改/创建路由,将请求头中包含 user: admin 标识符的所有流量路由到 v2 版本。
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml 内容
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: dubbo-go-server
spec:
hosts:
- dubbo-go-server
http:
- match:
- headers:
user:
exact: admin
route:
-destination:
host: dubbo-go-server
subset: v2
- route:
-destination:
host: dubbo-go-server
subset: v1
4.3 验证路由是否生效
所有流量都将流向 v2 应用。
$ kubectl get pods | grep client | awk '{print $1}' | xargs kubectl logs
...
2022-04-07T05:52:18.714Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:19.716Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:20.717Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:21.718Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:22.720Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:23.722Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:52:24.723Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
5. 基于权重的路由
5.1 创建基于权重的路由
继续上述任务,我们执行以下命令修改/创建路由,将 10% 的流量导入新版本的应用程序进行灰度测试。
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml 内容
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: dubbo-go-server
spec:
hosts:
- dubbo-go-server
http:
- route:
-destination:
host: dubbo-go-server
subset: v1
weight: 90
-destination:
host: dubbo-go-server
subset: v2
weight: 10
5.2 验证路由是否生效
少量流量将流向 v2 版本。
$ kubectl get pods | grep client | awk '{print $1}' | xargs kubectl logs
...
2022-04-07T05:55:52.035Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:53.036Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:54.037Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:55.039Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:56.041Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:57.043Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:58.045Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:55:59.047Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:56:00.049Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:56:01.050Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v2.0.0"
2022-04-07T05:56:02.053Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"
2022-04-07T05:56:03.055Z INFO cmd/app.go:35 call server response = name:"Hello laurence" id:"v1.0.0"