写点什么

使用 Gateway API 在命名空间之间发送流量

作者:Gingxing
  • 2024-03-17
    上海
  • 本文字数:4292 字

    阅读完需:约 14 分钟

使用 Gateway API 在命名空间之间发送流量

在这篇博客文章中,我们将展示如何使用 Gateway API 的 HTTPRoutes 在单个 Kubernetes 集群中轻松地将流量路由到部署在不同命名空间的工作负载——这个过程比以往任何时候都更简单。

之前,我们只有 Ingress API 来定义入口路由规则。它很好地为我们服务,但也存在我们必须克服的缺点和局限性,有时我们甚至不得不采用一些不太优雅的方式。其中一个局限性就是,将流量路由到与我们的 Ingress 定义在不同命名空间中的 Services。

完整的示例请参考我们创建的 Gist 。


使用 ReferenceGrants 实现跨命名空间的 HTTPRoute 和 Service 引用

假设我们想要将 Kubernetes 集群分成三个命名空间,每个团队一个:

  • infra - 用于部署公司的 API Gateway 以及配置其行为所需的资源(即 Ingress、Gateway、HTTPRoute 等)。

  • apples - 用于部署 apples 团队管理的工作负载。

  • bananas - 用于部署 bananas 团队管理的工作负载。

在 Ingress API 中,要使用一个 Service,我们必须确保在 Ingress 规则中引用的 Service 定义在与 Ingress 相同的命名空间中。因此,如果我们想在 infra 命名空间中创建一个 Ingress,并使用来自 apples 和 bananas 命名空间的 Service,我们就必须使用一些非标准的变通方法——比如在 Ingress 命名空间中创建一个额外的 ExternalName Service,该 Service 指向另一个命名空间中 Service 的 FQDN。更多内容可以参考 Stack Overflow 上的这两个讨论(点击这里1 和 点击这里2.)。

好消息是?使用 Gateway API 不再需要这些变通方法,因为这些用例从一开始就被考虑在内了。

在 Gateway API 中,Ingress 的等价物是  HTTPRoute 资源。根据设计,它可以在单个规则中使用多个 Service 作为其后端,以便使用用户定义的权重对流量进行负载均衡。更重要的是,只要在 Service 的命名空间中定义了 ReferenceGrant 对象,允许在指定的资源(例如 HTTPRoute)中引用它,那么这些 Service 就可以部署在任何命名空间中。

假设我们希望在 infra 命名空间中定义一个 HTTPRoute,将发送到 /fruits 端点的 70% 的流量路由到团队的 bananas Service(部署在 bananas 命名空间中),其余的流量则路由到团队的 apples Service(部署在 apples 命名空间中)。这样的 HTTPRoute 可能如下所示:

kind: HTTPRouteapiVersion: gateway.networking.k8s.io/v1metadata:  name: echo-route  namespace: infraspec:  parentRefs:    - group: gateway.networking.k8s.io      kind: Gateway      name: kong  rules:    - matches:        - path:            type: PathPrefix            value: /fruits      backendRefs:        - name: bananas-echo          port: 1027          kind: Service          namespace: bananas          weight: 30        - name: apples-echo          port: 1027          kind: Service          namespace: apples          weight: 70
复制代码

假设我们在 bananas 和 apples 命名空间中未定义任何 ReferenceGrants,就定义这样的 HTTPRoute。在这种情况下,我们的 Gateway API 实现(在我们的案例中为 Kong Ingress Controller)将拒绝这种引用,并在 HTTPRoute 父级的状态的 ResolvedRefs 条件中通知我们这一点:

$ kubectl get httproute -n infra echo-route -o=jsonpath='{.status.parents[0].conditions}' | jq [  ...  {    "observedGeneration": 1,    "reason": "RefNotPermitted",    "status": "False",    "type": "ResolvedRefs"  },  ...]
复制代码

为了解析这些引用,我们只需要为这两个命名空间创建 ReferenceGrants 即可:

kind: ReferenceGrantapiVersion: gateway.networking.k8s.io/v1beta1metadata:  name: allow-apples-echo-in-infra  namespace: bananasspec:  from:     - group: gateway.networking.k8s.io      kind: HTTPRoute      namespace: infra  to:    - group: ""      kind: Service      name: bananas-echo # This could be left empty to allow ANY service in the namespace.# A similar one for apples namespace and apples-echo service should be created as well.
复制代码

ReferenceGrant 的 spec.from 和 spec.to 列表定义了我们可以从哪些资源引用我们在定义 ReferenceGrant 的命名空间中定义的资源。在我们的案例中,我们允许在 infra 命名空间中定义的 HTTPRoute 引用在 bananas 命名空间中名为 banana-echo 的 Service(因为 ReferenceGrant 在这里定义)。

有了这些设置,我们现在可以确保引用已被正确解析,并且我们的流量已按预期转发。

首先,让我们检查 Gateway 是否已解析这些引用:


$ kubectl get httproute -n infra echo-route -o=jsonpath='{.status.parents[0].conditions}' | jq [ { "observedGeneration": 2, "reason": "ResolvedRefs", "status": "True", "type": "ResolvedRefs" }, ]
复制代码

引用已解析后,现在我们可以确保流量按预期进行路由:

$ http 'http://${PROXY_IP}/fruits'   HTTP/1.1 200 OK   Connection: keep-alive   Content-Length: 22   Content-Type: text/plain; charset=utf-8   Date: Mon, 06 Nov 2023 18:06:01 GMT   Via: kong/3.4.2   X-Kong-Proxy-Latency: 2   X-Kong-Upstream-Latency: 3
In namespace apples.
$ http 'http://${PROXY_IP}/fruits' HTTP/1.1 200 OK Connection: keep-alive Content-Length: 23 Content-Type: text/plain; charset=utf-8 Date: Mon, 06 Nov 2023 18:06:02 GMT Via: kong/3.4.2 X-Kong-Proxy-Latency: 0 X-Kong-Upstream-Latency: 1
In namespace bananas.
复制代码

正如预期的那样,我们的流量通过部署在 infra 命名空间中的 Gateway 被路由到部署在 apples 和 bananas 团队的命名空间中的 Service。


跨命名空间的 HTTPRoute 共享 Gateway

既然我们已知 ReferenceGrant 可以作为将来自不同命名空间的 Service 用作 Gateway API Routes 的后端的补救措施,那么现在让我们思考一下公司基础设施的另一种略有不同的场景。

我们不想为基础设施(Gateway、HTTPRoutes 等)设置单独的命名空间,而是希望允许团队在他们的命名空间中定义 HTTPRoute,以便他们可以对入口流量规则进行更多的控制。我们命名空间的特性将如下:

  • infra - 用于部署我们公司的 API Gateway(即 Gateway、GatewayClass)。

  • apples - 用于部署团队 apples 管理的工作负载和 Gateway API Routes,允许入口流量进入。

  • bananas - 用于部署团队 bananas 管理的工作负载和 Gateway API Routes,允许入口流量进入。

首先,让我们在团队的 apples 命名空间中创建一个 HTTPRoute,看看默认的 Gateway 配置会发生什么。

kind: HTTPRouteapiVersion: gateway.networking.k8s.io/v1metadata:  name: route  namespace: applesspec:  parentRefs:    - group: gateway.networking.k8s.io      kind: Gateway      name: kong      namespace: infra # We need to explicitly specify the namespace of the Gateway as now it is not in the same namespace                        # as our HTTPRoute (apples).  rules:    - matches:        - path:            type: PathPrefix            value: /apples      backendRefs:        - name: apples-echo          port: 1027          kind: Service          # We can omit the namespace as the Service is in the same namespace as our HTTPRoute (apples).
复制代码

在创建了上述 HTTPRoute 后,让我们查看其状态条件。

$ kubectl get -n apples httproute route -o=jsonpath='{.status.parents[0].conditions}' | jq[    {        "lastTransitionTime": "2023-12-07T12:31:56Z",        "observedGeneration": 1,        "reason": "NotAllowedByListeners",        "status": "False",        "type": "Accepted"    },]
复制代码

我们得到了状态为 FalseAccepted 条件,原因是 NotAllowedByListeners。这意味着我们的 Gateway 的监听器不允许将我们的 HTTPRoute 附加到它们上。默认情况下,Gateway 仅接受来自其命名空间的 Routes。如果我们想克服这个限制,可以使用 Gateway 的监听器的 allowedRoutes 字段。

在我们的示例中,我们将修改 Gateway 的配置,以允许来自 apples 和 bananas 命名空间的 HTTPRoutes。

apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata:  name: kong  namespace: infraspec:  gatewayClassName: kong  listeners:  - name: proxy    port: 80    protocol: HTTP    allowedRoutes:      namespaces:        from: Selector        selector:          matchExpressions:            # Allow attaching Routes from apples and bananas namespaces.            - key: kubernetes.io/metadata.name              operator: In              values: [apples, bananas]
复制代码

在将代理监听器的 allowRoutes 更改为上述内容后,我们现在应该能够看到我们的 HTTPRoute 被 Gateway 成功接受。

$ kubectl get -n apples httproute route -o=jsonpath='{.status.parents[0].conditions}' | jq [     {         "lastTransitionTime": "2023-12-07T13:09:58Z",         "observedGeneration": 1,         "reason": "Accepted",         "status": "True",         "type": "Accepted"     }, ]
复制代码

为了最终验证在 apples 命名空间中创建的 HTTPRoute 是否已成功附加到 Gateway 并进行了配置,我们可以调用预期返回 200 状态码的端点。

$ http "http://${PROXY_IP}/apples?details=true"HTTP/1.1 200 OK
In namespace apples.
复制代码

完成!


结论

总结我们所学的知识,Gateway 的监听器的 allowedRoutes 和 ReferenceGrant 都是有用的 Gateway API 工具。它们允许明确配置 Gateway 控制器在解析可能在 Gateway API 对象定义中出现的跨命名空间引用时将遵循的规则。

如果您想进一步了解这个主题,以下是一些可能有用的参考链接:

发布于: 刚刚阅读数: 4
用户头像

Gingxing

关注

还未添加个人签名 2019-03-25 加入

还未添加个人简介

评论

发布
暂无评论
使用 Gateway API 在命名空间之间发送流量_kong_Gingxing_InfoQ写作社区