在使用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集群。
已设置通过kubectl连接该集群,详情参见获取集群KubeConfig并通过kubectl工具连接集群。
操作步骤:
登录容器服务管理控制台。
- 在控制台左侧导航栏,单击集群。
- 在集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情。
- 在集群管理页左侧导航栏,单击命名空间与配额。
在命名空间页面单击右上方的创建,在创建命名空间对话框,输入命名空间的名称,例如grpc-nosidecar,单击确定。
在命名空间grpc-nosidecar下部署gRPC服务的服务端istio-grpc-server。
假设,待创建的描述文件为istio-grpc-server.yaml,请执行如下命令:
kubectl apply -n grpc-nosidecar -fistio-grpc-server.yaml
其中,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 ---
在命名空间grpc-nosidecar下部署gRPC服务的客户端istio-grpc-client。
假设,待创建的描述文件为istio-grpc-client.yaml,请执行如下命令:
kubectl apply -n grpc-nosidecar -fistio-grpc-client.yaml
其中,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 ---
执行以下命令,查看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
可以看到所有的请求都指向了一个Pod:
2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd 2020/01/14 14:37:14 Hello world from istio-grpc-server-v2-66d9b8847-276bd
由此可见,从客户端发送了所有消息都由一个Pod处理,从而导致负载不均衡。
使用ASM实现负载均衡
下面将示例说明如何通过ASM实现负载均衡。
前提条件:
已创建至少一个服务网格ASM实例,并已经添加至少一个集群到该实例中。
已设置通过kubectl连接到该集群,详情参见获取集群KubeConfig并通过kubectl工具连接集群。
操作步骤:
登录容器服务管理控制台。
- 在控制台左侧导航栏,单击集群。
- 在集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情。
- 在集群管理页左侧导航栏,单击命名空间与配额。
在命名空间页面单击右上方的创建,在创建命名空间对话框,输入命名空间的名称,例如grpc-sidecar,并新增标签
istio-injection:enabled
,单击确定。命名空间grpc-sidecar下部署gRPC服务的服务端istio-grpc-server。
假设,待创建的描述文件为istio-grpc-server.yaml,请执行如下命令:
kubectl apply -n grpc-sidecar -fistio-grpc-server.yaml
istio-grpc-server.yaml文件的内容,参见上文示例。
命名空间grpc-sidecar下部署gRPC服务的客户端istio-grpc-client。
假设,待创建的描述文件为istio-grpc-client.yaml,请执行如下命令:
kubectl apply -n grpc-sidecar -fistio-grpc-client.yaml
istio-grpc-client.yaml文件的内容,参见上文示例。
执行以下命令,查看Pod运行状态:
kubectl get pod -n grpc-sidecar
此时可以看到每个Pod中包含了2个容器,其中一个容器就是注入的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容器。
kubectl exec -it -n grpc-nosidecar istio-grpc-client-695f5fc66f-2brmv -c istio-grpc-client sh
进入容器后,执行以下命令:
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
可以看到所有的请求分别指向了对应的2个Pod,比例接近于50:50,即负载均衡中的Round-Robin。
2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62 2020/01/14 14:53:16 Hello world from istio-grpc-server-v1-546d9876c4-tndsm 2020/01/14 14:53:16 Hello world from istio-grpc-server-v2-66d9b8847-99v62
配置服务网格ASM实例的控制平面。
登录ASM控制台。
在左侧导航栏,选择 。
在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
在网格详情页面左侧导航栏,选择 ,然后在右侧页面,单击新建。
在新建命名空间对话框设置命名空间名称为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"
使用以下内容创建虚拟服务,具体操作,请参见管理虚拟服务。
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-nosidecar istio-grpc-client-695f5fc66f-2brmv -c istio-grpc-client sh
进入容器后,执行以下命令:
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
可以看到所有的请求分别指向了对应的2个Pod,但比例接近于90:10,并非默认的Round-Robin。
在网格详情页面左侧导航栏单击虚拟服务,找到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
通过Kubectl登录容器,执行以下命令:
/bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100
可以看到所有的请求分别指向了对应的1个Pod,即版本v2对应的Pod。