gRPC客户端通过调用变量GRPC_SERVER定义的服务grpc-server-svc.grpc-best.svc.cluster.local,将请求均匀地路由到4个编程语言版本的服务端上。本文介绍如何在ACK上部署gRPC服务端的Service,并对Service的负载均衡进行验证。

背景信息

4个编程语言版本的客户端通过调用变量GRPC_SERVER定义的服务grpc-server-svc.grpc-best.svc.cluster.local,当接收到内部请求时,可以均匀地路由到4个编程语言版本的服务端上。与此同时,通过配置Istio中的Ingress Gateway,可以将外部请求按负载均衡策略路由到4个版本的gRPC服务端上。示例

示例工程

您可以单击hello-servicemesh-grpc,直接下载gRPC的示例工程。本文提到的目录均为示例工程下的目录。
说明 本文的镜像仓库仅供参考,请您根据镜像脚本自行构建和推送镜像至自建仓库。关于镜像脚本的具体信息,请参见已下载的gRPC的示例工程。

步骤一:创建gRPC服务端的Service

本系列的示例只有一个命名为grpc-server-svc的gRPC类型的Service。
说明 spec.ports.name的值需要以grpc开头。
  1. 使用以下内容,创建名为grpc-server-svc的YAML文件。
    apiVersion: v1
    kind: Service
    metadata:
      namespace: grpc-best
      name: grpc-server-svc
      labels:
        app: grpc-server-svc
    spec:
      ports:
        - port: 9996
          name: grpc-port
      selector:
        app: grpc-server-deploy
  2. 执行以下命令,创建Service。
    kubectl apply -f grpc-server-svc.yaml

步骤二:创建gRPC服务端的Deployment

完整的Deployment请参见kube/deployment。下文以Node.js语言的gRPC服务端的Deployment文件grpc-server-node.yaml为例,创建gRPC服务端的Deployment。
说明 服务端的4个Deployment都需要定义app标签的值为grpc-server-deploy,以匹配gRPC服务端Service的Selector。同时每种语言的version标签需要保证各不相同。
  1. 使用以下内容,创建名为grpc-server-node的YAML文件。
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: grpc-best
      name: grpc-server-node
      labels:
        app: grpc-server-deploy
        version: v3
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: grpc-server-deploy
          version: v3
      template:
        metadata:
          labels:
            app: grpc-server-deploy
            version: v3
        spec:
          containers:
            - name: grpc-server-deploy
              image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/asm-grpc-server-node:1.0.0
              imagePullPolicy: Always
              ports:
                - containerPort: 9996
                  name: grpc-port
  2. 执行以下命令,创建Depolyment。
    kubectl apply -f grpc-server-node.yaml

步骤三:创建gRPC客户端的Deployment

客户端和服务端有以下两处不同。
  • 服务端启动后会持续运行,而客户端完成请求后就会结束进程,因此,需要实现一种死循环的方式保持客户端容器不退出。
  • 客户端需要定义变量GRPC_SERVER的值,在客户端容器启动时传递给gRPC客户端。

下文以Go语言的gRPC客户端的Deployment文件grpc-client-go.yaml为例,创建gRPC客户端的Deployment。

  1. 使用以下内容,创建名grpc-client-go的YAML文件。
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: grpc-best
      name: grpc-client-go
      labels:
        app: grpc-client-go
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: grpc-client-go
      template:
        metadata:
          labels:
            app: grpc-client-go
        spec:
          containers:
            - name: grpc-client-go
              image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/asm-grpc-client-go:1.0.0
              command: ["/bin/sleep", "3650d"]
              env:
                - name: GRPC_SERVER
                  value: "grpc-server-svc.grpc-best.svc.cluster.local"
              imagePullPolicy: Always
  2. 执行以下命令,创建Depolyment。
    kubectl apply -f grpc-client-go.yaml

其中,command: ["/bin/sleep", "3650d"]是定义Go语言的gRPC客户端启动后执行的命令。通过Sleep的方式保持客户端容器运行。env中定义了GRPC_SERVER变量,其值为grpc-server-svc.grpc-best.svc.cluster.local

步骤四:部署服务和应用

  1. 执行以下命令,在ACK实例中,创建名为grpc-best的命名空间。
    alias k="kubectl --kubeconfig $USER_CONFIG"
    k create ns grpc-best
  2. 执行以下命令,为命名空间启用自动注入Sidecar。
    k label ns grpc-best istio-injection=enabled
  3. 执行以下命令,部署1个Service和8个Deployment。
    kubectl apply -f grpc-svc.yaml
    kubectl apply -f deployment/grpc-server-java.yaml
    kubectl apply -f deployment/grpc-server-python.yaml
    kubectl apply -f deployment/grpc-server-go.yaml
    kubectl apply -f deployment/grpc-server-node.yaml
    kubectl apply -f deployment/grpc-client-java.yaml
    kubectl apply -f deployment/grpc-client-python.yaml
    kubectl apply -f deployment/grpc-client-go.yaml
    kubectl apply -f deployment/grpc-client-node.yaml

结果验证

从Pod侧验证gRPC服务的负载均衡

从客户端容器请求gRPC服务端的Service,验证gRPC服务端Service的负载均衡。

  1. 执行以下命令,获取4个客户端容器的名称。
    client_java_pod=$(k get pod -l app=grpc-client-java -n grpc-best -o jsonpath={.items..metadata.name})
    client_go_pod=$(k get pod -l app=grpc-client-go -n grpc-best -o jsonpath={.items..metadata.name})
    client_node_pod=$(k get pod -l app=grpc-client-node -n grpc-best -o jsonpath={.items..metadata.name})
    client_python_pod=$(k get pod -l app=grpc-client-python -n grpc-best -o jsonpath={.items..metadata.name})
  2. 执行以下命令,在客户端容器中,对4个gRPC服务端的Service发起请求。
    k exec "$client_java_pod" -c grpc-client-java -n grpc-best -- java -jar /grpc-client.jar
    k exec "$client_go_pod" -c grpc-client-go -n grpc-best -- ./grpc-client
    k exec "$client_node_pod" -c grpc-client-node -n grpc-best -- node proto_client.js
    k exec "$client_python_pod" -c grpc-client-python -n grpc-best -- sh /grpc-client/start_client.sh
  3. 以Node.js客户端为例,执行以下命令,通过一个循环,验证gRPC服务端Service的负载均衡。
    for ((i = 1; i <= 100; i++)); do
    kubectl exec "$client_node_pod" -c grpc-client-node -n grpc-best -- node kube_client.js > kube_result
    done
    sort kube_result | grep -v "^[[:space:]]*$" | uniq -c | sort -nrk1
    预期输出:
      26 Talk:PYTHON
      25 Talk:NODEJS
      25 Talk:GOLANG
      24 Talk:JAVA
    结果显示4个版本的gRPC服务端Service收到相近的请求数。说明ASM收到外部请求时,可以将外部请求按负载均衡策略路由到4个版本的gRPC服务端Service上。

从本地验证gRPC服务的负载均衡

从本地请求Istio中的Ingress Gateway,验证gRPC服务端Service的负载均衡。

  1. 创建入口网关,暴露网关的9996端口。
    具体操作,请参见创建入口网关服务
  2. 使用以下YAML,创建网关规则。
    具体操作,请参见管理网关规则
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      namespace: grpc-best
      name: grpc-gateway
    spec:
      selector:
        istio: ingressgateway
      servers:
        - port:
            number: 9996
            name: grpc
            protocol: GRPC
          hosts:
            - "*"
  3. 使用以下YAML,创建虚拟服务。
    具体操作,请参见管理虚拟服务
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      namespace: grpc-best
      name: grpc-vs
    spec:
      hosts:
      - "*"
      gateways:
      - grpc-gateway
      http:
      - route:
        - destination:
            host: grpc-server-svc.grpc-best.svc.cluster.local
            port:
              number: 9996
  4. 获取Ingress Gateway的IP。
    INGRESS_IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  5. 验证gRPC服务端Service的负载均衡。
    docker run -d --name grpc_client_node -e GRPC_SERVER="${INGRESS_IP}" registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/asm-grpc-client-node:1.0.0 /bin/sleep 3650d
    client_node_container=$(docker ps -q)
    
    docker exec -e GRPC_SERVER="${INGRESS_IP}" -it "$client_node_container" node kube_client.js
    
    for ((i = 1; i <= 100; i++)); do
    docker exec -e GRPC_SERVER="${INGRESS_IP}" -it "$client_node_container" node kube_client.js >> kube_result
    done
    sort kube_result | grep -v "^[[:space:]]*$" | uniq -c | sort -nrk1
    预期输出:
      26 Talk:PYTHON
      25 Talk:NODEJS
      25 Talk:GOLANG
      24 Talk:JAVA
    结果显示4个版本的gRPC服务端Service收到相近的请求数。说明ASM收到外部请求时,可以将外部请求按负载均衡策略路由到4个版本的gRPC服务端Service上。