为什么要将gRPC服务暴露到Kubernetes集群外部

在实践中我们的微服务使用下面的集成方式:

grpc-services-in-k8s

  • 所有服务之间使用gRPC通讯, gRPC是我们微服务的最主要的技术栈
  • 从集成方式上,将微服务划分为两类:
    • 基础服务:这种服务作为聚合服务的后端服务,关注最基础的模块和功能,基础服务以gRPC协议暴露
    • 聚合服务:聚合多个后端服务成为一个独立应用的后端服务,聚合服务同时以gRPC和HTTP协议暴露(HTTP暴露使用了grpc-gateway这个代理组件)

在测试和生产环境中,聚合服务往往需要暴露到Kubernetes集群外部,比如可以是某个前端站点服务(前后端分离)的HTTP API,也可能是某个APP(Android/iOS)的gRPC API。 在开发和测试环境中,基础服务也需要暴露到Kubernetes集群外部,因为某个开发人员在开发过程中可能需要调用这个由其他团队负责的基础服务。

在早期我们使用的NodePort将Kubernetes集群中gRPC服务暴露到集群外部,随着服务数量的增多,在集群的Node节点上暴露了大量的NodePort端口,同时维护一个很大的端口列表。Nginx从1.13.10开始提供了对gRPC的原生支持,Kubernetes的Nginx Ingress随后也加入了对gRPC的支持,因此使用Kubernetes的Nginx Ingress可以从7层的暴露gRPC服务到集群外部。即不需要再为每个gRPC服务暴露一个NodePort,只需为每个gRPC服务分配一个域名就可以将这个gRPC服务从Nginx Ingress Controller的443端口暴露出去。

使用Ingress将gRPC服务暴露到Kubernetes集群外部

这里假设Kubernetes集群中运行一个foo-svc的gRPC Service,以foo-svc为例,将它暴露到集群外部。 从集群外部访问这个服务的域名为foo-svc.frognew.com,需要创建foo-svc.frognew.com的TLS证书的Secret,这里是frognew-tls-secret

接下来集群中为foo-svc创建下面的Ingress资源即可:

 1apiVersion: extensions/v1beta1
 2kind: Ingress
 3metadata:
 4  name: foo-svc
 5  labels:
 6    app: foo-svc
 7  annotations:
 8      nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
 9      # nginx.ingress.kubernetes.io/grpc-backend: "true" DEPRECATED since nginx ingress 1.8
10      nginx.ingress.kubernetes.io/ssl-redirect: "false"
11spec:
12  rules:
13    - host: foo-svc.frognew.com
14      http:
15        paths:
16          - path: /
17            backend:
18              serviceName: foo-svc
19              servicePort: 80
20  tls:
21    - hosts:
22      - foo-svc.frognew.com
23      secretName: frognew-tls-secret

注意这里使用nginx.ingress.kubernetes.io/backend-protocol: "GRPC"来致命ingress后端的Service是gRPC协议。 (在nginx ingress controller 1.8之前的版本使用的是nginx.ingress.kubernetes.io/grpc-backend: "true")。

关于Nginx Ingress Controller的配置都是通过Annotation实现的,更多配置可参考NGINX Ingress Annotations

参考