远程过程调用gRPC(Google Remote Procedure Call)是基于HTTP/2协议标准和ProtoBuf(Protocol Buffers)序列化协议开发设计,且支持众多开发语言,继而提供了连接多路复用、头部压缩、流控等特性,极大地提高了客户端与服务端的通信效率。本文介绍如何通过阿里云容器服务Ingress Controller实现gRPC协议的服务后端的路由功能。

背景信息

gRPC是Google开源的一个高性能RPC通信框架,通过Protocol Buffers作为其IDL,在不同语言开发的平台上使用,同时gRPC基于HTTP/2协议实现,提供了多路复用、头部压缩、流控等特性,极大地提高了客户端与服务端的通信效率。gRPC简介在gRPC服务中,客户端应用可以同本地方法一样调用到位于不同服务器上的服务端应用方法,可以很方便地创建分布式应用和服务。同其他RPC框架一样,gRPC也需要定义一个服务接口,同时指定被远程调用的方法和返回类型。服务端需要实现被定义的接口,同时运行一个gRPC服务器来处理客户端请求。

前提条件

gRPC服务示例

定义如下gRPC服务,客户端可调用helloworld.Greeter服务的SayHello接口:
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;
}
关于gRPC服务示例的源码,请参见gRPC Hello World

示例说明

NGINX Ingress Controller中,gRPC服务只运行在HTTPS端口(默认443)上,因此在生产环境中,需要域名和对应的SSL证书。本示例使用 grpc.example.com和自签SSL证书。

本示例基于1.20.4-aliyun集群,Nginx Ingress Controller组件版本为v0.44.0.3-8e83e7dc6-aliyun。

若您已有gRPC服务,可直接执行步骤三:创建Ingress路由规则

步骤一:申请SSL证书

使用Ingress转发gRPC服务需要对应域名拥有SSL证书,使用TLS协议进行通信。

您可以在SSL证书控制台购买证书,也可以通过Let's Encrypt等第三方平台申请免费的SSL证书。

本示例使用OpenSSL生成的自签证书。

  1. 复制以下内容并保存至/tmp/openssl.cnf文件中。
    [ req ]
    #default_bits           = 2048
    #default_md             = sha256
    #default_keyfile        = privkey.pem
    distinguished_name      = req_distinguished_name
    attributes              = req_attributes
    req_extensions          = v3_req
    
    [ req_distinguished_name ]
    countryName                     = Country Name (2 letter code)
    countryName_min                 = 2
    countryName_max                 = 2
    stateOrProvinceName             = State or Province Name (full name)
    localityName                    = Locality Name (eg, city)
    0.organizationName              = Organization Name (eg, company)
    organizationalUnitName          = Organizational Unit Name (eg, section)
    commonName                      = Common Name (eg, fully qualified host name)
    commonName_max                  = 64
    emailAddress                    = Email Address
    emailAddress_max                = 64
    
    [ req_attributes ]
    challengePassword               = A challenge password
    challengePassword_min           = 4
    challengePassword_max           = 20
    
    [v3_req]
    # Extensions to add to a certificate request
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = grpc.example.com
  2. 执行以下命令签署证书请求。
    openssl req -new -nodes -keyout grpc.key -out grpc.csr -config /tmp/openssl.cnf -subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=AlibabaCloud/OU=ContainerService/CN=grpc.example.com"
  3. 执行以下命令签署证书。
    openssl x509 -req -days 3650 -in grpc.csr -signkey grpc.key -out grpc.crt -extensions v3_req -extfile /tmp/openssl.cnf
    命令执行成功后,可得到证书grpc.crt与私钥文件grpc.key
  4. 执行以下命令将名称为grpc-secret的TLS Secret添加到集群中。
    或可通过控制台将TLS Secret添加到集群中。具体操作,请参见创建保密字典
    kubectl create secret tls grpc-secret --key grpc.key --cert grpc.crt

步骤二:创建gRPC服务所需资源

在集群中创建使用gRPC协议的后端服务。本示例使用镜像registry.cn-hangzhou.aliyuncs.com/acs-sample/grpc-server创建gRPC服务。

  1. 复制以下YAML内容创建grpc.yaml文件。
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: grpc-service
    spec:
      replicas: 1
      selector:
        matchLabels:
          run: grpc-service
      template:
        metadata:
          labels:
            run: grpc-service
        spec:
          containers:
          - image: registry.cn-hangzhou.aliyuncs.com/acs-sample/grpc-server:latest
            imagePullPolicy: Always
            name: grpc-service
            ports:
            - containerPort: 50051
              protocol: TCP
          restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: grpc-service
    spec:
      ports:
      - port: 50051
        protocol: TCP
        targetPort: 50051
      selector:
        run: grpc-service
      sessionAffinity: None
      type: NodePort
  2. 执行以下命令创建gRPC Deployment资源以及对应的服务。
    kubectl apply -f grpc.yaml
    预期输出:
    deployment.apps/grpc-service created
    service/grpc-service created
  3. 执行以下命令查看Pod。
    kubectl get pod
    预期输出:
    NAME                                   READY   STATUS              RESTARTS   AGE
    grpc-service-57884679-2bkbr            1/1     Running             0          98s
    从预期输出可得,grpc-service-57884679-2bkbr的状态为Running,已成功启动。

步骤三:创建Ingress路由规则

  1. 复制以下YAML内容创建grpc-ingress.yaml文件。
    注意
    • 部署gRPC服务所使用的Ingress需要在annotation中加入nginx.ingress.kubernetes.io/backend-protocol,值为GRPC
    • 本示例使用的域名为grpc.example.com,请根据实际情况修改。
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: grpc-ingress
      annotations:
        # 注意这里:必须要配置以指明后端服务为gRPC服务
        nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
    spec:
      # 指定证书
      tls:
      - hosts:
        - grpc.example.com
        secretName: grpc-secret
      rules:
      # gRPC服务域名
      - host: grpc.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                # gRPC服务
                name: grpc-service
                port:
                  number: 50051
  2. 执行以下命令创建Ingress路由规则。
    kubectl apply -f grpc-ingress.yaml
    预期输出:
    ingress.networking.k8s.io/grpc-ingress created

结果验证

本地安装gRPCurl工具后,输入grpcurl <域名>:443 list验证请求是否成功转发到后端服务。

本示例中使用域名grpc.example.com以及自签证书,执行以下命令验证请求是否成功转发到后端服务。

grpcurl -insecure -authority grpc.example.com <ip_address>:443 list
说明 ip_address为Nginx Ingress Controller的Service外部IP,可通过kubectl get ingress获取。
预期输出:
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
从预期输出可得,流量被Ingress成功转发到后端gRPC服务。

后续步骤

关于如何通过Ingress Controller实现gRPC服务的灰度发布,请参见通过Ingress实现灰度发布和蓝绿发布
注意 由于Nginx grpc_pass的限制,目前对于gRPC服务,暂不支持service-weight的配置。