traffic management
In this section, we will continue the previous task [Deploy Dubbo-go application in Istio environment].
In the previous task, we deployed a set of Dubbo-go Server and Client applications in the cluster, and verified the success of service discovery and invocation. In this section, we will create a new version of the server-side application. By configuring VirtualService and DestinationRule, routing management and traffic transfer capabilities are realized
1. Preparations
- The dubbo-go cli tool and dependent tools have been installed, grpc_cli (for local debugging).
- The docker, helm, and kubectl environments have been installed. (arm machines need to support docker buildx)
- Task [Deploy Dubbo-go application in Istio environment] completed
2. Develop multi-version Dubbo-go applications.
2.1 Use dubbogo-cli to create another project template
$ dubbogo-cli newApp .
2.2 Develop and deploy client Dubbo-go application v2:
Write business logic
- Modify the implementation method of package/service/service.go, and return the version number as 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
}
Modify the following configuration file, use the xds protocol as the registration center, and load the service structure named 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
At this point, the application development is complete.
Configure build and deployment parameters
Specify the image to be built:
Modify the following fields in the Makefile, specify the address and version of the image to be built, and change the image tag to 2.0.0.
Specify the name that needs to be installed through helm.
IMAGE = xxx/dubbo-go-server TAG = 2.0.0 HELM_INSTALL_NAME = dubbo-go-server
Specify the application and image to be deployed:
Modify the following fields in chart/app/Chart.yaml, and specify the current application name as
dubbo-go-server
. When we created the v1 version of the service, we already had the service of the application. The template will not be created during this deployment service.apiVersion: v1 name: dubbo-go-server description: dubbo-go-server
Modify the following fields in chart/app/values.yaml, specify the image to be deployed as 2.0.0, and the currently developed application version dubbogoAppVersion as v2.
The deployed image needs to be consistent with the image built above. The current application version is used for mesh traffic rule control.
image: repository: xxx/dubbo-go-server pullPolicy: Always tag: "2.0.0" # Dubbo-go-mesh version control labels version: labels: dubbogoAppVersion: v2
At this point, the build parameters and release parameters have been specified, ready to build and deploy.
Use templates to build and deploy Dubbo-go applications
Build and push images
$ make build
(locally for amd64 machines)or
$ make buildx-publish
(Local is arm64 machine, depends on docker buildx command)Publish Dubbo-go Server v2 to the cluster
$ 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
It can be seen that the deployment through helm is successful, and there is already a Client application and two versions of Server in the cluster.
2.3 Verify application
View resource deployment
Looking at the deployed deployment, the server contains two versions.
$ 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
View the deployed service. Two versions of the deployment share the same 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
Check the Client application log to verify that the request is called to two versions of the application.
$ 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"
So far, we have successfully developed and deployed multi-version applications.
3. Configure request routing
3.1 Configure target rules
Execute the following command to create a target rule that subdivides dubbo-go-server into two subsets. v1 and v2
$ kubectl apply -f destinationrule.yaml
destinationrule.yaml content:
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 Apply Virtual Service
Execute the following command to create a route that routes all traffic to the v1 application.
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml content
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 Verify that the routing takes effect
All traffic will go to the v1 app.
$ 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. Routing based on user identity
With the above foundation of multi-version application routing, we can implement flexible traffic management through some strategies.
4.1 Add user identity for client application
We hope that traffic with the user: admin identifier can experience the new v2 version of the application.
Go back to the previously created dubbo-go-client project, modify the main function of cmd/app.go, and add the calling identifier: 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)
}
}
Build and push mirrors, overwriting previous commits. You can also upgrade the released image version.
$ make build
(locally for amd64 machines)or
$ make buildx-publish
(Local is arm64 machine, depends on docker buildx command)Remove dubbo-go-client application
$ make remove helm uninstall dubbo-go-client release "dubbo-go-client" uninstalled
Republish the app.
$ make deploy
After publishing, the verification call is successful because of the previous routing configuration. All traffic goes to the v1 version.
$ 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 Create a route based on user identity
Execute the following command to modify/create a route that routes all traffic with the user: admin identifier in the request header to the v2 version.
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml content
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 Verify that the routing takes effect
All traffic will go to the v2 app.
$ 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. Weight-based routing
5.1 Create weight-based routing
Continuing the above tasks, we execute the following commands to modify/create a route that imports 10% of the traffic into the new version of the application for grayscale testing.
$ kubectl apply -f virtualservice.yaml
virtualservice.yaml content
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 Verify that the routing takes effect
A small amount of traffic will go to the v2 release.
$ 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"