Ingress gateways in an Alibaba Cloud Service Mesh (ASM) instance support protocol transcoding. This feature allows you to send HTTP requests that use the JSON data format from your browser or client to access gRPC services in an ASM instance. This topic shows you how to use an ingress gateway to access a gRPC service in an ASM instance over HTTP.

Prerequisites

  • The grpc-transcoder tool is installed. The tool is used to automatically generate Envoy filters. For more information, visit grpc-transcoder.
  • Protocol Buffers is installed. For more information, visit Protocol Buffers v3.14.0.

Background information

Envoy is a proxy service that composes the data plane of an ASM instance. Envoy contains various built-in HTTP filter extensions, including the gRPC-JSON transcoder. To enable the gRPC-JSON transcoder, Envoy defines relevant filter protocols. For more information, see gRPC-JSON transcoder. Accordingly, the control plane of an ASM instance must define an Envoy filter to declare the specific phase in which the gRPC-JSON transcoder is enabled. Then, the defined Envoy filter is applied to enable the gRPC-JSON transcoder in the specific phase.

Transcoding process

Ingress gateways in an ASM instance can transcode HTTP/JSON to gRPC. The following figure shows the transcoding process. Transcoding process
Ordinal number Description
1 The control plane of an ASM instance applies the following configurations to an ingress gateway: an Envoy filter that is used for gRPC transcoding and an Istio gateway and virtual service that are used to configure rules to route traffic to a gRPC service port. After the ingress gateway receives the configurations, the ingress gateway immediately loads the configurations for the configurations to take effect.
2 After an HTTP request is received from the browser or client of a user, the ingress gateway matches routing rules. Then, the ingress gateway transcodes the HTTP request to a gRPC request and sends the request to the destination gRPC service in the ASM instance.
3 After a gRPC response is received from the backend service, the ingress gateway transcodes the gRPC response to an HTTP response and returns the HTTP response to the user.

Step 1: Add a transcoding declaration

To create a gRPC service, you must first define a .proto file in Protocol Buffers format. The gRPC service project encapsulates a gRPC API. You must build an image, compile a deployment, and then deploy the gRPC service as a pod to a Container Service for Kubernetes (ACK) cluster by using an ASM instance. gRPC
To enable transcoding from HTTP/JSON to gRPC, you must add the following transcoding declaration to the method definition in the .proto file:
option(google.api.http) = {
  get: "/v1/talk/{data}/{meta}"
};
The .proto file in the hello-servicemesh-rpc repository is used as an example. The following code shows the content of the .proto file to which a transcoding declaration is added. For more information, visit hello-servicemesh-grpc.
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;
}

Step 2: Generate a .proto-descriptor file

Run the following Protoc command in Protocol Buffers to generate the landing.proto-descriptor file from the landing.proto file:
# 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

Step 3: Generate a YAML file for creating an Envoy filter

Enter the following code in the command window on your computer to call the gRPC API. Then, the grpc-transcoder tool is automatically started to generate a YAML file for creating an Envoy filter.
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: the Istio version of the ASM instance.
  • service_port: the port of the gRPC service.
  • service_name: the name of the gRPC service.
  • proto_pkg: the definition of the package name of the .proto file of the gRPC service.
  • proto_svc: the definition of the service name in the .proto file of the gRPC service.
  • descriptor: the path of the .proto-descriptor file.
The following content for creating an Envoy filter is automatically generated after you run the preceding code. Copy the following content to the grpc-transcoder-envoyfilter.yaml file:
#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

Step 4: Create the Envoy filter in the ASM console

  1. Log on to the ASM console.
  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column of the ASM instance.
  4. On the details page of the ASM instance, choose Traffic Management > EnvoyFilter in the left-side navigation pane. On the EnvoyFilter page, click Create.
  5. In the Create panel, select a namespace from the Namespace drop-down list and copy the content of the grpc-transcoder-envoyfilter.yaml file that is edited in Step 3 to the code editor. Then, click OK.

Step 5: Verify the Envoy configuration

Run the following commands in sequence to check whether the dynamic Envoy configuration contains the gRPC-JSON transcoder:
# Obtain the name of the ingress gateway pod.
ingressgateway_pod=$(kubectl get pod -l app="istio-ingressgateway" -n istio-system -o jsonpath='{.items[0].metadata.name}')
# Obtain the timestamp.
timestamp=$(date "+%Y%m%d-%H%M%S")
# Obtain the dynamic Envoy configuration and save the configuration to the dynamic_listeners-"$timestamp".json file.
kubectl -n istio-system exec $ingressgateway_pod \
  -c istio-proxy \
  -- curl -s "http://localhost:15000/config_dump?dynamic_listeners" >dynamic_listeners-"$timestamp".json
# Check whether the configuration contains the gRPC-JSON transcoder.
grep -B3 -A7 GrpcJsonTranscoder dynamic_listeners-"$timestamp".json
If the following content appears in the output, the dynamic Envoy configuration contains the gRPC-JSON transcoder:
{
  "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
    },
    ...

Step 6: Check whether the gRPC service in the ASM instance can be accessed over HTTP

The .proto file defines the request API and response declaration of the gRPC service. When you call the gRPC service by using the request API, the defined response declaration is returned. The following content shows the request API and response declaration that are defined in the .proto file:
  • Request API that is defined in the .proto file:
    rpc talk (TalkRequest) returns (TalkResponse) {
      option(google.api.http) = {
        get: "/v1/talk/{data}/{meta}"
      };
    }
  • Response declaration that is defined in the .proto file:
    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;
    }
Run the following commands to use an ingress gateway to call the gRPC service over HTTP:
# Obtain the IP address of the ingress gateway.
INGRESS_IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# Send an HTTP request to access port 9996 of the ingress gateway. The path is /v1/talk/{data}/{meta}.
curl http://$INGRESS_IP:9996/v1/talk/0/java
The following output is expected:
{
 "status": 200,
 "results": [
  {
   "id": "699882576081691",
   "type": "OK",
   "kv": {
    "data": "Hello",
    "meta": "JAVA",
    "id": "8c175d5c-d8a3-4197-a7f8-6e3e0ab1fe59",
    "idx": "0"
   }
  }
 ]
}

If the return result is as expected after you use an ingress gateway to call the gRPC service over HTTP, the call is successful, and transcoding from HTTP/JSON to gRPC is successful.