跨命名空间路由
原文:https://gateway-api.sigs.k8s.io/guides/user-guides/multiple-ns/
Gateway API 原生支持跨 Namespace 路由。在多用户或多团队共享同一套底层网络基础设施、但又需要把控制权与配置分片以最小化访问域和故障域的场景下,这一能力非常有用。
Gateway 和 Route 可以被部署到不同的命名空间,并且 Route 可以跨命名空间附加到 Gateway 上。这让我们能够针对 Route 和 Gateway 分别为不同的命名空间设置不同的访问控制,从而把"集群级路由配置"中不同部分的访问与控制有效分片。Route 跨命名空间附加到 Gateway 的能力由 Route 附加 规则约束。本指南就深入讲解 Route 附加机制,演示独立团队如何安全地共享同一个 Gateway。
在本指南中,两个独立团队 store 和 site 在同一 Kubernetes 集群的 store-ns 和 site-ns 命名空间内运行。他们的目标以及他们用 Gateway API 资源达成目标的方式如下:
- site 团队拥有两个应用:home 和 login。该团队希望尽可能在两个应用之间隔离访问与配置,以最小化访问域和故障域。他们使用分别独立的 HTTPRoute(共享同一个 Gateway)来隔离路由配置(如金丝雀发布),但依然共享同一个 IP、端口、DNS 域名和 TLS 证书。
- store 团队有一个名为 store 的 Service,部署在
store-ns命名空间下,也需要通过同一个 IP 和域名对外暴露。 - Foobar 公司把
foo.example.com这个域名下的所有应用都视为一个整体来管理。这套配置由一个集中的基础设施团队管理,团队所在命名空间是infra-ns。 - 最后,安全团队负责
foo.example.com的证书管理。通过把证书挂到那个唯一的共享 Gateway 上,他们就能在不直接介入应用团队的情况下,集中把控安全相关配置。
Gateway API 资源之间的逻辑关系如下图所示:
flowchart TD
subgraph "infra-ns (基础设施团队)"
GW[shared-gateway]
SECRET[foo-example-com 证书]
end
subgraph "site-ns (site 团队)"
HR1[HTTPRoute: home]
HR2[HTTPRoute: login]
end
subgraph "store-ns (store 团队)"
HR3[HTTPRoute: store]
end
HR1 -. "parentRefs" .-> GW
HR2 -. "parentRefs" .-> GW
HR3 -. "parentRefs" .-> GW跨命名空间 Route 附加
Route 附加 是一个重要概念,它决定了 Route 如何挂到 Gateway 上,并形成最终生效的路由规则。在多命名空间共享 Gateway 资源的场景下,这一概念尤其重要。
Gateway 与 Route 的附加是双向的:只有当 Gateway 拥有者和 Route 拥有者都同意这种关系时,附加才能成功。这种双向关系存在两个原因:
- Route 拥有者不希望通过他们并不了解的路径过度暴露自己的应用。
- Gateway 拥有者不希望某些应用或团队未经允许就使用 Gateway。比如:内部服务不应通过一个面向公网的 Gateway 被访问到。
Gateway 通过附加约束(attachment constraints) 来控制允许哪些 Route 被附加。附加约束是 Gateway listener 上的字段,可以基于 Namespace 和 Route 类型 来进行限制。任何不满足附加约束的 Route 都无法挂到这个 Gateway 上。
同样,Route 通过自己的 parentRef 字段显式指定要附加的 Gateway。这两者共同在基础设施所有者和应用所有者之间形成一道**"握手"机制,让他们能各自独立地定义"应用如何通过 Gateway 暴露"。这种机制本质上是一条策略,能显著降低运维开销:应用所有者可以指明他们的应用要用哪些 Gateway;基础设施所有者则能约束**该 Gateway 接受哪些 Namespace、哪些类型的 Route。
共享 Gateway
基础设施团队把 shared-gateway 这个 Gateway 部署到 infra-ns 命名空间:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: infra-ns
spec:
gatewayClassName: shared-gateway-class
listeners:
- name: https
hostname: "foo.example.com"
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
tls:
certificateRefs:
- name: foo-example-com上面这个 Gateway 中的 https listener 匹配的是 foo.example.com 这个域名下的流量。这让基础设施团队可以全权管理该域名相关的所有细节。下面的 HTTPRoute 不需要显式指定域名——如果不设置 hostname,所有流量都会被默认匹配上。这让 HTTPRoute 的管理更轻松:HTTPRoute 可以是与域名无关的,这在"应用域名不固定"的场景下特别有用。
这个 Gateway 还通过 infra-ns 命名空间下的 foo-example-com Secret 配置了 HTTPS。这让基础设施团队能够代表应用所有者集中管理 TLS。foo-example-com 这张证书会对所有挂到该 listener 上的 Route 流量进行 TLS 终止,而 HTTPRoute 自身完全不需要任何 TLS 配置。
该 Gateway 使用 Namespace selector 来决定哪些 HTTPRoute 允许附加上来。这让基础设施团队通过白名单一组 Namespace 来约束哪些人或哪些应用可以使用该 Gateway。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
spec:
listeners:
- allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
...只有打了 shared-gateway-access: "true" 这个 label 的命名空间,才能把自己的 Route 挂到 shared-gateway 上。在下面这一组命名空间中,如果 no-external-access 命名空间下存在一条 HTTPRoute,并且 parentRef 指向 infra-ns/shared-gateway,那么它会被 Gateway 忽略——因为它不满足附加约束(Namespace label 不匹配):
apiVersion: v1
kind: Namespace
metadata:
name: infra-ns
labels:
shared-gateway-access: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: site-ns
labels:
shared-gateway-access: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: store-ns
labels:
shared-gateway-access: "true"
---
apiVersion: v1
kind: Namespace
metadata:
name: no-external-access注意:Gateway 上的附加约束不是必须的,但在拥有多团队、多数命名空间的集群中属于最佳实践。如果集群中所有应用都"天然"拥有挂到 Gateway 的权限,那么就不需要配置 listeners[].allowedRoutes 字段,所有 Route 都可以自由使用 Gateway。
Route 附加
store 团队把 store Service 的路由部署到 store-ns 命名空间:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: store
namespace: store-ns
spec:
parentRefs:
- name: shared-gateway
namespace: infra-ns
rules:
- matches:
- path:
value: /store
backendRefs:
- name: store
port: 8080这条 Route 的路由逻辑很直接:匹配 /store 路径下的流量,并把它发到 store Service。
site 团队接着部署他们应用的 Route。他们在 site-ns 命名空间下部署两条 HTTPRoute:
- home HTTPRoute 充当默认路由规则:匹配
foo.example.com/*路径下、未被已有规则匹配的流量,把它发到 home Service。 - login HTTPRoute 把
foo.example.com/login路径下的流量路由到service/login-v1和service/login-v2。它使用权重来精细地控制两者之间的流量分配比例。
这两条 Route 共享同一份 Gateway 附加配置——它们都把 infra-ns 命名空间下的 shared-gateway 列为唯一想附加到的 Gateway。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: home
namespace: site-ns
spec:
parentRefs:
- name: shared-gateway
namespace: infra-ns
rules:
- backendRefs:
- name: home
port: 8080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: login
namespace: site-ns
spec:
parentRefs:
- name: shared-gateway
namespace: infra-ns
rules:
- matches:
- path:
value: /login
backendRefs:
- name: login-v1
port: 8080
weight: 90
- name: login-v2
port: 8080
weight: 10这三条 Route 部署完成后,它们都会挂到 shared-gateway 上。Gateway 会把这些 Route 合并成一份扁平的路由规则列表。这些路由规则之间的路由优先级 由"最具体的匹配"决定;冲突则按冲突解决规则处理。这样,不同所有者之间的路由规则合并就可预测、可推导。
正是有了跨命名空间路由,Foobar 公司才能更均衡地分担基础设施的所有权,同时又保留了集中管控能力——两全其美,且全部通过声明式、开放的 API 交付。