在本节中,我们将延续上一个任务【在 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"