< Back to articles

How to Setup a gRPC-Web Backend on Google Cloud Run with Envoy Proxy

Josef GattermayerJosef Gattermayer
February 25, 2021

So far we used Cloud Run at Ackee only for smaller projects with REST backends, but since January 2021 the situation changed as Google added support for gRPC streams - it became a serious option for gRPC.

A great starting point is this example where you can find everything you need to set up a gRPC server on Cloud Run... but everything? Only if you don't plan to use web clients. As current browsers don't fully support HTTP/2 gRPC spec there must be a backend that provides gRPC-web - a standard usable by current browsers. And this is something that Cloud Run doesn't provide, so we need to create own proxy.

We use envoy for that - a bit too robust solution, but so far I'm not aware of anything better for a production environment. Envoy is deployed to Cloud Run as a service that provides HTTP/1 endpoint for gRPC-web clients and proxies traffic to gRPC services (such as Calculator from the example above). A nice thing about Cloud Run is that it runs out of the box, but until things go wrong... and when I tried to implement envoy things went wrong. The problem is that there are not enough logs from the load balancer of Cloud Run that is responsible for TLS negotiation and envoy failed exactly on this.

After hours I solved this issue so I will share my configuration as I haven't found any other working example.

We have this Dockerfile container with envoy

FROM envoyproxy/envoy-alpine:v1.17.0  
COPY envoy.yaml /etc/envoy/envoy.yaml  
RUN apk --no-cache add ca-certificates  

And now the whole envoy.yaml config file

static_resources:  
  listeners:  
  - name: listener_0  
    address:  
      socket_address: { address: 0.0.0.0, port_value: 8080 }  
    filter_chains:  
    - filters:  
      - name: envoy.filters.network.http_connection_manager  
        typed_config:  
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager  
          codec_type: auto  
          stat_prefix: ingress_http  
          route_config:  
            name: local_route  
            virtual_hosts:  
            - name: local_service  
              domains: ["*"]  
              routes:  
              - match: { prefix: "/" }  
                route:  
                  cluster: api-gateway-proxy  
                  auto_host_rewrite: true  
                  max_stream_duration:  
                    grpc_timeout_header_max: 0s  
              cors:  
                allow_origin_string_match:  
                - prefix: "*"  
                allow_methods: GET, PUT, DELETE, POST, OPTIONS  
                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout  
                max_age: "1728000"  
                expose_headers: custom-header-1,grpc-status,grpc-message  
          http_filters:  
          - name: envoy.filters.http.grpc_web  
          - name: envoy.filters.http.cors  
          - name: envoy.filters.http.router  
            typed_config:  
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router  
  clusters:  
  - name: api-gateway-proxy  
    type: strict_dns  
    connect_timeout: 20s  
    http2_protocol_options: {}  
    lb_policy: round_robin  
    dns_refresh_rate: 90s  
    load_assignment:  
      cluster_name: api-gateway-proxy  
      endpoints:  
        - lb_endpoints:  
            - endpoint:  
                address:  
                  socket_address:  
                    address: YOUR_GRPC_APP_URL.a.run.app  
                    port_value: 443  
    dns_lookup_family: V4_ONLY  
    transport_socket:  
      name: envoy.transport_sockets.tls  
      typed_config:  
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext  
        common_tls_context:  
          alpn_protocols: h2  
          validation_context:  
            trusted_ca:  
              filename: /etc/ssl/certs/ca-certificates.crt  
        sni: YOUR_GRPC_APP_URL.a.run.app  

Replace YOUR_GRPC_APP_URL with an URL of your gRPC service (on Cloud Run or anywhere else) and deploy a container with envoy to Cloud Run. Then you should be able to start accepting gRPC-web connections from the new endpoint this newly deployed service creates.

Josef Gattermayer
Josef Gattermayer
Co-Founder Ackee & CEO Ackee BlockchainJosef is one of the three co-founders of Ackee, CEO of Ackee Blockchain and doctor of distributed systems at CTU.

Are you interested in working together? Let’s discuss it in person!

Get in touch >