全部产品
Search
文档中心

服务网格 ASM:使用ASM为网格内gRPC服务实现负载均衡

更新时间:Jun 05, 2023

在使用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服务间调用的负载不均衡问题。

前提条件:

操作步骤:

  1. 登录容器服务管理控制台

  2. 在控制台左侧导航栏,单击集群
  3. 集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏,单击命名空间与配额
  5. 命名空间页面单击右上方的创建,在创建命名空间对话框,输入命名空间的名称,例如grpc-nosidecar,单击确定

  6. 在命名空间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
    ---
  7. 在命名空间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
    ---
  8. 执行以下命令,查看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
  9. 执行以下命令,登录到客户端Pod容器。

    kubectl exec -it -n grpc-nosidecar istio-grpc-client-dd56bcb45-hvmjt sh
  10. 进入容器后,执行以下命令:

    /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实现负载均衡。

前提条件:

操作步骤:

  1. 登录容器服务管理控制台

  2. 在控制台左侧导航栏,单击集群
  3. 集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏,单击命名空间与配额
  5. 命名空间页面单击右上方的创建,在创建命名空间对话框,输入命名空间的名称,例如grpc-sidecar,并新增标签istio-injection:enabled,单击确定

  6. 命名空间grpc-sidecar下部署gRPC服务的服务端istio-grpc-server。

    假设,待创建的描述文件为istio-grpc-server.yaml,请执行如下命令:

    kubectl apply -n grpc-sidecar -fistio-grpc-server.yaml

    istio-grpc-server.yaml文件的内容,参见上文示例。

  7. 命名空间grpc-sidecar下部署gRPC服务的客户端istio-grpc-client。

    假设,待创建的描述文件为istio-grpc-client.yaml,请执行如下命令:

    kubectl apply -n grpc-sidecar -fistio-grpc-client.yaml

    istio-grpc-client.yaml文件的内容,参见上文示例。

  8. 执行以下命令,查看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
  9. 执行以下命令,登录到客户端Pod容器。

    kubectl exec -it -n grpc-nosidecar istio-grpc-client-695f5fc66f-2brmv -c istio-grpc-client sh
  10. 进入容器后,执行以下命令:

    /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
  11. 配置服务网格ASM实例的控制平面。

    1. 登录ASM控制台

    2. 在左侧导航栏,选择服务网格 > 网格管理

    3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理

    4. 在网格详情页面左侧导航栏,选择网格实例 > 全局命名空间,然后在右侧页面,单击新建

    5. 新建命名空间对话框设置命名空间名称为grpc-sidecar,单击确定

    6. 使用以下内容,创建目标规则。具体操作,请参见管理目标规则

      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"
    7. 使用以下内容创建虚拟服务,具体操作,请参见管理虚拟服务

      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
    8. 执行以下命令,登录到客户端Pod容器。

      kubectl exec -it -n grpc-nosidecar istio-grpc-client-695f5fc66f-2brmv -c istio-grpc-client sh
    9. 进入容器后,执行以下命令:

      /bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100

      可以看到所有的请求分别指向了对应的2个Pod,但比例接近于90:10,并非默认的Round-Robin。

    10. 在网格详情页面左侧导航栏单击虚拟服务,找到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
    11. 通过Kubectl登录容器,执行以下命令:

      /bin/greeter-client --insecure=true --address=istio-grpc-server:8080 --repeat=100

      可以看到所有的请求分别指向了对应的1个Pod,即版本v2对应的Pod。