美文网首页
入口网关支持HTTP请求ServiceMesh内的GRPC服务实

入口网关支持HTTP请求ServiceMesh内的GRPC服务实

作者: 六爷1_1 | 来源:发表于2021-03-01 11:25 被阅读0次

    概述

    本文将讲述ServiceMesh的入口网关(ingressgateway)支持协议转码的能力,以支持使用HTTP/JSON访问服务网格内的gRPC服务。

    image.png
    1. ServiceMesh控制平面下发用于grpc转码的EnvoyFilter、用于路由到gRPC服务端口的规则配置Gateway和VirtualService到ASM入口网关,入口网关接收后即时加载生效。
    2. 入口网关收到用户或其客户端http协议的请求后,将进行路由规则匹配和协议转换,然后以grpc协议请求服务网格内的gRPC服务。
    3. 入口网关收到后端服务的grpc响应,再将其转换为http的响应返回给请求方。

    原理和目标

    Envoy作为ServiceMesh数据平面的proxy组件,内置了多种http扩展过滤器,本文涉及的是其中的http到grpc的转码器。为了启用这个过滤器,Envoy定义了相应的过滤器协议config.filter.http.transcoder.v2.GrpcJsonTranscoder。为此,ServiceMesh的控制平面需要定义一个EnvoyFilter来声明在什么地方哪个阶段启用这个过滤器,然后下发这个EnvoyFilter在指定环节启用转码器。另一方面,这个转码器需要知道grpc服务的具体协议,包括描述grpc服务的Proto Descriptors文件、grpc服务全名列表。

    Envoy是ServiceMesh入口网关的核心组件。因此,实现入口网关转码的关键任务就是生成这个用于grpc转码的EnvoyFilter。

    由于EnvoyFilter的定义较为复杂,从头写一个这样的EnvoyFilter比较费时费力且容易出错,本文提供了自动化生成EnvoyFilter的工具grpc-transcoder(https://github.com/AliyunContainerService/grpc-transcoder),一方面保证EnvoyFilter的定义正确,另一方面可以节省你的时间以及省去编写EnvoyFilter的痛苦。

    实战

    1 gRPC服务

    如下图所示,通常用户的gRPC服务是从protobuf格式的gRPC服务协议文件.proto的定义开始的。grpc-service-project封装grpc接口并完成实现,后续步骤包括构建镜像,编写Deployment,最终通过ASM,将grpc服务以POD的形式部署到ACK集群。

    image.png

    为了支持http转码grpc,需要确认.proto中是否定义了支持转码的声明。这里以hello-servicemesh-grpc示例中的proto为例,示意如下。

    import "google/api/annotations.proto";
    service LandingService {
      //Unary RPC
      rpc talk (TalkRequest) returns (TalkResponse) {
        option(google.api.http) = {
          get: "/v1/talk/{data}/{meta}"
        };
      }
    ...
    }
    
    message TalkRequest {
      string data = 1;
      string meta = 2;
    }
    

    其中,5-7行是为了支持转码新增的声明,第1行是引入相应能力的.proto声明。如果你的.proto中没有相关声明,请补充。这里需要提示的是,补充这个.proto只是为了生成pb(Proto Descriptors)文件,补充后并不影响grpc-service-project及后续步骤。

    2 生成Proto Descriptors文件

    这里以hello-servicemesh-grpc为例,使用protoc命令从landing.proto生成landing.proto-descriptor文件。如果没有安装protoc请在此处下载。

    # https://github.com/AliyunContainerService/hello-servicemesh-grpc
    proto_path={path/to/hello-servicemesh-grpc}/proto
    # https://github.com/grpc-ecosystem/grpc-gateway/tree/master/third_party/
    proto_dep_path={path/to/third_party}
    protoc \
        --proto_path=${proto_path} \
        --proto_path=${proto_dep_path} \
        --include_imports \
        --include_source_info \
        --descriptor_set_out=landing.proto-descriptor \
        "${proto_path}"/landing.proto
    

    3 生成EnvoyFilter

    此时,我们已经有了Proto Descriptors文件landing.proto-descriptor。接下来,通过grpc-transcoder生成EnvoyFilter。调用接口的示意请求如下。

    grpc-transcoder \
    --version 1.7 \
    --service_port 9996 \
    --service_name grpc-server-svc \
    --proto_pkg org.feuyeux.grpc \
    --proto_svc LandingService \
    --descriptor landing.proto-descriptor
    

    参数说明:

    • version:由于ASM是托管集群,因此需要保证版本升级对EnvoyFilter的影响。因此这个参数是必选值。
    • service_port:对应的grpc服务(详见下图)端口。
    • service_name:对应的grpc服务(详见下图)名称。
    • proto_pkg:对应的grpc服务.proto中包名的定义。
    • proto_svc:对应的grpc服务.proto中服务名的定义。
    • descriptor:Proto Descriptors文件本地路径。

    使用上述请求自动生成的EnvoyFilter(grpc-transcoder-envoyfilter.yaml)如下。

    #Generated by ASM(http://servicemesh.console.aliyun.com)
    #GRPC Transcoder EnvoyFilter[1.7]
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: grpc-transcoder-grpc-server-svc
    spec:
      workloadSelector:
        labels:
          app: istio-ingressgateway
      configPatches:
        - applyTo: HTTP_FILTER
          match:
            context: GATEWAY
            listener:
              portNumber: 9996
              filterChain:
                filter:
                  name: "envoy.filters.network.http_connection_manager"
                  subFilter:
                    name: "envoy.filters.http.router"
            proxy:
              proxyVersion: ^1\.7.*
          patch:
            operation: INSERT_BEFORE
            value:
              name: envoy.grpc_json_transcoder
              typed_config:
                '@type': type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
                proto_descriptor_bin: Ctl4ChVnb29nbGUvYXBpL2h0dHAucHJ...
                services: 
                - org.feuyeux.grpc.LandingService
                print_options:
                  add_whitespace: true
                  always_print_primitive_fields: true
                  always_print_enums_as_ints: false
                  preserve_proto_field_names: false
    

    4 入口网关启用转码器

    通过工具生成EnvoyFilter后,我们通过执行命令kubectl --kubeconfig mesh_config -f grpc-transcoder-envoyfilter.yaml -n istio-system,使转码器即时生效。到此,配置的步骤就完成了,完整的流程示意图如下。

    image.png

    5 envoy配置校验

    接下来我们校验入口网关的envoy配置是否生效。依次执行如下命令,验证envoy动态配置中是否包含解码器GrpcJsonTranscoder的配置:

    #获取入口网关POD名称
    ingressgateway_pod=$(kubectl get pod -l app="istio-ingressgateway" -n istio-system -o jsonpath='{.items[0].metadata.name}')
    #时间戳
    timestamp=$(date "+%Y%m%d-%H%M%S")
    #获取envoy动态配置并保存到dynamic_listeners-"$timestamp".json
    kubectl -n istio-system exec $ingressgateway_pod \
      -c istio-proxy \
      -- curl -s "http://localhost:15000/config_dump?resource=dynamic_listeners" >dynamic_listeners-"$timestamp".json
    #确认配置中存在GrpcJsonTranscoder
    grep -B3 -A7 GrpcJsonTranscoder dynamic_listeners-"$timestamp".json
    

    envoy动态配置中应包含类似如下的配置:

    {
      "name": "envoy.grpc_json_transcoder",
      "typed_config": {
        "@type": "type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder",
        "services": [
          "org.feuyeux.grpc.LandingService"
        ],
        "print_options": {
          "add_whitespace": true,
          "always_print_primitive_fields": true
        },
        ...
    

    6 功能校验

    最后,我们进行功能校验,验证HTTP请求网格内GRPC服务。

    我们再次给出grpc服务接口的定义:

    rpc talk (TalkRequest) returns (TalkResponse) {
      option(google.api.http) = {
        get: "/v1/talk/{data}/{meta}"
      };
    }
    

    为了验证响应信息,这里给出.proto中定义的响应声明:

    message TalkResponse {
      int32 status = 1;
      repeated TalkResult results = 2;
    }
    
    message TalkResult {
      //timestamp
      int64 id = 1;
      //enum
      ResultType type = 2;
      // id:result uuid
      // idx:language index
      // data: hello
      // meta: serverside language
      map<string, string> kv = 3;
    }
    
    enum ResultType {
      OK = 0;
      FAIL = 1;
    }
    

    依次执行如下命令,验证这个grpc服务接口可以通过HTTP请求入口网关实现调用:

    #获取入口网关IP
    INGRESS_IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    #http请求入口网关的9996端口,/v1/talk/{data}/{meta}路径
    curl http://$INGRESS_IP:9996/v1/talk/0/java
    

    我们应该得到如下响应:

    {
     "status": 200,
     "results": [
      {
       "id": "699882576081691",
       "type": "OK",
       "kv": {
        "data": "Hello",
        "meta": "JAVA",
        "id": "8c175d5c-d8a3-4197-a7f8-6e3e0ab1fe59",
        "idx": "0"
       }
      }
     ]
    }
    

    相关文章

      网友评论

          本文标题:入口网关支持HTTP请求ServiceMesh内的GRPC服务实

          本文链接:https://www.haomeiwen.com/subject/zgaofltx.html