在本节中,我们将延续上一个任务【在 Istio 环境部署 Dubbo-go 应用】。
在之前的任务中,我们在集群中部署了一组 Dubbo-go Server和 Client 端应用,验证了服务发现和调用成功。在本节中,我们将创建新版本的 Server 端应用。通过配置 VirtualService 和 DestinationRule ,实现路由管理,和流量转移能力
$ dubbogo-cli newApp . 
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 如下字段,指定好需要构建的镜像地址和版本,我们把镜像 tag 改为 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版本服务的时候,已经有了该应用的 service,这次部署时模板将不会创建 service。
apiVersion: v1
name: dubbo-go-server
description: dubbo-go-server
修改 chart/app/values.yaml 如下字段,指定需要部署的镜像为2.0.0,以及当前开发的应用版本 dubbogoAppVersion 为 v2。
部署的镜像需要和上述构建的镜像一致。当前应用版本用于 mesh 流量规则控制。
image:
  repository:  xxx/dubbo-go-server
  pullPolicy: Always
  tag: "2.0.0"
# Dubbo-go-mesh version control labels
version:
  labels:
    dubbogoAppVersion: v2
至此,构建参数和发布参数都已指定好,可以进行构建和部署了。
构建、推送镜像
$ 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 的两个版本。
查看部署好的 deployment ,server 包含了两个版本。
$  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
查看部署好的 service。两个版本的deployment 共用同一个 service。
$ 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"
至此,我们开发并且部署成功了多版本应用。
执行以下命令以创建目标规则,该目标规则将 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 # 对应应用模板中 chart/app/values.yaml 中指定的版本标签
    - name: v2
      labels:
        dubbogoAppVersion: v2
执行以下命令以创建路由,该路由将所有流量都路由至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
所有流量将流向 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"
有了上述多版本应用路由的基础,我们可以通过一些策略,来进行灵活的流量管理。
我们希望拥有 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", // 使用上下文 context 为调用增加标识
		})
		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"
执行以下命令以修改/创建路由,该路由将所有请求头存在 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
所有流量将流向 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"
延续上述任务,我们执行以下命令以修改/创建路由,该路由将流量的 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
少数流量将流向 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"