服务网格是云原生时代下新一代的应用感知的云原生基础设施,能够为负责的应用提供标准、通用、无侵入的流量管理、可观测、安全等能力。Dubbo是一款易用高性能的微服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。
通过接入服务网格,Dubbo应用将能够无缝获得其带来的多项云原生能力,包括应用mTLS通信加密、统一的云原生可观测能力(Prometheus、日志、网格拓扑)、无侵入流量管理能力等。本文以阿里云服务网格ASM为基础,主要介绍使用Dubbo框架开发的微服务应用可以用怎样的姿势接入服务网格,实现微服务体系针对云原生时代基础设施的无缝接入。
1. 前提条件
1.1 服务网格环境准备
首先在阿里云上准备ACK集群以及ASM实例,作为应用的部署环境
- 已创建ASM实例。具体操作,请参见创建ASM实例或升级ASM实例。
- 已添加集群到ASM实例。具体操作,请参见添加集群到ASM实例。
- 在ASM实例中创建名为dubbo-demo的命名空间,并为该命名空间开启Sidecar自动注入。具体操作,请参见管理全局命名空间。
1.2 Dubbo应用前提
如果可能,强烈建议您的应用升级到使用Dubbo3,并通过Triple协议进行通信。Dubbo3针对云原生部署方式进行了更完善的适配,并提供了Triple协议作为通信规范。Triple 协议完全兼容 gRPC 协议,支持 Request-Response、Streaming 流式等通信模型,可同时运行在 HTTP/1 和 HTTP/2 之上。通过使用Triple协议,Dubbo应用间通信的七层信息可被服务网格识别,带来更加完善的云原生能力。
在使用Dubbo2以及dubbo协议通信的情况下,服务网格只能拦截TCP4层流量,能够使用的服务网格能力极其有限。
2. 最佳实践场景:使用社区Dubbo Mesh接入服务网格ASM
Dubbo社区已经提供了接入服务网格的标准方案,一般情况下,都建议您参照此场景来实现您的Dubbo应用、通过云原生的方式实现服务相互调用,以无缝接入服务网格。
主要参考:https://github.com/apache/dubbo-samples/tree/master/3-extensions/registry/dubbo-samples-mesh-k8s
2. 步骤1:将Dubbo应用部署到ACK集群
使用ACK集群提供的kubeconfig连接到集群,并执行以下指令部署应用
kubectl apply -f- <<EOF apiVersion: v1 kind: Service metadata: name: dubbo-samples-mesh-provider namespace: dubbo-demo spec: type: ClusterIP sessionAffinity: None selector: app: dubbo-samples-mesh-provider ports: - name: grpc-tri port: 50052 targetPort: 50052 --- apiVersion: apps/v1 kind: Deployment metadata: name: dubbo-samples-mesh-provider-v1 namespace: dubbo-demo spec: replicas: 2 selector: matchLabels: app: dubbo-samples-mesh-provider version: v1 template: metadata: labels: app: dubbo-samples-mesh-provider version: v1 annotations: # Prevent istio rewrite http probe sidecar.istio.io/rewriteAppHTTPProbers: "false" spec: containers: - name: server image: apache/dubbo-demo:dubbo-samples-mesh-provider-v1_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 50052 protocol: TCP - name: http-health containerPort: 22222 protocol: TCP livenessProbe: httpGet: path: /live port: http-health initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 1 readinessProbe: httpGet: path: /ready port: http-health initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 startupProbe: httpGet: path: /startup port: http-health failureThreshold: 30 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 2 --- apiVersion: apps/v1 kind: Deployment metadata: name: dubbo-samples-mesh-provider-v2 namespace: dubbo-demo spec: replicas: 2 selector: matchLabels: app: dubbo-samples-mesh-provider version: v2 template: metadata: labels: app: dubbo-samples-mesh-provider version: v2 annotations: # Prevent istio rewrite http probe sidecar.istio.io/rewriteAppHTTPProbers: "false" spec: containers: - name: server image: apache/dubbo-demo:dubbo-samples-mesh-provider-v2_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 50052 protocol: TCP - name: http-health containerPort: 22222 protocol: TCP livenessProbe: httpGet: path: /live port: http-health initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 1 readinessProbe: httpGet: path: /ready port: http-health initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 startupProbe: httpGet: path: /startup port: http-health failureThreshold: 30 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 2 --- apiVersion: v1 kind: Service metadata: name: dubbo-samples-mesh-consumer namespace: dubbo-demo spec: type: ClusterIP sessionAffinity: None selector: app: dubbo-samples-mesh-consumer ports: - name: grpc-dubbo protocol: TCP port: 50052 targetPort: 50052 --- apiVersion: apps/v1 kind: Deployment metadata: name: dubbo-samples-mesh-consumer namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: dubbo-samples-mesh-consumer version: v1 template: metadata: labels: app: dubbo-samples-mesh-consumer version: v1 annotations: # Prevent istio rewrite http probe sidecar.istio.io/rewriteAppHTTPProbers: "false" spec: containers: - name: server image: apache/dubbo-demo:dubbo-samples-mesh-consumer_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 50052 protocol: TCP - name: http-health containerPort: 22222 protocol: TCP env: - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace # This environment variable does not need to be configured by default. When the domain name suffix used inside k8s is artificially changed, it is only necessary to configure this #- name: CLUSTER_DOMAIN # value: cluster.local livenessProbe: httpGet: path: /live port: http-health initialDelaySeconds: 5 periodSeconds: 5 readinessProbe: httpGet: path: /ready port: http-health initialDelaySeconds: 5 periodSeconds: 5 startupProbe: httpGet: path: /startup port: http-health failureThreshold: 30 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 EOF
所部署的应用包含一个consumer和一个provider、由consumer向provider发起调用。
服务代码可以直接查看上文参考中的Dubbo官方仓库,根据官方描述,Dubbo应用的配置中包含几个要点 :
- 保持 K8S Service 与 Dubbo application name 一致(在 dubbo.properties中定义)。
- 不使用nacos、zookeeper等注册中心,将dubbo.registry.address填写为N/A(在 dubbo.properties中定义)。
- Dubbo Consumer 服务声明中指定了消费的 Provider 服务(应用)名
@DubboReference(version = "1.0.0", providedBy = "dubbo-samples-mesh-provider", lazy = true)
有关该示例的配置文件,可详细参考dubbo.properties。
2.2 步骤2:检查consumer、provider已经通过服务网格正常通信
继续通过kubectl查看consumer端 istio-proxy日志,可以看到发送给provider的请求已经在istio-proxy中留下访问日志。
kubectl -n dubbo-demo logs deploy/dubbo-samples-mesh-consumer -c istio-proxy|grep outbound ... {"authority_for":"dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local:50052","bytes_received":"140","bytes_sent":"161","downstream_local_address":"192.168.4.76:50052","downstream_remote_address":"10.0.239.171:56276","duration":"15","istio_policy_status":"-","method":"POST","path":"/org.apache.dubbo.samples.Greeter/greetStream","protocol":"HTTP/2","request_id":"b613c0f5-1834-45d8-9857-8547d8899a02","requested_server_name":"-","response_code":"200","response_flags":"-","route_name":"default","start_time":"2024-07-15T06:56:01.457Z","trace_id":"-","upstream_cluster":"outbound|50052||dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local","upstream_host":"10.0.239.196:50052","upstream_local_address":"10.0.239.171:41202","upstream_response_time":"6","upstream_service_time":"6","upstream_transport_failure_reason":"-","user_agent":"-","x_forwarded_for":"-"}
注意到访问日志中upstream_cluster
字段已经是outbound|50052||dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local
,证明provider已经被服务网格正确注册、且请求已经被服务网格拦截并进行路由。
2.3 步骤3:部署虚拟服务、目标规则,查看服务网格路由管理
使用kubectl连接到ASM实例(参考通过控制面kubectl访问Istio资源),并通过kubectl执行以下指令。
kubectl apply -f- <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: dubbo-samples-mesh-provider namespace: dubbo-demo spec: hosts: - dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local http: - route: - destination: host: dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local subset: v1 port: # Specifies the port on the host being addressed. If the service exposes only one port, you don't need to choose the port explicitly number: 50052 weight: 80 - destination: host: dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local subset: v2 port: # Specifies the port on the host being addressed. If the service exposes only one port, you don't need to choose the port explicitly number: 50052 weight: 20 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: dubbo-samples-mesh-provider namespace: dubbo-demo spec: host: dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local trafficPolicy: loadBalancer: # Envoy load balancing strategy simple: ROUND_ROBIN subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 EOF
上述的目标规则通过pod标签将provider划分为v1、v2两个版本,虚拟服务中则指定了发往v1、v2版本的流量比例约为80:20。
此时重新部署consumer(可以删除原先的consumer pod、等待新pod拉起),并在一段时间后查看新的consumer日志,可以观察到发往provider的流量比例已经发生变化。
kubectl -n dubbo-demo logs deploy/dubbo-samples-mesh-consumer ... ==================== dubbo unary invoke 0 end ==================== [15/07/24 07:06:54:054 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v2: 10.0.239.196:50052, client: 10.0.239.196, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ==================== dubbo unary invoke 1 end ==================== [15/07/24 07:06:59:059 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v1: 10.0.239.189:50052, client: 10.0.239.189, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ==================== dubbo unary invoke 2 end ==================== [15/07/24 07:07:04:004 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v1: 10.0.239.189:50052, client: 10.0.239.189, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ==================== dubbo unary invoke 3 end ==================== [15/07/24 07:07:09:009 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v1: 10.0.239.175:50052, client: 10.0.239.175, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ==================== dubbo unary invoke 4 end ==================== [15/07/24 07:07:14:014 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v1: 10.0.239.175:50052, client: 10.0.239.175, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ==================== dubbo unary invoke 5 end ==================== [15/07/24 07:07:19:019 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello,service mesh, response from provider-v1: 10.0.239.189:50052, client: 10.0.239.189, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true" ...
可以看到,通过Dubbo官方提供的使用方式,Dubbo服务之间可以直接以云原生的k8s Service域名来相互调用,此时使用服务网格的各项能力都没有问题,Dubbo服务和普通的HTTP服务是没有区别的。
3. 过渡场景:在已有注册中心的情况下,通过插件使用服务网格
虽然上述最佳实践场景中实现了Dubbo和云原生基础设施的无缝结合,但是该场景要求应用不使用注册中心,对于一些已经接入注册中心并正在运行的Dubbo应用来说,可能无法立刻切换到这种方式上来。
在这种情况下,服务网格ASM在插件中心中提供了相关的支持插件,让应用能够先接入服务网格、然后过渡到社区最佳实践的云原生方案上。
3.1 步骤1:部署示例服务到Kubernetes集群
通过ACK集群的kubeconfig连接到集群、使用kubectl执行以下指令来部署示例应用。
apiVersion: v1 kind: ConfigMap metadata: name: frontend-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-frontend # Specify the QoS port dubbo.application.qos-port=20991 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20881 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 #spring.freemarker.template-loader-path: /templates spring.freemarker.suffix=.ftl server.servlet.encoding.force=true server.servlet.encoding.charset=utf-8 server.servlet.encoding.enabled=true --- apiVersion: v1 kind: ConfigMap metadata: name: shop-order-v1-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-order # Specify the QoS port dubbo.application.qos-port=20992 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20882 dubbo.protocol.name=tri --- apiVersion: v1 kind: ConfigMap metadata: name: shop-order-v2-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-order # Specify the QoS port dubbo.application.qos-port=20993 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20882 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: ConfigMap metadata: name: shop-user-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-user # Specify the QoS port dubbo.application.qos-port=20994 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20884 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: ConfigMap metadata: name: shop-detail-v1-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-detail # Specify the QoS port dubbo.application.qos-port=20995 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20885 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: ConfigMap metadata: name: shop-detail-v2-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-detail # Specify the QoS port dubbo.application.qos-port=20996 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20885 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: ConfigMap metadata: name: comment-v1-config namespace: dubbo-demo data: application.properties: |- dubbo.application.name=shop-comment # Specify the QoS port dubbo.application.qos-port=20997 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20887 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: ConfigMap metadata: name: comment-v2-config namespace: dubbo-demo data: application.properties: |- # Specify the application name of Dubbo dubbo.application.name=shop-comment # Specify the QoS port dubbo.application.qos-port=20998 # Enable token verification for each invocation dubbo.provider.token=false # Specify the registry address # dubbo.registry.address=nacos://localhost:8848?username=nacos&password=nacos dubbo.registry.address=nacos://${nacos.address:localhost}:8848?username=nacos&password=nacos # Specify the port of Dubbo protocol dubbo.protocol.port=20888 dubbo.protocol.name=tri dubbo.protocol.serialization=hessian2 --- apiVersion: v1 kind: Namespace metadata: name: dubbo-system --- # Nacos apiVersion: apps/v1 kind: Deployment metadata: name: nacos namespace: dubbo-system spec: replicas: 1 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: consumer image: nacos/nacos-server:v2.1.2 imagePullPolicy: Always resources: requests: memory: "2Gi" cpu: "500m" ports: - containerPort: 8848 name: client - containerPort: 9848 name: client-rpc env: - name: NACOS_SERVER_PORT value: "8848" - name: NACOS_APPLICATION_PORT value: "8848" - name: PREFER_HOST_MODE value: "hostname" - name: MODE value: "standalone" - name: NACOS_AUTH_ENABLE value: "true" --- apiVersion: v1 kind: Service metadata: name: nacos namespace: dubbo-system spec: type: ClusterIP sessionAffinity: None selector: app: nacos ports: - port: 8848 name: server targetPort: 8848 - port: 9848 name: client-rpc targetPort: 9848 --- # Dubbo Admin apiVersion: v1 kind: ConfigMap metadata: name: dubbo-admin namespace: dubbo-system data: # Set the properties you want to override, properties not set here will be using the default values # check application.properties inside dubbo-admin project for the keys supported application.properties: | admin.registry.address=nacos://nacos.dubbo-system.svc:8848?username=nacos&password=nacos admin.config-center=nacos://nacos.dubbo-system.svc:8848?username=nacos&password=nacos admin.metadata-report.address=nacos://nacos.dubbo-system.svc:8848?username=nacos&password=nacos --- apiVersion: apps/v1 kind: Deployment metadata: name: dubbo-admin namespace: dubbo-system labels: app: dubbo-admin spec: replicas: 2 selector: matchLabels: app: dubbo-admin template: metadata: labels: app: dubbo-admin spec: containers: - image: apache/dubbo-admin:0.6.0 name: dubbo-admin ports: - containerPort: 38080 volumeMounts: - mountPath: /config name: application-properties volumes: - name: application-properties configMap: name: dubbo-admin --- apiVersion: v1 kind: Service metadata: name: dubbo-admin namespace: dubbo-system spec: selector: app: dubbo-admin ports: - protocol: TCP port: 38080 targetPort: 38080 --- # Skywalking apiVersion: apps/v1 kind: Deployment metadata: name: skywalking-oap-server namespace: dubbo-system spec: replicas: 1 selector: matchLabels: app: skywalking-oap-server template: metadata: labels: app: skywalking-oap-server spec: containers: - name: skywalking-oap-server image: apache/skywalking-oap-server:9.3.0 imagePullPolicy: Always --- apiVersion: apps/v1 kind: Deployment metadata: name: skywalking-oap-dashboard namespace: dubbo-system spec: replicas: 1 selector: matchLabels: app: skywalking-oap-dashboard template: metadata: labels: app: skywalking-oap-dashboard spec: containers: - name: skywalking-oap-dashboard image: apache/skywalking-ui:9.3.0 imagePullPolicy: Always env: - name: SW_OAP_ADDRESS value: http://skywalking-oap-server.dubbo-system.svc:12800 --- apiVersion: v1 kind: Service metadata: name: skywalking-oap-server namespace: dubbo-system spec: type: ClusterIP sessionAffinity: None selector: app: skywalking-oap-server ports: - port: 12800 name: rest targetPort: 12800 - port: 11800 name: grpc targetPort: 11800 --- apiVersion: v1 kind: Service metadata: name: skywalking-oap-dashboard namespace: dubbo-system spec: type: ClusterIP sessionAffinity: None selector: app: skywalking-oap-dashboard ports: - port: 8080 name: http targetPort: 8080 --- apiVersion: v1 kind: Service metadata: labels: app: shop-frontend name: shop-frontend namespace: dubbo-demo spec: ports: - name: http-8080 port: 8080 protocol: TCP targetPort: 8080 - name: grpc-tri port: 20881 protocol: TCP targetPort: 20881 selector: app: shop-frontend --- # App FrontEnd apiVersion: apps/v1 kind: Deployment metadata: name: shop-frontend namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-frontend template: metadata: labels: app: shop-frontend spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: frontend-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-frontend image: apache/dubbo-demo:dubbo-samples-shop-frontend_0.0.1 imagePullPolicy: Always ports: - name: http-8080 containerPort: 8080 protocol: TCP - name: grpc-tri containerPort: 20881 protocol: TCP - name: dubbo-qos containerPort: 20991 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-frontend - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" --- apiVersion: v1 kind: Service metadata: labels: app: shop-order name: shop-order namespace: dubbo-demo spec: ports: - name: grpc-tri port: 20882 protocol: TCP targetPort: 20882 selector: app: shop-order --- # App Order V1-1 apiVersion: apps/v1 kind: Deployment metadata: name: shop-order-v1 namespace: dubbo-demo spec: replicas: 2 selector: matchLabels: app: shop-order orderVersion: v1 template: metadata: labels: app: shop-order orderVersion: v1 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: shop-order-v1-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-order image: apache/dubbo-demo:dubbo-samples-shop-order_v1_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20882 protocol: TCP - name: dubbo-qos containerPort: 20992 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-order - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "orderVersion=v1" --- # App Order V2 apiVersion: apps/v1 kind: Deployment metadata: name: shop-order-v2 namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-order orderVersion: v2 template: metadata: labels: app: shop-order orderVersion: v2 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: shop-order-v2-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-order image: apache/dubbo-demo:dubbo-samples-shop-order_v2_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20882 protocol: TCP - name: dubbo-qos containerPort: 20993 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-order - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "orderVersion=v2;" --- apiVersion: v1 kind: Service metadata: labels: app: shop-user name: shop-user namespace: dubbo-demo spec: ports: - name: grpc-tri port: 20884 protocol: TCP targetPort: 20884 selector: app: shop-user --- # App User apiVersion: apps/v1 kind: Deployment metadata: name: shop-user namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-user template: metadata: labels: app: shop-user spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: shop-user-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-user image: apache/dubbo-demo:dubbo-samples-shop-user_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20884 protocol: TCP - name: dubbo-qos containerPort: 20994 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-user - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" --- apiVersion: v1 kind: Service metadata: labels: app: shop-detail name: shop-detail namespace: dubbo-demo spec: ports: - name: grpc-tri port: 20885 protocol: TCP targetPort: 20885 selector: app: shop-detail --- # App Detail-1 apiVersion: apps/v1 kind: Deployment metadata: name: shop-detail-v1 namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-detail detailVersion: v1 template: metadata: labels: app: shop-detail detailVersion: v1 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: shop-detail-v1-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-detail image: apache/dubbo-demo:dubbo-samples-shop-detail_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20885 protocol: TCP - name: dubbo-qos containerPort: 20995 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-detail - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "detailVersion=v1; region=beijing" --- # App Detail-2 apiVersion: apps/v1 kind: Deployment metadata: name: shop-detail-v2 namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-detail detailVersion: v2 template: metadata: labels: app: shop-detail detailVersion: v2 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: shop-detail-v2-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-detail image: apache/dubbo-demo:dubbo-samples-shop-detail_v2_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20885 protocol: TCP - name: dubbo-qos containerPort: 20996 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-detail - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "detailVersion=v2; region=hangzhou;" --- apiVersion: v1 kind: Service metadata: labels: app: shop-comment name: shop-comment namespace: dubbo-demo spec: ports: - name: grpc-tri port: 20887 protocol: TCP targetPort: 20887 selector: app: shop-comment --- #App Comment v1 apiVersion: apps/v1 kind: Deployment metadata: name: shop-comment-v1 namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-comment commentVersion: v1 template: metadata: labels: app: shop-comment commentVersion: v1 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: comment-v1-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-comment image: apache/dubbo-demo:dubbo-samples-shop-comment_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20887 protocol: TCP - name: dubbo-qos containerPort: 20997 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-comment - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "commentVersion=v1; region=beijing" --- #App Comment v2 apiVersion: apps/v1 kind: Deployment metadata: name: shop-comment-v2 namespace: dubbo-demo spec: replicas: 1 selector: matchLabels: app: shop-comment commentVersion: v2 template: metadata: labels: app: shop-comment commentVersion: v2 spec: volumes: - name: skywalking-agent emptyDir: { } - name: config-volume configMap: name: comment-v2-config items: - key: application.properties path: application.properties initContainers: - name: agent-container image: apache/skywalking-java-agent:8.13.0-java17 volumeMounts: - name: skywalking-agent mountPath: /agent command: [ "/bin/sh" ] args: [ "-c", "cp -R /skywalking/agent /agent/" ] containers: - name: shop-comment image: apache/dubbo-demo:dubbo-samples-shop-comment_v2_0.0.1 imagePullPolicy: Always ports: - name: grpc-tri containerPort: 20887 protocol: TCP - name: dubbo-qos containerPort: 20998 protocol: TCP volumeMounts: - name: skywalking-agent mountPath: /skywalking - name: config-volume mountPath: /app/resources/application.properties subPath: application.properties env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/skywalking/agent/skywalking-agent.jar" - name: SW_AGENT_NAME value: shop::shop-comment - name: SW_AGENT_COLLECTOR_BACKEND_SERVICES value: "skywalking-oap-server.dubbo-system.svc:11800" - name: DUBBO_LABELS value: "commentVersion=v2; region=hangzhou;" EOF
此处的示例服务使用了Dubbo社区的sample-shop应用,有关应用的具体介绍以及代码实现,可以参考链接。
该示例应用的所有服务都接入了一个nacos注册中心(部署在dubbo-system下),可以利用这一点演示这一类使用注册中心的应用过渡使用服务网格的过程。
相比社区提供的示例应用,上面的YAML主要做了以下更改以进行场景演示:
- 由于该示例应用还是使用dubbo协议在通信,这里通过configmap覆盖社区示例镜像里的application.properties,将通信协议改成Triple。对于当前的Dubbo版本,这样的做法仍可能导致Dubbo服务调用因为缺少protobuf依赖而失败,后续主要通过访问日志先看下接入服务网格的效果。
- 给每个集群中的Deployment都部署了对应的k8s Service。声明k8s Service是Dubbo引用向云原生化过渡的第一步,建议可以将k8s Service与Dubbo的application name进行统一、方便管理。服务网格将以k8s Service域名为基础管理服务的流量。
3.2 步骤2:在服务网格ASM中开启插件
当Dubbo应用使用注册中心时,调用端会通过注册信息直接得知服务端地址、并通过地址进行调用,期间并不会通过k8s Service域名发起调用。因此,直接为此类应用注入Sidecar并不能使得Sidecar拦截和管理服务之间调用的流量。
服务网格ASM提供了“支持Spring Cloud服务”插件,其主要原理是给请求加上host header来帮助服务网格识别请求的目标k8s Service、进而完成对流量的管理。从功能上来讲同样支持Dubbo应用以相同方案过渡使用服务网格能力,但要求Dubbo应用必须使用Triple等基于HTTP协议的RPC协议。
具体插件开启步骤如下:
- 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理。
- 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择插件扩展中心 > 插件市场。
- 在插件市场页面,单击支持Spring Cloud服务,进入插件详情页面。
- 在插件详情页面,单击使用指引,了解插件的具体功能和插件配置YAML的相关说明。
- 在插件详情页面,单击新建插件实例,将插件生效范围调整为全局生效,配置插件参数,然后在页面下方,打开生效开关。
对于支持Spring Cloud服务插件来说,其主要参数包含集群中的pod_cidrs以及provider_port_number。
- pod_cidrs:ACK或ASK集群的Pod CIDR。您可以登录容器服务管理控制台,在集群信息页面的基本信息 页签下查看Pod CIDR。若容器网络插件采用Terway ,您可以在集群信息页面的集群资源页签查看Pod虚拟交换机对应的CIDR进行配置。该参数为必填,默认值仅为一个示例值。
- provider_port_number:provider的服务端口,在集群中部署了多个provider、各自有不同的服务端口时,可以配置多个插件实例、或者不配置此参数。
例如可以使用如下参数填写:
provider_port_number: '20881' # provider为20881端口 pod_cidrs: - 10.0.0.0/16 # 集群中pod cidr、或者集群交换机cidr(在使用terway的情况下)
3.3 步骤3:检查应用已经接入网格
可通过port-forward方式获取应用访问地址
kubectl port-forward -n dubbo-demo deployment/shop-frontend 8080:8080
浏览器访问localhost:8080,使用任意字符充当用户名和密码登录。
由于此示例还没完全适配Triple协议,应用会产生报错,但通过frontend服务的istio-proxy可以看到访问日志:
kubectl -n dubbo-demo logs deploy/shop-frontend -c istio-proxy ... {"authority_for":"shop-detail.dubbo-demo.svc.cluster.local:20885","bytes_received":"69","bytes_sent":"0","downstream_local_address":"10.0.239.181:20885","downstream_remote_address":"10.0.239.165:35638","duration":"31","istio_policy_status":"-","method":"POST","path":"/org.apache.dubbo.metadata.MetadataService/getMetadataInfo","protocol":"HTTP/2","request_id":"240aaced-a67d-4b10-a252-a738f67b346d","requested_server_name":"-","response_code":"200","response_flags":"URX","route_name":"default","start_time":"2024-07-15T08:21:29.620Z","trace_id":"-","upstream_cluster":"outbound|20885||shop-detail.dubbo-demo.svc.cluster.local","upstream_host":"10.0.239.169:20885","upstream_local_address":"10.0.239.165:52506","upstream_response_time":"31","upstream_service_time":"31","upstream_transport_failure_reason":"-","user_agent":"-","x_forwarded_for":"-"}
可以看到类似上述的日志,和最佳实践场景中的访问日志一样,可以看到请求以及路由目标的k8s Service都已经被服务网格正确识别。
如果关掉插件,再次访问时,则会看到类似这样的访问日志
kubectl -n dubbo-demo logs deploy/shop-frontend -c istio-proxy ... {"authority_for":"10.0.239.163:20884","bytes_received":"65","bytes_sent":"0","downstream_local_address":"10.0.239.163:20884","downstream_remote_address":"10.0.239.165:56152","duration":"2","istio_policy_status":"-","method":"POST","path":"/org.apache.dubbo.samples.UserService/login","protocol":"HTTP/2","request_id":"1560b7ef-e9f6-4ddf-883b-e4f56d8753db","requested_server_name":"-","response_code":"200","response_flags":"-","route_name":"allow_any","start_time":"2024-07-15T08:30:02.068Z","trace_id":"-","upstream_cluster":"PassthroughCluster","upstream_host":"10.0.239.163:20884","upstream_local_address":"10.0.239.165:56166","upstream_response_time":"2","upstream_service_time":"2","upstream_transport_failure_reason":"-","user_agent":"-","x_forwarded_for":"-"
可以看到upstream_cluster字段为PassthroughCluster。此时服务网格无法将被调用服务识别为被注册在服务网格内的k8s服务,应用也将无法使用服务网格的云原生能力。
注:
上述场景应该只作为过渡方案来使用,最终我们仍然建议您过渡到Dubbo社区提供的最佳实践方案来为Dubbo应用接入服务网格,以避免任何未来可能发生的不兼容或错误。
4. 小结
本文主要就Dubbo应用如何接入服务网格、获得各项云原生能力进行了探讨,并提出了最佳实践以及过渡两种实践场景。我们首先推荐您使用Dubbo社区提供的最佳实践场景来接入服务网格,在必要时可以通过过渡方案来向最佳实践方案逐步实现过渡。