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.

Are you interested in working together? We wanna know more. Let’s discuss it in person!

Get in touch >