使用 Envoy 和 Istio 的 Dubbo 代理网格
通过以下步骤,您可以轻松学习如何开发符合服务网格架构的 Dubbo 服务,将其部署到 Kubernetes,并访问 Istio 的流量管理系统。查看这里 完整的示例源代码
1 整体目标
- 将 Dubbo 应用程序部署到 Kubernetes
- Istio 自动注入 Envoy 并实现流量拦截
- 基于 Istio 规则的流量治理
2 基本流程和工作原理
本示例演示如何在 Istio 系统下部署由 Dubbo 开发的应用程序,实现 Envoy 对 Dubbo 服务的自动代理。示例的整体架构如下图所示。
您需要完成以下步骤才能完成示例
- 创建一个 Dubbo 应用程序 ( dubbo-samples-mesh-k8s )
- 构建容器镜像并将其推送到镜像仓库 (本示例的官方镜像 )
- 分别将 Dubbo Provider 和 Dubbo Consumer 部署到 Kubernetes,并验证 Envoy 代理注入是否成功
- 验证 Envoy 是否发现服务地址,正常拦截 RPC 流量并实现负载均衡
- 基于 Istio VirtualService 的比例流量转发
3 详细步骤
3.1 环境要求
请确保本地安装了以下环境,以提供容器运行时、Kubernetes 集群和访问工具
使用以下命令启动本地 Kubernetes 集群
minikube start
使用 kubectl 检查集群是否已启动并运行,以及 kubectl 是否绑定到默认本地集群
kubectl cluster-info
3.2 创建独立命名空间并启用自动注入
使用以下命令为示例项目创建一个独立的 Namespace dubbo-demo
,并同时启用 sidecar 自动注入。
# Initialize the namespace
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/Namespace.yml
# switch namespace
kubens dubbo-demo
# dubbo-demo enable automatic injection
kubectl label namespace dubbo-demo istio-injection=enabled
3.3 部署到 Kubernetes
3.3.1 部署 Provider
# Deploy the Service
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/provider/Service.yml
# Deploy Deployment
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/provider/Deployment.yml
以上命令创建了一个名为 dubbo-samples-mesh-provider
的 Service,注意这里服务名称与项目中 dubbo 应用程序名称相同。
然后 Deployment 部署了 2 个副本的 pod 实例,并启动了 Provider。
您可以使用以下命令检查启动日志。
# View pod list
kubectl get pods -l app=dubbo-samples-mesh-provider
# View pod deployment logs
kubectl logs your-pod-id
此时,pod 中应该有一个 dubbo provider 容器实例和一个 Envoy Sidecar 容器实例。
3.3.2 部署 Consumer
# Deploy the Service
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/consumer/Service.yml
# Deploy Deployment
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/consumer/Deployment.yml
部署 consumer 和 provider 是一样的,这里也保持 K8S Service 和 Dubbo consumer 应用程序名称(在 [dubbo.properties](https://github.com/apache/dubbo-samples/blob/master/3-extensions/registry //dubbo-samples-mesh-k8s/dubbo-samples-mesh-consumer/src/main/resources/spring/dubbo-consumer.properties) 中一致:dubbo.application.name=dubbo-samples-mesh- consumer
。
消费者的提供者服务(应用程序)名称也在 Dubbo Consumer 服务声明中指定
@DubboReference(version = "1.0.0", providedBy = "dubbo-samples-mesh-provider", lazy = true)
3.4 检查 Provider 和 Consumer 之间的正常通信
执行完步骤 3.3 后,检查启动日志,查看 consumer 是否已完成对 provider 服务的消费。
# View pod list
kubectl get pods -l app=dubbo-samples-mesh-consumer
# View pod deployment logs
kubectl logs your-pod-id
# View pod isitio-proxy logs
kubectl logs your-pod-id -c istio-proxy
您可以看到 consumer pod 日志输出如下(Triple 协议由 Envoy 代理进行负载均衡)
===================== dubbo invoke 0 end ======================
[10/08/22 07:07:36:036 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.22:50052, client: 172.18. 96.22, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 1 end ======================
[10/08/22 07:07:42:042 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
consumer istio-proxy 日志输出如下
[2022-07-15T05:35:14.418Z] "POST /org.apache.dubbo.samples.Greeter/greet HTTP/2" 200
- via_upstream - "-" 19 160 2 1 "-" "-" "6b8a5a03-5783-98bf-9bee-f93ea6e3d68e"
"dubbo-samples-mesh-provider:50052" "172.17.0.4:50052"
outbound|50052||dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local 172.17.0.7:52768 10.101.172.129:50052 172.17.0.7:38488 - default
您可以看到 provider pod 日志输出如下
[10/08/22 07:08:47:047 UTC] tri-protocol-50052-thread-8 INFO impl.GreeterImpl: Server test dubbo tri mesh received greet request name: "service mesh"
[10/08/22 07:08:57:057 UTC] tri-protocol-50052-thread-9 INFO impl.GreeterImpl: Server test dubbo tri mesh received greet request name: "service mesh"
provider istio-proxy 的日志输出如下
[2022-07-15T05:25:34.061Z] "POST /org.apache.dubbo.samples.Greeter/greet HTTP/2" 200
- via_upstream - "-" 19 162 1 1 "-" "-" "201e6976-da10-96e1-8da7-ad032e58db47"
"dubbo-samples-mesh-provider:50052" "172.17.0.10:50052"
inbound|50052|| 127.0.0.6:47013 172.17.0.10:50052 172.17.0.7:60244
outbound_.50052_._.dubbo-samples-mesh-provider.dubbo-demo.svc.cluster.local default
3.5 流量治理 - VirtualService 实现比例流量转发
部署 demo provider 的 v2 版本
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/provider/Deployment-v2.yml
设置 VirtualService 和 DestinationRule,观察流量分别以 4:1 的比例被定向到 provider v1 和 provider v2。
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/3-extensions/registry/dubbo-samples-mesh-k8s/deploy/traffic/virtual-service.yml
从 consumer 侧的日志输出中,观察流量分配效果,如下图所示
===================== dubbo invoke 100 end ======================
[10/08/22 07:15:58:058 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 101 end ======================
[10/08/22 07:16:03:003 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.22:50052, client: 172.18. 96.22, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 102 end ======================
[10/08/22 07:16:08:008 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 103 end ======================
[10/08/22 07:16:13:013 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v2: 172.18.96.6:50052, client: 172.18. 96.6, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 104 end ======================
[10/08/22 07:16:18:018 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.22:50052, client: 172.18. 96.22, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 105 end ======================
[10/08/22 07:16:24:024 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 106 end ======================
[10/08/22 07:16:29:029 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.22:50052, client: 172.18. 96.22, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 107 end ======================
[10/08/22 07:16:34:034 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 108 end ======================
[10/08/22 07:16:39:039 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.22:50052, client: 172.18. 96.22, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
===================== dubbo invoke 109 end ======================
[10/08/22 07:16:44:044 UTC] main INFO action.GreetingServiceConsumer: consumer Unary reply <-message: "hello, service mesh, response from provider-v1: 172.18.96.18:50052, client: 172.18. 96.18, local: dubbo-samples-mesh-provider, remote: null, isProviderSide: true"
3.6 查看仪表盘
参见 Istio 官方网站上的 如何启动仪表盘。
4 修改示例
- 修改示例不是必需的步骤,本节适用于希望调整代码并查看部署效果的读者。
- 注意,项目源代码存储路径必须为英文,否则 protobuf 编译会失败。
修改 Dubbo Provider 配置 dubbo-provider.properties
# provider
dubbo.application.name=dubbo-samples-mesh-provider
dubbo.application.metadataServicePort=20885
dubbo.registry.address=N/A
dubbo.protocol.name=tri
dubbo.protocol.port=50052
dubbo.application.qosEnable=true
# In order for the Kubernetes cluster to access the probe normally, it is necessary to enable QOS to allow remote access, which may bring security Risk, please carefully evaluate before opening
dubbo.application.qosAcceptForeignIp=true
修改 Dubbo Consumer 配置 dubbo-consumer.properties
# consumer
dubbo.application.name=dubbo-samples-mesh-consumer
dubbo.application.metadataServicePort=20885
dubbo.registry.address=N/A
dubbo.protocol.name=tri
dubbo.protocol.port=20880
dubbo.consumer.timeout=30000
dubbo.application.qosEnable=true
# In order for the Kubernetes cluster to access the probe normally, it is necessary to enable QOS to allow remote access. This operation may bring security risks. Please evaluate it carefully before opening it
dubbo.application.qosAcceptForeignIp=true
# Flag to enable mesh sidecar proxy mode
dubbo.consumer.meshEnable=true
完成代码修改后,通过项目提供的 Dockerfile 打包镜像
# Package and push the image
mvn compile jib:build
Jib 插件会自动打包并发布镜像。注意,对于本地开发,您需要将 jib 插件配置中的 docker registry 组织 apache/dubbo-demo 更改为具有您自己权限的组织(包括其他 kubernetes manifests 中的 dubboteam,以确保 kubernetes 部署您自己的自定义镜像),如果您遇到 jib 插件身份验证问题,请参考 [相应链接](https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#what-should-i-do-when-the- registry-responds-with-unauthorized) 配置 docker registry 身份验证信息。您可以在命令行上直接指定
mvn compile jib:build -Djib.to.auth.username=x -Djib.to.auth.password=x -Djib.from.auth.username=x -Djib.from.auth.username=x
,或者使用 docker-credential-helper。
5 常用命令
# dump current Envoy configs
kubectl exec -it ${your pod id} -c istio-proxy curl http://127.0.0.1:15000/config_dump > config_dump
# Enter the istio-proxy container
kubectl exec -it ${your pod id} -c istio-proxy -- /bin/bash
# View container logs
kubectl logs ${your pod id} -n ${your namespace}
kubectl logs ${your pod id} -n ${your namespace} -c istio-proxy
# Enable automatic sidecar injection
kubectl label namespace ${your namespace} istio-injection=enabled --overwrite
# Turn off automatic sidecar injection
kubectl label namespace ${your namespace} istio-injection=disabled --overwrite
6 注意事项
- 在示例中,生产者和消费者都属于同一个命名空间;如果您需要调用不同命名空间的提供者,则需要进行如下配置(dubbo 版本>=3.1.2)
注解方式
@DubboReference(providedBy = "istio-demo-dubbo-producer", providerPort = 20885, providerNamespace = "istio-demo")
xml 方式
<dubbo:reference id="demoService" check="true"
interface="org.apache.dubbo.samples.basic.api.DemoService"
provider-port="20885"
provided-by="istio-dubbo-producer"
provider-namespace="istio-demo"/>