使用 Envoy 和 Istio 的 Dubbo 代理網格

此範例示範如何使用 Istio + Envoy 的服務網格部署模式來開發 Dubbo3 服務。Dubbo3 服務使用 Triple 作為通訊協定。通訊過程由 Envoy 資料平面攔截,並使用標準的 Istio 流量管理功能來管理 Dubbo。

按照以下步驟,您可以輕鬆學習如何開發符合服務網格架構的 Dubbo 服務,將其部署到 Kubernetes,並使用 Istio 的流量管理系統。在此查看完整的範例原始碼

1 總體目標

  • 將 Dubbo 應用程式部署到 Kubernetes
  • Istio 自動注入 Envoy 並實現流量攔截
  • 基於 Istio 規則的流量治理

2 基本流程和工作原理

此範例示範如何在 Istio 系統下部署由 Dubbo 開發的應用程式,以實現 Envoy 對 Dubbo 服務的自動代理。範例的整體架構如下圖所示。

thinsdk

您需要完成範例的步驟如下:

  1. 創建一個 Dubbo 應用程式 (dubbo-samples-mesh-k8s)
  2. 構建容器映像並將其推送到映像倉庫(此範例的官方映像
  3. 分別將 Dubbo 提供者和 Dubbo 消費者部署到 Kubernetes,並驗證 Envoy 代理注入是否成功
  4. 驗證 Envoy 發現服務地址,正常攔截 RPC 流量並實現負載均衡
  5. 基於 Istio VirtualService 的比例流量轉發

3 詳細步驟

3.1 環境需求

請確保在本地安裝以下環境,以提供容器運行時、Kubernetes 叢集和存取工具

使用以下命令啟動本地 Kubernetes 叢集

minikube start

使用 kubectl 檢查叢集是否已啟動並正在運行,以及 kubectl 是否已綁定到預設的本地叢集

kubectl cluster-info

3.2 建立獨立命名空間並開啟自動注入

使用以下指令為範例專案建立一個獨立的命名空間 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) 中也指定了消費的 Provider 服務(應用程式)名稱。

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 實現比例流量轉發

部署 v2 版本的 demo provider

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 修改範例

  1. 修改範例不是必要的步驟,本節適用於想要調整程式碼並查看部署效果的讀者。
  2. 請注意,專案原始碼儲存路徑必須使用英文,否則 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 註冊表組織 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 註冊表身份驗證資訊。您可以在命令列上直接指定 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 備註

  1. 在範例中,生產者和消費者都屬於同一個命名空間;如果您需要呼叫不同命名空間的 Provider,則需要按如下方式配置(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"/>

上次修改時間:2023 年 1 月 2 日:增強英文文件 (#1798) (95a9f4f6c1c)