
# 为Nginx Ingress配置gRPC协议的后端服务
在现代微服务架构中，为了提高服务间的通信效率，许多开发者选择使用gRPC协议。当通过Nginx Ingress将流量路由到gRPC后端服务时，您需要在Ingress规则中添加特殊的注解配置。本文介绍如何使用Nginx Ingress将流量路由到gRPC后端服务。
#### gRPC介绍
gRPC是一种高性能、通用的RPC开源软件框架，使用Protocol Buffer作为其接口定义语言（IDL）以及底层消息交换格式。同时GRPC采用HTTP/2标准协议实现，提供了多路复用、头部压缩、流控等特性，极大地提高了客户端与服务端的通信效率。更多信息参见[Introduction to gRPC](https://grpc.io/docs/what-is-grpc/introduction/)。
图1gRPC示意图   
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000001873175773.png "点击放大")
在gRPC中，客户端应用程序可以直接调用位于不同机器上的服务端应用方法，可以轻松创建分布式应用程序和服务。和许多其他RPC框架一样，使用gRPC需要定义调用服务的方法，包括参数和返回类型等，服务端需要实现被定义的方法，同时运行一个gRPC服务器来处理客户端请求。
#### 准备工作
- 已创建CCE Standard集群，详情请参见[购买Standard/Turbo集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0028.html)。
- 集群中已安装[NGINX Ingress控制器](https://support.huaweicloud.com/usermanual-cce/cce_10_0034.html)。
- 已安装和配置kubectl，详情请参见[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)。
- 已安装grpcurl工具，详情请参见[gRPCurl](https://github.com/fullstorydev/grpcurl/)。
 
#### gRPC服务示例
在proto文件中定义如下的gRPC服务，客户端可调用helloworld.Greeter服务的SayHello接口。关于gRPC服务示例的源码，请参见[gRPC Hello World](https://github.com/grpc/grpc-go/tree/master/examples/helloworld)。
```
syntax = "proto3";
 
option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
 
package helloworld;
 
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloReply {
  string message = 1;
}
```
Nginx Ingress中，gRPC服务只运行在HTTPS端口（默认443）上，因此在生产环境中，需要域名和对应的SSL证书。本示例使用 grpc.example.com和自签SSL证书。
#### 步骤1：创建SSL证书
1. 复制以下内容并保存至openssl.cnf文件中。 
   ```
   [req]
   distinguished_name = req_distinguished_name
   attributes = req_attributes
    
   [req_distinguished_name]
    
   [req_attributes]
    
   [test_ca]
   basicConstraints        = critical,CA:TRUE
   subjectKeyIdentifier    = hash
   authorityKeyIdentifier  = keyid:always,issuer:always
   keyUsage                = critical,keyCertSign
    
   [test_server]
   basicConstraints        = critical,CA:FALSE
   subjectKeyIdentifier    = hash
   keyUsage                = critical,digitalSignature,keyEncipherment,keyAgreement
   subjectAltName          = @server_alt_names
    
   [server_alt_names]
   DNS.1 = grpc.example.com
    
   [test_client]
   basicConstraints        = critical,CA:FALSE
   subjectKeyIdentifier    = hash
   keyUsage                = critical,nonRepudiation,digitalSignature,keyEncipherment
   extendedKeyUsage        = critical,clientAuth
   ```
   
   
2. 复制以下内容保存至create.sh文件中，create.sh与openssl.cnf文件位于同一目录下。 
   ```
   #!/bin/bash
    
   # Create the server CA certs.
   openssl req -x509                                     \
     -newkey rsa:4096                                    \
     -nodes                                              \
     -days 3650                                          \
     -keyout ca_key.pem                                  \
     -out ca_cert.pem                                    \
     -subj /C=CN/ST=CA/L=SVL/O=gRPC/CN=test-server_ca/   \
     -config ./openssl.cnf                               \
     -extensions test_ca                                 \
     -sha256
    
   # Create the client CA certs.
   openssl req -x509                                     \
     -newkey rsa:4096                                    \
     -nodes                                              \
     -days 3650                                          \
     -keyout client_ca_key.pem                           \
     -out client_ca_cert.pem                             \
     -subj /C=CN/ST=CA/L=SVL/O=gRPC/CN=test-client_ca/   \
     -config ./openssl.cnf                               \
     -extensions test_ca                                 \
     -sha256
    
   # Generate a server cert.
   openssl genrsa -out server_key.pem 4096
   openssl req -new                                    \
     -key server_key.pem                               \
     -days 3650                                        \
     -out server_csr.pem                               \
     -subj /C=CN/ST=CA/L=SVL/O=gRPC/CN=test-server1/   \
     -config ./openssl.cnf                             \
     -reqexts test_server
   openssl x509 -req           \
     -in server_csr.pem        \
     -CAkey ca_key.pem         \
     -CA ca_cert.pem           \
     -days 3650                \
     -set_serial 1000          \
     -out server_cert.pem      \
     -extfile ./openssl.cnf    \
     -extensions test_server   \
     -sha256
   openssl verify -verbose -CAfile ca_cert.pem  server_cert.pem
    
   # Generate a client cert.
   openssl genrsa -out client_key.pem 4096
   openssl req -new                                    \
     -key client_key.pem                               \
     -days 3650                                        \
     -out client_csr.pem                               \
     -subj /C=CN/ST=CA/L=SVL/O=gRPC/CN=test-client1/   \
     -config ./openssl.cnf                             \
     -reqexts test_client
   openssl x509 -req           \
     -in client_csr.pem        \
     -CAkey client_ca_key.pem  \
     -CA client_ca_cert.pem    \
     -days 3650                \
     -set_serial 1000          \
     -out client_cert.pem      \
     -extfile ./openssl.cnf    \
     -extensions test_client   \
     -sha256
   openssl verify -verbose -CAfile client_ca_cert.pem  client_cert.pem
    
   rm *_csr.pem
   ```
   
   
3. 执行以下命令生成证书。 
   ```
   chmod +x ./create.sh && ./create.sh
   ```
   命令执行成功后，可得到server_key.pem私钥文件和server_cert.pem证书文件。
   
   
4. 执行以下命令创建名为grpc-secret的TLS Secret。 
   ```
   kubectl create secret tls grpc-secret --key server_key.pem --cert server_cert.pem
   ```
   
   
 
#### 步骤2：创建gRPC应用的工作负载
在集群中创建使用gRPC协议的工作负载。
1. 复制以下YAML内容创建grpc.yaml文件。本文中使用[官方示例应用](https://github.com/grpc/grpc-go/blob/91e0aeb192456225adf27966d04ada4cf8599915/examples/features/reflection/server/main.go)构建的Docker镜像作为示例。
   
   ```
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     annotations:
       description: ''
     labels:
       appgroup: ''
       version: v1
     name: grpc-hello
     namespace: default
   spec:
     selector:
       matchLabels:
         app: grpc-hello
         version: v1
     template:
       metadata:
         labels:
           app: grpc-hello
           version: v1
       spec:
         containers:
           - name: container-1
             image: swr.cn-east-3.myhuaweicloud.com/container/grpc-hello:latest  #本文镜像仅作示例
             imagePullPolicy: IfNotPresent
         imagePullSecrets:
           - name: default-secret
         terminationGracePeriodSeconds: 30
         dnsPolicy: ClusterFirst
     replicas: 1
   ```
   
   
2. 执行以下命令创建工作负载。 
   ```
   kubectl apply -f grpc.yaml
   ```
   
   
 
#### 步骤3：为工作负载创建Service及Ingress
1. 复制以下YAML内容创建grpc-svc.yaml文件。 
   ```
   apiVersion: v1
   kind: Service
   metadata:
     name: grpc-hello
     namespace: default
     labels:
       app: grpc-hello
   spec:
     ports:
       - name: cce-service-0
         protocol: TCP
         port: 50051
         targetPort: 50051
     selector:
       app: grpc-hello
     type: NodePort
     sessionAffinity: None
   ```
   
   
2. 执行以下命令创建Service： 
   ```
   kubectl apply -f grpc-svc.yaml
   ```
   
   
3. 复制以下YAML内容创建grpc-ingress.yaml文件。 
   ```
   apiVersion: networking.k8s.io/v1
   kind: Ingress
   metadata:
     name: grpc-hello
     namespace: default
     annotations:
       nginx.ingress.kubernetes.io/backend-protocol: GRPC # 指定后端服务为gRPC服务
   spec:
     ingressClassName: nginx
     tls:
       - secretName: grpc-secret
     rules:
       - host: grpc.example.com
         http:
           paths:
             - path: /
               pathType: Prefix
               backend:
                 service:
                   name: grpc-hello
                   port:
                     number: 50051
   ```
   
   
4. 执行以下命令创建Ingress路由规则。 
   ```
   kubectl apply -f grpc-ingress.yaml
   ```
   
   
 
#### 结果验证
本地安装grpcurl工具后，执行以下命令，验证是否安装成功
```
./grpcurl -insecure -servername "grpc.example.com" <ip_address>:443 list
```
其中，\<ip_address\>为负载均衡器的ip地址，可通过**kubectl get ingress**获取。
预期输出：
```
grpc.examples.echo.Echo
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
```
#### 相关文档
您可以使用Nginx Ingress实现gRPC服务的灰度发布，请参见[使用Nginx Ingress实现灰度发布和蓝绿发布](https://support.huaweicloud.com/bestpractice-cce/cce_bestpractice_10003.html)。
