~Engineers solve problems, I solve engineer's problems 🤘
gRPC web is the JavaScript implementation of gRPC for browser clients.
There is no current browser that supports HTTP/2 gRPC spec currently. The reason essentially is because raw HTTP/2 frames are inaccessible in browsers. You can read further on this here. Hence, gRPC-web clients connect to gRPC services via a special proxy; by default, gRPC-web uses Envoy. The purpose of Envoy is to translates the HTTP/1.1 calls produced by the client into HTTP/2 calls that can be handled by those services.
In this post, We will try to see what needs to be done on Istio, Kubernetes and Envoy side to have a gRPC-web client talk (through browser) to gRPC based services deployed in k8s cluster over a service-mesh (Istio). The GA release of gRPC-web was announced in October 2018 and since then there is very little documentation on this. The most help I got was from this article by Venil Noronha. But this was also written approximately 3 years ago so here is my take with latest versions.
Considering you have a backend app ready locally, you can test it tools like grpcurl, grpcui or evans cli which you are comfortable with. When deploying to the Kubernetes cluster, you need to make sure the following things:
On the gateway side you need to allow specific ports for http and https traffic. You can also allow a specific port for gRPC traffic in case you want to call backend from another gRPC service through gateway but it’s not a requirement in our case with grpc-web.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: gateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 8080
protocol: HTTP
tls:
httpsRedirect: true
- hosts:
- '*'
port:
name: https
number: 8443
protocol: HTTPS
tls:
mode: SIMPLE
- hosts:
- '*'
port:
name: grpc
number: 50051
protocol: GRPC
You have to add the port name with prefix grpc-web
so that Kubernetes recognizes it as gRPC based service and Istio automatically adds the http listeners required on envoy side for grpc-web. Further read on this here
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
ports:
- name: grpc-web
port: 50051
protocol: TCP
targetPort: 50051
selector:
name: backend
type: ClusterIP
grpc-web do require a complex CORS policy to work properly and the port should match the one you have given in gateway for that specific traffic e.g. in our case https
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: backend-vs
spec:
gateways:
- gateway
hosts:
- "*"
- corsPolicy:
allowCredentials: true
allowHeaders:
- grpc-timeout
- content-type
- 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
allowMethods:
- POST
- GET
- OPTIONS
- PUT
- DELETE
allowOrigins:
- regex: "*"
exposeHeaders:
- content-type
- grpc-status
- grpc-message
maxAge: 1728s
match:
- port: 8443
route:
- destination:
host: backend
port:
number: 50051
The URL to call from the frontend code is https based where host url is the one in virtual service but you can also do http and change the matching port config in virtual service.
var echoService = new proto.mypackage.EchoServiceClient('https://backend-host-url-in-vritualservice');
For the rest of the things and deploying frontend etc you dont really need any extra configuration.
Deploying grpc-web on k8s + Istio does require a few extra configurations but all of them are quite reasonable and not as painful as I thought. Good Luck!