Notice:
This is the "latest" release of Envoy Gateway, which contains the most recent commits from the main branch.
This release might not be stable.
Please refer to the /docs documentation for the most current information.

gRPC-JSON Transcoding

This task shows how to use HTTPRouteFilter to convert JSON/HTTP requests to gRPC requests, so REST clients can call a gRPC backend.

The JSON/HTTP API is defined by the google.api.http options in your proto files.

The filter can only be used in the rule filters of an HTTPRoute. It is not supported in a GRPCRoute or in the filters of a backendRef.

Prerequisites

Follow the steps below to install Envoy Gateway and the example manifest. Before proceeding, you should be able to query the example backend using HTTP.

Expand for instructions
  1. Install the Gateway API CRDs and Envoy Gateway using Helm:

    helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace
    
  2. Install the GatewayClass, Gateway, HTTPRoute and example app:

    kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default
    
  3. Verify Connectivity:

    Get the External IP of the Gateway:

    export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get
       

    The above command should succeed with status code 200.

    Get the name of the Envoy service created the by the example Gateway:

    export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Get the deployment of the Envoy service created the by the example Gateway:

    export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Port forward to the Envoy service:

    kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 &
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://localhost:8888/get
       

    The above command should succeed with status code 200.

Generate the Proto Descriptor

The transcoder needs the protobuf descriptor set of your services. Generate it with --include_imports, so the imported proto files are included:

protoc --include_imports --descriptor_set_out=proto-descriptor.pb path/to/your.proto

Store it in a ConfigMap in the same namespace as the HTTPRoute:

kubectl create configmap greeter-proto-descriptor --from-file=proto-descriptor=proto-descriptor.pb

The descriptor set is read from the proto-descriptor key, either in binaryData or in data, where it can be base64-encoded. If the ConfigMap has no data and only one binaryData entry, that entry is used.

Backend

The transcoded request is a gRPC request, so Envoy Gateway connects to the backend over HTTP/2, like the backend of a GRPCRoute. No appProtocol is needed. A backend with a WebSocket app protocol, like appProtocol: kubernetes.io/ws, can’t be used, because it needs HTTP/1.1.

Note: don’t set useClientProtocol in a BackendTrafficPolicy for the route. The client of the transcoder uses HTTP/1.1 most of the time, so Envoy would send the gRPC requests over HTTP/1.1 and they would fail.

Configuration

Create an HTTPRouteFilter that references the ConfigMap, and use it in the HTTPRoute rule:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: HTTPRouteFilter
metadata:
  name: grpc-transcoder
spec:
  grpcJSONTranscoder:
    protoDescriptor:
      valueRef:
        group: ""
        kind: ConfigMap
        name: greeter-proto-descriptor
    services:
    - example.Greeter
    printOptions:
      alwaysPrintPrimitiveFields: true
    ignoreUnknownQueryParameters: true
    convertGRPCStatus: true
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: grpc-route
spec:
  parentRefs:
  - name: eg
  hostnames:
  - grpc.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1/hello
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.envoyproxy.io
        kind: HTTPRouteFilter
        name: grpc-transcoder
    backendRefs:
    - name: grpc-service
      port: 9000

Then call the JSON API:

curl -H "Host: grpc.example.com" http://${GATEWAY_HOST}/v1/hello/world

If services is not specified, all the services in the descriptor set are transcoded, except the ones that come from imported files.

Policies and Routing

The transcoder changes the path of the request to the gRPC method, for example /example.Greeter/SayHello, but the request is not matched to another route. The backend and the policies of the HTTPRoute are used for the transcoded request, so attach the policies, like authentication and rate limiting, to the HTTPRoute. The policies of a GRPCRoute for the same service are not applied.

Note: if EnvoyProxy.spec.filterOrder moves a filter that clears the route cache after the transcoder, like ExtAuth or JWT with recomputeRoute, the transcoded request may be matched to another route.

The following filters can’t be used in the same rule with the transcoder, because they use the path of the request:

  • URLRewrite with a path modifier
  • URLRewrite with a PathRegex hostname
  • RequestRedirect
  • DirectResponse

Such a rule is not accepted and returns 500.

Note: a RequestMirror in the same rule sends the transcoded gRPC request to the mirror backend, over HTTP/2.

Troubleshooting

The descriptor set is validated when the route is translated. If it is invalid, the rule returns 500 and the reason is in the Accepted condition of the HTTPRoute:

kubectl get httproute grpc-route -o yaml

These problems are reported:

  • The descriptor set can’t be parsed, or an imported file is missing.
  • An imported file comes after the file that imports it.
  • A service in services is not in the descriptor set.
  • The body or response_body of a google.api.http option is not a field of the message, or a response_body is not a google.api.HttpBody field.
  • The path template of a google.api.http option is invalid, for example v1/hello without the leading /.
  • With convertGRPCStatus, the descriptor set conflicts with the google/protobuf/any.proto and google/rpc/status.proto that Envoy adds. This happens when google.protobuf.Any is in another file, for example a vendored third_party/google/protobuf/any.proto.

Note: the descriptor set should be generated by protoc. Envoy may still reject a descriptor set made by another tool that protoc wouldn’t produce, for example with two fields that have the same JSON name. In that case, the whole listener is rejected.

Clean-Up

Follow the steps from the Quickstart to uninstall Envoy Gateway and the example manifest.

kubectl delete httproutefilter/grpc-transcoder
kubectl delete configmap/greeter-proto-descriptor

Next Steps

Check out the Developer Guide to get involved in the project.