在使用gRPC(基于HTTP/2)的Kubernetes服務時,到目標的單個連接將在一個Pod處終止。如果從客戶端發送了多條消息,則所有消息將由該Pod處理,從而導致負載不均衡。本文通過示例介紹gRPC服務間負載不均衡的問題以及如何實現負載均衡。
背景信息
gRPC是一種基于HTTP/2的服務通信協議,使用基于Protocol Buffers(簡稱為PB)格式的服務定義。服務之間調用的數據可以被序列化為較小的二進制格式進行傳輸。使用gRPC,可以從.proto文件生成多種語言的代碼,這也就使得gRPC成為了多語言微服務開發的最佳選擇之一。
使用基于HTTP/1.1的RPC時,一個簡單的TCP負載均衡器足以勝任,因為這些連接都是短暫的,客戶端將嘗試重新連接,不會保持與運行中的舊Pod之間的連接。但是使用基于HTTP/2的gRPC時,TCP連接保持打開狀態,這樣將保持連接到即將失效的Pod,亦或使集群失去平衡。
gRPC服務間調用的負載不均衡
通過以下示例可以看出,在Kubernetes下gRPC服務間調用的負載不均衡問題。
前提條件
已創建至少一個Kubernetes集群。具體操作,請參見創建Kubernetes托管版集群。
已創建grpc-nosidecar命名空間。具體操作,請參見創建命名空間。
已通過kubectl連接集群,詳情參見獲取集群KubeConfig并通過kubectl工具連接集群。
操作步驟
在grpc-nosidecar命名空間下,部署gRPC服務的服務端istio-grpc-server。
使用以下內容,創建istio-grpc-server.yaml。
apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-server-v1 labels: app: istio-grpc-server version: v1 spec: replicas: 1 selector: matchLabels: app: istio-grpc-server version: v1 template: metadata: labels: app: istio-grpc-server version: v1 spec: containers: - args: - --address=0.0.0.0:8080 image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-server imagePullPolicy: Always livenessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 name: istio-grpc-server ports: - containerPort: 8080 readinessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 --- apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-server-v2 labels: app: istio-grpc-server version: v2 spec: replicas: 1 selector: matchLabels: app: istio-grpc-server version: v2 template: metadata: labels: app: istio-grpc-server version: v2 spec: containers: - args: - --address=0.0.0.0:8080 image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-server imagePullPolicy: Always livenessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 name: istio-grpc-server ports: - containerPort: 8080 readinessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 --- apiVersion: v1 kind: Service metadata: name: istio-grpc-server labels: app: istio-grpc-server spec: ports: - name: grpc-backend port: 8080 protocol: TCP selector: app: istio-grpc-server type: ClusterIP ---
執行以下命令,部署istio-grpc-server。
kubectl apply -n grpc-nosidecar -f istio-grpc-server.yaml
在grpc-nosidecar命名空間下,部署gRPC服務的客戶端istio-grpc-client。
使用以下內容,創建istio-grpc-client.yaml。
apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-client labels: app: istio-grpc-client spec: replicas: 1 selector: matchLabels: app: istio-grpc-client template: metadata: labels: app: istio-grpc-client spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-client imagePullPolicy: Always command: ["/bin/sleep", "3650d"] name: istio-grpc-client --- apiVersion: v1 kind: Service metadata: name: istio-grpc-client spec: ports: - name: grpc port: 8080 protocol: TCP selector: app: istio-grpc-client type: ClusterIP ---
執行以下命令,部署istio-grpc-client。
kubectl apply -n grpc-nosidecar -f istio-grpc-client.yaml
執行以下命令,查看Pod運行狀態。
kubectl get pod -n grpc-nosidecar
預期輸出:
NAME READY STATUS RESTARTS AGE istio-grpc-client-dd56bcb45-hvmjt 1/1 Running 0 95m istio-grpc-server-v1-546d9876c4-j2p9r 1/1 Running 0 95m istio-grpc-server-v2-66d9b8847-276bd 1/1 Running 0 95m
執行以下命令,登錄到客戶端Pod容器。
kubectl exec -it -n grpc-nosidecar istio-grpc-client-dd56bcb45-hvmjt sh
進入容器后,執行以下命令。
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
預期輸出:
2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2024/02/07 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd
可以看到所有的請求都指向了一個Pod。由此可見,從客戶端發送了所有消息都由一個Pod處理,從而導致負載不均衡。
使用ASM實現負載均衡
下面將示例說明如何通過ASM實現負載均衡。
前提條件
已創建至少一個服務網格ASM實例,并已經添加至少一個集群到該實例中。具體操作,請參見添加集群到ASM實例。
已創建grpc-sidecar命名空間,并添加
istio-injection:enabled
標簽。具體操作,請參見創建命名空間。已設置通過kubectl連接到該集群,詳情參見獲取集群KubeConfig并通過kubectl工具連接集群。
操作步驟
在grpc-sidecar命名空間下,部署gRPC服務的服務端istio-grpc-server。
使用以下內容,創建istio-grpc-server.yaml。
apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-server-v1 labels: app: istio-grpc-server version: v1 spec: replicas: 1 selector: matchLabels: app: istio-grpc-server version: v1 template: metadata: labels: app: istio-grpc-server version: v1 spec: containers: - args: - --address=0.0.0.0:8080 image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-server imagePullPolicy: Always livenessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 name: istio-grpc-server ports: - containerPort: 8080 readinessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 --- apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-server-v2 labels: app: istio-grpc-server version: v2 spec: replicas: 1 selector: matchLabels: app: istio-grpc-server version: v2 template: metadata: labels: app: istio-grpc-server version: v2 spec: containers: - args: - --address=0.0.0.0:8080 image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-server imagePullPolicy: Always livenessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 name: istio-grpc-server ports: - containerPort: 8080 readinessProbe: exec: command: - /bin/grpc_health_probe - -addr=:8080 initialDelaySeconds: 2 --- apiVersion: v1 kind: Service metadata: name: istio-grpc-server labels: app: istio-grpc-server spec: ports: - name: grpc-backend port: 8080 protocol: TCP selector: app: istio-grpc-server type: ClusterIP ---
執行以下命令,部署istio-grpc-server。
kubectl apply -n grpc-sidecar -f istio-grpc-server.yaml
在grpc-sidecar命名空間下,部署gRPC服務的客戶端istio-grpc-client。
使用以下內容,創建istio-grpc-client.yaml。
apiVersion: apps/v1 kind: Deployment metadata: name: istio-grpc-client labels: app: istio-grpc-client spec: replicas: 1 selector: matchLabels: app: istio-grpc-client template: metadata: labels: app: istio-grpc-client spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-client imagePullPolicy: Always command: ["/bin/sleep", "3650d"] name: istio-grpc-client --- apiVersion: v1 kind: Service metadata: name: istio-grpc-client spec: ports: - name: grpc port: 8080 protocol: TCP selector: app: istio-grpc-client type: ClusterIP ---
執行以下命令,部署istio-grpc-client。
kubectl apply -n grpc-sidecar -f istio-grpc-client.yaml
執行以下命令,查看Pod運行狀態。
kubectl get pod -n grpc-sidecar
預期輸出:
NAME READY STATUS RESTARTS AGE istio-grpc-client-dd56bcb45-zhfsg 2/2 Running 0 1h15m istio-grpc-server-v1-546d9876c4-tndsm 2/2 Running 0 1h15m istio-grpc-server-v2-66d9b8847-99v62 2/2 Running 0 1h15m
可以看到每個Pod中包含了2個容器,其中一個容器就是注入的Sidecar代理。
執行以下命令,登錄到客戶端Pod容器。
kubectl exec -it -n grpc-sidecar istio-grpc-client-dd56bcb45-zhfsg -c istio-grpc-client sh
進入容器后,執行以下命令。
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
預期輸出:
2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62
可以看到所有的請求分別指向了對應的2個Pod,比例接近于50:50,即負載均衡中的Round-Robin。
配置服務網格ASM實例的控制平面。
在ASM實例中創建grpc-sidecar命名空間。具體操作,請參見新建命名空間。
使用以下內容,在grpc-sidecar命名空間創建目標規則。具體操作,請參見管理目標規則。
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: dr-istio-grpc-server spec: host: istio-grpc-server trafficPolicy: loadBalancer: simple: ROUND_ROBIN subsets: - name: v1 labels: version: "v1" - name: v2 labels: version: "v2"
使用以下內容,在grpc-sidecar命名空間創建虛擬服務。具體操作,請參見管理虛擬服務。
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: vs-istio-grpc-server spec: hosts: - "istio-grpc-server" http: - match: - port: 8080 route: - destination: host: istio-grpc-server subset: v1 weight: 90 - destination: host: istio-grpc-server subset: v2 weight: 10
執行以下命令,登錄到客戶端Pod容器。
kubectl exec -it -n grpc-sidecar istio-grpc-client-dd56bcb45-zhfsg -c istio-grpc-client sh
進入容器后,執行以下命令。
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
預期輸出:
2024/02/07 14:56:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2024/02/07 14:56:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm
可以看到所有的請求分別指向了對應的2個Pod,但比例接近于90:10,并非默認的Round-Robin。
調整虛擬服務的權重,查看調用的不同結果。
登錄ASM控制臺,在左側導航欄,選擇 。
在網格管理頁面,單擊目標實例名稱,然后在左側導航欄,選擇 。
在虛擬服務頁面上方,選擇grpc-sidecar命名空間,在操作列,單擊vs-istio-grpc-server對應的查看YAML,在編輯對話框中,調整虛擬服務的權重,然后單擊確認。
修改內容如下:
route: - destination: host: istio-grpc-server subset: v1 weight: 0 - destination: host: istio-grpc-server subset: v2 weight: 100
執行以下命令,登錄到客戶端Pod容器。
kubectl exec -it -n grpc-sidecar istio-grpc-client-dd56bcb45-zhfsg -c istio-grpc-client sh
進入容器后,執行以下命令。
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
預期輸出:
2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2024/02/07 14:58:30 Hello world from istio-grpc-server-v2-66d9b8847-99v62
可以看到所有的請求都指向了v2版本對應的Pod。