无法为 gRPC 打开 Istio ingress-gateway

Unable to open Istio ingress-gateway for gRPC

这个问题是关于我无法使用 Istio 入口网关将 gRPC 客户端连接到 Kubernetes (AWS EKS) 中托管的 gRPC 服务。

在 kubernetes 方面: 我有一个带有 Go 进程的容器,在端口 8081 上侦听 gRPC。该端口在容器级别公开。我定义了一个 kubernetes 服务并公开了 8081。我定义了一个 istio 网关,它选择 istio: ingressgateway 并为 gRPC 打开端口 8081。最后,我定义了一个 istio 虚拟服务,其中包含端口 8081 上任何内容的路由。

在客户端:我有一个可以向服务发送 gRPC 请求的 Go 客户端。

日志:

Istio 的 Ingress-Gateway

kubectl describe service -n istio-system istio-ingressgateway

输出以下内容,我怀疑是问题所在,尽管我努力打开它,但未列出端口 8081。我对默认打开多少个端口感到困惑,我没有打开它们(欢迎评论如何关闭我不使用的端口,但这不是这个 SO 问题的原因)

Name:                     istio-ingressgateway
Namespace:                istio-system
Labels:                   [redacted]
Annotations:              [redacted]
Selector:                 app=istio-ingressgateway,istio=ingressgateway
Type:                     LoadBalancer
IP:                       [redacted]
LoadBalancer Ingress:     [redacted]
Port:                     status-port  15021/TCP
TargetPort:               15021/TCP
NodePort:                 status-port  31125/TCP
Endpoints:                192.168.101.136:15021
Port:                     http2  80/TCP
TargetPort:               8080/TCP
NodePort:                 http2  30717/TCP
Endpoints:                192.168.101.136:8080
Port:                     https  443/TCP
TargetPort:               8443/TCP
NodePort:                 https  31317/TCP
Endpoints:                192.168.101.136:8443
Port:                     tcp  31400/TCP
TargetPort:               31400/TCP
NodePort:                 tcp  31102/TCP
Endpoints:                192.168.101.136:31400
Port:                     tls  15443/TCP
TargetPort:               15443/TCP
NodePort:                 tls  30206/TCP
Endpoints:                192.168.101.136:15443
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

所以我认为我没有为 GRPC 正确打开端口 8081。我可以 运行 哪些其他日志或测试来帮助确定这是从哪里来的?

这是相关的 yaml:

Kubernetes Istio 虚拟服务: 其目的是将端口 8081 上的任何内容路由到 myservice

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: myservice
  namespace: mynamespace
spec:
  hosts:
  - "*" 
  gateways:
  - myservice
  http:
  - match:
    - port: 8081
    route:
    - destination:
        host: myservice

Kubernetes Istio 网关: 目的是为 GRPC 打开 8081 端口

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: myservice
  namespace: mynamespace
spec:
  selector:
    istio: ingressgateway 
  servers:
    - name: myservice-plaintext
      port:
        number: 8081
        name: grpc-svc-plaintext
        protocol: GRPC
      hosts:
      - "*"

Kubernetes 服务: 显示端口 8081 在服务级别暴露,我通过前面提到的 port-forward 测试确认

apiVersion: v1
kind: Service
metadata:
  name: myservice
  namespace: mynamespace
  labels:
    app: myservice
spec:
  selector:
    app: myservice
  ports:
    - protocol: TCP
      port: 8081
      targetPort: 8081
      name: grpc-svc-plaintext

Kubernetes 部署: 显示端口 8081 在容器级别公开,我通过前面提到的 port-forward 测试确认了这一点

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myservice
  namespace: mynamespace
  labels:
    app: myservice
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myservice
  template:
    metadata:
      labels:
        app: myservice
    spec:
      containers:
      - name: myservice
        image: [redacted]
        ports:
        - containerPort: 8081

重新检查 DNS 在客户端上的工作情况:

getent hosts [redacted]-[redacted].us-west-2.elb.amazonaws.com

输出 3 个 IP,我认为这很好。

[IP_1 redacted]  [redacted]-[redacted].us-west-2.elb.amazonaws.com
[IP_2 redacted]  [redacted]-[redacted].us-west-2.elb.amazonaws.com
[IP_3 redacted]  [redacted]-[redacted].us-west-2.elb.amazonaws.com

正在检查 Istio Ingressgateway 的路由:

istioctl proxy-status istio-ingressgateway-[pod name]
istioctl proxy-config routes istio-ingressgateway-[pod name]

returns

Clusters Match
Listeners Match
Routes Match (RDS last loaded at Wed, 23 Sep 2020 13:59:41)

NOTE: This output only contains routes loaded via RDS.
NAME          DOMAINS     MATCH                  VIRTUAL SERVICE
http.8081     *           /*                     myservice.mynamespace
              *           /healthz/ready*        
              *           /stats/prometheus*

端口 8081 路由到 myservice.mynamespace,我觉得不错。

更新 1: 我开始明白我无法使用默认的 istio 入口网关打开端口 8081。该服务不公开该端口,我假设创建网关会“在后台”更新服务,但事实并非如此。 我可以选择的外部端口是:80、443、31400、15443、15021,我认为我的网关只需要依赖这些端口。我已将网关和虚拟服务更新为使用端口 80,然后客户端可以正常连接到服务器。

这意味着我必须区分多个服务,而不是通过端口(显然不能从同一个端口路由到两个服务),而是通过 SNI,我不清楚如何在 gRPC 中做到这一点,我'我猜我可以在 gRPC header 中添加一个 Host:[hostname]。不幸的是,如果这就是我的路由方式,这意味着 headers 需要在网关上读取,并且当我希望在 pod 处终止时,这要求在网关处终止 TLS。

I am starting to understand I can't open port 8081 using the default istio ingress gateway. That service does not expose that port, and I was assuming creating a gateway would update the service "under the hood" but that's not the case. The external ports that I can pick from are: 80, 443, 31400, 15443, 15021 and I think my gateway needs to rely only on those. I've updated my gateway and virtual service to use port 80 and the client then connects to the server just fine.

我不确定您是如何尝试为入口网关添加自定义端口的,但这是可能的。

据我检查 可以通过 3 种方式完成,这里是选项以及@A_Suh、@Ryota 和@peppered 提供的示例的链接。


其他资源:


That means I have to differentiate between multiple services not by port (can't route from the same port to two services obviously), but by SNI, and I'm unclear how to do that in gRPC, I'm guessing I can add a Host:[hostname] in the gRPC header. Unfortunately, if that's how I can route, it means headers need to be read on the gateway, and that mandates terminating TLS at the gateway when I was hoping to terminate at the pod.

我看到你已经创建了新问题here,所以让我们移到那里。

我已成功将端口添加到ingress gateway,但仍然无法让客户端连接到服务器。对我来说,端口转发也有效,但是当我尝试通过入口连接时出现以下错误。这里的 istio ingressgateway 在 GKE 上,所以它使用全局 HTTPS 负载均衡器。

Jun 14, 2021 8:28:08 PM com.manning.mss.ch12.grpc.sample01.InventoryClient updateInventory
WARNING: RPC failed: Status{code=INTERNAL, description=http2 exception, cause=io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: First received frame was not SETTINGS. Hex dump for first 5 bytes: 485454502f
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:85)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.verifyFirstFrameIsSettings(Http2ConnectionHandler.java:350)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:251)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:450)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:441)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491)
at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:905)
at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)