流量管理

在本节中,我们将继续之前的任务 [在 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"

上次修改时间:2023 年 1 月 2 日: 增强 Dubbogo 文档 (#1800) (71c8e722740)