天天看點

非容器應用與K8s工作負載的服務網格化實踐-4 基于ASM的POD和VM互訪實踐-GRPC協定篇

為了實作高可用,非容器應用通常有一套服務注冊和發現的機制。相對而言,kubernetes容器服務為POD提供了統一的基于dns的注冊和發現機制。對于http協定的非容器應用的遷移,因為都是基于dns機制,切換成本交底。而對于使用非http協定的非容器應用,服務注冊和發現這個技術點為遷移帶來了額外的困難。

如何從非容器的注冊和發現大腦最終遷移到kubernetes的注冊和發現大腦,目前尚沒有廣泛認可的方案。目前常見的讨論是雙腦方案,就是讓非容器應用在啟動時同時向兩套大腦注冊,并從兩套中發現依賴服務,然後逐漸實作向kubernetes大腦過度。這套方案的優點是可以通過随時擺動來保證可用性。

我個人比較傾向于徹底的kubernetes一腦模式。相比多腦,一腦的複雜度會極大下降。遺留大腦叢集保持現狀,kubernetes大腦叢集使用新的方案。這樣可以将服務發現和網格化解耦為兩個獨立的問題。借助服務網格的藍綠部署/流量轉移,可以将驗證完畢依賴關系的叢集逐漸放行并最終替換遺留大腦叢集。這個方案最有挑戰的地方是如何在不改變非容器應用代碼的前提下,将其底層協定替換。如果可以替換為grpc,那麼就可以按照本篇的實踐實作網格化。目前這個想法還很不成熟,期待交流。

1 POD和VM互訪

路由規則

服務網格的流量轉移是一腦方案的核心。首先需要明确的是,網格化的服務鍊路都會經過sidecar,sidecar内部(envoy)實作了對grpc的負載均衡,grpc協定的上遊服務通過長連結和下遊服務各個節點保持連接配接,在不配置流量轉移的情況下,上遊請求會均勻地路由到下遊各個節點。

與http協定通過path配置流量轉移類似,grpc的配置是通過

service/method

或者

service.method

來定義比對規則的。grpc的路由配置示意如下。

spec:
  hosts:
    - hello2-svc
  http:
    - name: grpc-hello-route
      match:
        - uri:
            prefix: /org.feuyeux.grpc.Greeter/SayHello
      route:
        - destination:
            host: hello2-svc
            subset: v1
          weight: 30
        - destination:
            host: hello2-svc
            subset: v2
          weight: 60
        - destination:
            host: hello2-svc
            subset: v3
          weight: 10           

搭建實驗環境

本篇示例實驗與前一篇相仿,不再冗述重複部分。我們希望路由到hello2

en

/

fr

es

的流量比例為:30%:60%:10%。

示例(grpc_reciprocal_demo)

包含如下元素:

  • hello1 deployment(鏡像grpc_springboot_v1)
  • hello3 deployment(鏡像grpc_springboot_v1)
  • hello2 docker containers(鏡像grpc_springboot_v1/grpc_springboot_v2/grpc_springboot_v3)
  • 入口網關:istio-ingressgateway
  • 入口流量配置:gateway/virtualservice
  • hello1流量配置:hello1 service
  • hello2流量配置:hello2 service/hello2 virtualservice/hello2 destinationrule
  • hello3流量配置:hello3 service
  • hello2 serviceentry/hello2 workloadentry

與前一篇http所用示例類似,grpc示例使用的鏡像為grpc_springboot-{version},是一個基于springboot開發的grpc服務(源代碼在

這裡

)。每個執行個體可以通過環境變量

GRPC_HELLO_BACKEND

定義下遊服務。

啟動hello2應用

通過如下指令分别在3個ecs節點上啟動hello2的 docker container

#ssh1.sh
docker run \
--rm \
--network host \
--name http_v1 \
-e GRPC_HELLO_BACKEND=hello3-svc.grpc-reciprocal-hello.svc.cluster.local \
registry.cn-beijing.aliyuncs.com/asm_repo/grpc_springboot_v1:1.0.0
#ssh2.sh
docker run \
--rm \
--network host \
--name http_v2 \
-e GRPC_HELLO_BACKEND=hello3-svc.grpc-reciprocal-hello.svc.cluster.local \
registry.cn-beijing.aliyuncs.com/asm_repo/grpc_springboot_v2:1.0.0
#ssh3.sh
docker run \
--rm \
--network host \
--name http_v3 \
-e GRPC_HELLO_BACKEND=hello3-svc.grpc-reciprocal-hello.svc.cluster.local \
registry.cn-beijing.aliyuncs.com/asm_repo/grpc_springboot_v3:1.0.0           

其他部分的搭建與前篇類似,可以參考前篇和本篇示例

腳本

,餘文不在冗述。

grpcurl

相對于http的驗證工具curl,我們使用

對grpc服務進行驗證。

當grpc服務提供反射protocolbuf能力時,可以使用如下指令進行請求驗證:

k exec "$hello1_pod" -c hello-v1-deploy -n grpc-reciprocal-hello \
  -- grpcurl -plaintext -d '{"name":"eric"}' \
  hello3-svc.grpc-reciprocal-hello.svc.cluster.local:7001 org.feuyeux.grpc.Greeter/SayHello | jq -r '.reply'
  
Hello eric(172.18.1.10)           

如果上述指令傳回錯誤,說明grpc服務無法反射protocolbuf,需要在執行

grpccurl

時,通過

-import-path

-proto

參數提供proto檔案。示意如下。

k exec "$hello1_pod" -c hello-v1-deploy -n grpc-reciprocal-hello -- \
grpcurl -plaintext -d '{"name":"eric"}' \
-import-path /opt -proto hello.proto \
hello3-svc.grpc-reciprocal-hello.svc.cluster.local:7001 org.feuyeux.grpc.Greeter/SayHello | jq -r '.reply'

Hello eric(172.18.1.10)           

KUBE驗證-POD和VM互訪

初步掌握

grpccurl

指令後,我們驗證基于grpc協定的POD和VM互訪。請求從hello1 POD發向hello2 service,然後路由到各VM中的hello2 app,再由hello2 app請求到hello3 POD。

for i in {1..6}; do
  k exec "$hello1_pod" -c hello-v1-deploy -n grpc-reciprocal-hello \
    -- grpcurl -plaintext -d '{"name":"eric"}' localhost:7001 org.feuyeux.grpc.Greeter/SayHello | jq -r '.reply'
done           
Hello eric(172.18.1.11)<-Hola eric(192.168.0.172)<-Hello eric(172.18.1.10)
Hello eric(172.18.1.11)<-Bonjour eric(192.168.0.171)<-Hello eric(172.18.1.10)
Hello eric(172.18.1.11)<-Hello eric(192.168.0.170)<-Hello eric(172.18.1.10)
...           

MESH驗證-全鍊路流量轉移

最後部署gateway/virtualservice/destinationrule進行端到端驗證。

IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

for i in {1..100}; do
  resp=$(grpcurl -plaintext -d '{"name":"eric"}' "$IP":7004 org.feuyeux.grpc.Greeter/SayHello | jq -r '.reply')
  echo "$resp" >>test_traffic_shift_result
done

echo "expected 30%(Hello eric)-60%(Bonjour eric)-10%(Hola eric):"
sort test_traffic_shift_result | grep -v "^[[:space:]]*$"| uniq -c | sort -nrk1           
57 Hello eric(172.18.1.11)<-Bonjour eric(192.168.0.171)<-Hello eric(172.18.1.10)
  32 Hello eric(172.18.1.11)<-Hello eric(192.168.0.170)<-Hello eric(172.18.1.10)
  11 Hello eric(172.18.1.11)<-Hola eric(192.168.0.172)<-Hello eric(172.18.1.10)           

2 grpc基準測試

ghz

是grpc協定的基準測試工具,功能與http協定下的

apache bench fortio

類似。我們可以通過ghz對服務網格内的grpc服務進行測試和調參。ghz的基本用法示意如下。

hello1_pod=$(k get pod -l app=hello1-deploy -n grpc-reciprocal-hello -o jsonpath={.items..metadata.name})

k exec "$hello1_pod" -c hello-v1-deploy -n grpc-reciprocal-hello -- \
  ghz --insecure -d '{"name":"eric"}' \
  --proto /opt/hello.proto \
  --call org.feuyeux.grpc.Greeter/SayHello \
  -n 2000 \
  -c 20 \
  -q 500 \
  localhost:7001           
  • -n

    參數用來指定請求數量
  • -c

    參數用來指定并發數量
  • -q

    參數用來指定QPS

測試結果示意如下。

Summary:
  Count:        2000
  Total:        5.60 s
  Slowest:      851.86 ms
  Fastest:      4.88 ms
  Average:      54.51 ms
  Requests/sec: 357.21

Response time histogram:
  4.883 [1]     |
  89.580 [1919] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  174.278 [43]  |∎
  258.976 [3]   |
  343.673 [5]   |
  428.371 [2]   |
  513.068 [4]   |
  597.766 [3]   |
  682.464 [0]   |
  767.161 [7]   |
  851.859 [13]  |

Latency distribution:
  10 % in 24.37 ms 
  25 % in 31.71 ms 
  50 % in 41.98 ms 
  75 % in 53.87 ms 
  90 % in 70.67 ms 
  95 % in 83.83 ms 
  99 % in 683.16 ms 

Status code distribution:
  [OK]   2000 responses   
           

上述結果表示RT/latency基本(1919/2000)落在90ms以内,平均54.51 ms,95%的請求延遲在83.83 ms以内,99 %段的延遲在683.16 ms,存在長尾,需要進一步優化服務網格的配置以及服務源代碼。

trafficPolicy

通過修改hello2的DestinationRule可以随時調整連接配接池配置。我們可以結合基準測試,不斷調整配置找到最佳性能點。示意如下。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: hello2-dr
  namespace: grpc-reciprocal-hello
spec:
  host: hello2-svc
  subsets:
    - labels:
        version: v1
      name: v1
    - labels:
        version: v2
      name: v2
    - labels:
        version: v3
      name: v3
  trafficPolicy:
    connectionPool:
      http:
        http1MaxPendingRequests: 20
        http2MaxRequests: 100
        maxRequestsPerConnection: 10
        maxRetries: 1
      tcp:
        connectTimeout: 10s
        maxConnections: 10           

到此,POD和VM互訪的grpc協定流量管理講述完畢。

本篇示例完整示範了非容器應用網格化過程中的一個比較經典的場景。覆寫到從istio-ingressgateway到deployment、workloadentry等各種CRD的配置,較完整地展示了POD和VM互訪中遇到的各技術點的配置。

接下來,我們在此基礎上,驗證下POD和VM作為同一service的混合流量,如何實作流量轉移。