My blog server has two ports open, one for serving HTTP requests and one for serving gRPC requests from clients.

Later, after learning more about gRPC, I found out that the underlying transport layer protocol is actually based on HTTP/2. At the same time, I saw the official NGINX gRPC support. So I wanted to use NGINX to proxy gRPC requests so that I wouldn’t have to think about the security issues of gRPC (but HTTP/2 isn’t necessarily HTTPS).

The security issue is one, the biggest benefit, I think, is that no need to treat HTTP and gRPC addresses differently in the configuration file.

Detect NGINX support

NGINX supports the gRPC proxy family of directives from version v1.13.10 (reference: nginx_http_grpc_module), and started the ngx _http_v2_module module.

It can be viewed as follows.

1
2
3
4
5
$ nginx -vV
nginx version: nginx/1.17.8
...
--with-http_v2_module
...

You can see that my version is higher than v1.13.10 and has http_v2_module.

Simple configuration of NGINX

Then you can implement a proxy gRPC request in one sentence like the following.

1
2
3
4
5
6
7
server {
    # 省略其它配置

    location / {
        grpc_pass grpc://localhost:2563;
    }
}

The usage of grpc_pass is very similar to proxy_pass. Where grpc:// is optional, if gRPC is a secure connection, grpcs:// should be used.

gRPC client configuration

Then just modify the code of your gRPC client to change the original gRPC address to that of NGINX. Note: : The gRPC address has no protocol header, i.e. you should remove the http:// or https:// from the NGINX address, but the port is required. For example.

  • Your nginx address is http://example.com, then the gRPC address is: example.com:80
  • Your nginx address is https://example.com, then the gRPC address: example.com:443
  • Your nginx address is http://example.com:8080, then the gRPC address: example.com:8080

For gRPC clients, if nginx is an insecure connection (not HTTPS), you should use something like the following code.

1
2
3
4
conn, err := grpc.Dial(
    endpoint,
    grpc.WithInsecure(),
)

And if it is secure (a valid HTTPS certificate is used).

1
2
3
4
conn, err := grpc.Dial(
    endpoint,
    grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
)

And if a self-signed (or expired, etc.) certificate is used and you want to ignore the security detection.

1
2
3
4
5
6
conn, err := grpc.Dial(
    endpoint,
    grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
        InsecureSkipVerify: true,
    })),
)

You can also add your own server-side certificate for verification, which is omitted from this article and will be added if needed. (Considering that few nginx servers should use insecure certificates)

NGINX routes proxies according to the gRPC method

First, you need to know the method full name of your Protocol Buffer package.

Open the *.pb.go file corresponding to the RPC service generated by your PB, for example: service.pb.go. Then search for Invoke or FullMethod and you should see strings like /protocols.TaoBlog/ListComments, which are the method full names. Inside the double slash // is the PackageName.sServiceName followed by MethodName.

If you want to route based on package name. ServiceName to route, it would look like the following.

1
2
3
4
5
6
7
location /protocols.TaoBlog/ {
    grpc_pass grpc://192.168.20.11:50051;
}

location /helloworld.Greeter/ {
    grpc_pass grpc://192.168.20.11:50052;
}

Load balancing of gRPC servers in NGINX

There are many gRPC microservices, so load balancing is inevitable.

Use NGINX’s own upstream to do so.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
upstream grpcservers {
    server 192.168.20.11:50051;
    server 192.168.20.12:50051;
}

server {
    # 省略其它常用配置

    location /helloworld.Greeter/ {
        grpc_pass grpc://grpcservers;
    }
}

Other

I have only used one directive of the nginx_http_grpc_module module to complete the NGINX proxy for gRPC. If you need more configuration, see the NGINX documentation.

Reference