Skip to content

Gateway API 实施者指南(Implementer's Guide)

原文:https://gateway-api.sigs.k8s.io/guides/implementers-guide/

关于构建一个 Gateway API 实现,你知道的所有事情——只是一直没敢问。

本文档是收集各种"如何编写一个 Gateway API 实现"小贴士和技巧的地方——这些内容在底层类型的 godoc 字段没有合适的位置可放。

意在记录一些指导原则,帮助本 API 的实施者避免常见的错误。

如果你打算作为最终用户使用本 API(而不是基于构建东西),本文档可能对你帮助不大

这是一份文档——如果你发现漏掉了什么,欢迎提 PR!

关于 Gateway API,需要记住的重要事项

希望这些内容大多人来说不奇怪,但它们一些并不明显的含义,我们在这里一一列出。

Gateway API 是一个 kubernetes.io API

Gateway API 使用 gateway.networking.k8s.io API group。这意味着——与核心 Kubernetes 二进制提供的 API 一样——每次发布时,这些 API 已经过上游 Kubernetes 评审者的 review,核心二进制中提供**的 API 一样

Gateway API 通过 CRD 交付

Gateway API 以一组 CRD 的形式提供版本受我们的版本策略控制

该版本策略中重要的部分看起来同一个对象(即 groupversionkind 都相同)的资源,模式可能略有不同。我们以兼容的方式进行变更,因此事情通常"就是会工作"。但实现需要一些动作才能让"就是会工作"更可靠——这些见下

这种"基于 CRD 交付"的模型意味着:如果实现尝试在 CRD 尚未安装使用(即 get、list、watch 等)Gateway API 对象,那么你的 Kubernetes 客户端代码很可能会返回严重的错误。处理这种情况的技巧见下

Gateway API 对象的 CRD 定义包含两个特定的注解:

  • gateway.networking.k8s.io/bundle-version: <semver-release-version>
  • gateway.networking.k8s.io/channel: <channel-name>

"bundle version"和"channel"("发布通道"的简写)的概念版本化文档中解释。

实现可以用它们判断集群中是否安装以及安装了哪个 schema 版本。

Standard 通道 CRD 的变更保持向后兼容

Standard 通道 CRD 的合约的一部分是:同一个 API 版本内的变更必须兼容的注意:属于 Experimental 通道的 CRD 提供任何向后兼容性保证。

虽然 Gateway API 版本策略总体上与上游 Kubernetes API 保持一致允许"对校验的修正"。例如,如果 API 规范声明某个值无效、相应的校验覆盖到该情况,那么未来的发布可能新增校验来阻止这种无效输入。

这条合约意味着:实现在 API 版本比编写时所基于的版本更新不会失败,因为由 Kubernetes 存储的较新 schema 一定能够序列化为实现代码中使用的旧**版本。

类似地,如果实现基于一个更高的版本编写,那么能够理解的字段值将永远不会被使用——因为它们并不存在于较旧的版本中。

实现规则与指导

CRD 管理

关于如何管理 Gateway API CRD 的信息——包括何时把 CRD 安装你的实现打包在一起可接受的——请参考我们的 CRD 管理指南

一致性与版本兼容性

一个合规的 Gateway API 实现是指通过了每个 Gateway API bundle 版本发布中随附一致性测试的实现。

一个实现必须****没有任何跳过测试通过一致性套件合规。开发过程中可以跳过测试,想要声明为合规的版本必须不跳过任何测试。

扩展特性可以根据 Extended 状态的合约关闭。

Gateway API 一致性版本强相关。一个对 N 版本通过一致性测试的实现未必对 N+1 版本通过一致性测试,除非相应变更。

实现应当把一致性测试套件生成的报告提交回 Gateway API 的 GitHub 仓库,详细记录他们的测试情况。

一致性套件的输出中包含所支持的 Gateway API 版本。

Union feature 一致性

部分特性只有在其他特性组合实现时有意义。我们这种行为称为 union feature —— 一种辅助地作用于任何与之表面兼容其他特性的特性。

例如,BackendTLSPolicy 只有在会把流量转发到后端的 route filter 组合有用——TLS 终止的 route、带后端引用的 route、或 RequestMirror 这样针对后端 Service 的 filter。类似地,ReferenceGrant 只有在进行跨命名空间引用的资源组合有意义(跨命名空间引用后端的 route、跨命名空间引用** Secret 的 Gateway 等)。

决定支持一个 union feature 的实现,只要该组合适用,就必须支持它与报告每个其他特性的组合——无论那些特性处于 Core 还是 Extended 支持级别。

下面这些例子可以参考

  • 一个报告支持 BackendTLSPolicy GRPCRoute 的实现必须支持:在一 GRPCRoute使用 BackendTLSPolicy针对一个 Service。
  • 一个报告支持 BackendTLSPolicy TLSRouteTerminate 的实现必须支持:在使用 mode=Terminate listener 的 TLSRoute 中,使用 BackendTLSPolicy针对一个 Service。
  • 一个报告支持 BackendTLSPolicy HTTPRouteRequestMirror 的实现必须支持:当一条 route 定义了 httproute.spec.rules[].filters.requestMirror.backendRef 为 Service 类型时,使用 BackendTLSPolicy

依赖 Union feature 行为的特性必须在它们的 Enhancement Proposal 以及 API Reference 中明确说明。

发现 union feature 支持情况

实现通过 GatewayClass.status.supportedFeatures 字段报告它们支持的特性。这是实施者用户****了解实现支持哪些特性(从而支持哪些特性组合)的主要机制。

一致性测试套件通过其多特性测试门控自动校验 union feature 组合。每个一致性测试会声明要求的特性集。只有当实现声明支持测试所列全部特性时,测试才会运行。这意味着

  • 如果实现声明同时支持 BackendTLSPolicy GRPCRoute那么那些要求这两个特性的测试自动运行——如果这些测试失败说明该实现实际上并不支持该组合。
  • 如果实现声明支持 BackendTLSPolicy没有 GRPCRoute那么这些组合测试会被跳过。
  • 一致性报告的输出按 profile 展示 supportedFeatures unsupportedFeatures用户可以验证哪些组合校验过。

简而言之,一致性套件"声称支持某个 union feature 组合"但实际上"通过其校验测试"变得不可能。

Union features 与一致性 profile

Union features 必须列入****每个该组合适用的一致性 profile 的 ExtendedFeatures 集合中。这确保了一个实现某个 profile 运行一致性测试并声明一个 union feature profile 的组合测试会被执行。

例如,BackendTLSPolicy 适用于任何涉及把流量通过 TLS 转发到后端的 profile。它因此****必须出现在 GATEWAY-HTTPGATEWAY-GRPC 以及 GATEWAY-TLS profile extended features ——而不仅仅是某一个 profile。

Union features 可以处于不同的支持级别:

  • Core union featuresReferenceGrant所有 Gateway profile 都是 Core 特性。只要该 profile 被声明,对跨命名空间引用的支持就是强制的。因为它是 Core,所以实现该 profile 不能选择不支持它
  • Extended union featuresBackendTLSPolicy 是一个 Extended 特性。实现可以选择支持它,如果它们支持了,必须它们报告的所有适用的特性组合中都**支持它。

版本兼容性

一旦 v1.0 发布,对于支持 Gateway GatewayClass 实现,它们必须设置一个新的 Condition:SupportedVersionstatus: true 表示所安装的 CRD 版本支持,status: false 表示支持。

标准 Status 字段与 Conditions

Gateway API 很多资源,但在设计时,我们一直努力不同对象的 status 体验尽可能保持一致——方法是使用 Condition 类型 status.conditions 字段。

大多数资源都有 status.conditions 字段,部分资源有一个命名空间字段,其中包含一个 conditions 字段。

对于后者,Gateway 的 status.listeners Route 的 status.parents 字段典型的例子——slice 中每个元素某部分配置相关联的 Conditions。

对于 Gateway 来说,这样做为了支持每个 Listener 一组 Conditions;对于 Route 来说,这样做为了支持每个实现一组 Conditions(因为 Route 对象可能多个 Gateway 使用,而这些 Gateway 又可能不同的实现协调)。

所有这些情况中,都有一些相对通用的 Condition 类型,它们的含义比较相似:

  • Accepted:资源或其部分包含可接受的配置,在实现控制的底层数据平面中产生一些配置。意味着整个配置是有效的,说明足够的部分是有效的,产生某种**效果。
  • Programmed表示一个晚于 Accepted 阶段——资源或其部分****被 Accepted 并被配置底层数据平面之后。用户应当预期配置很快就会准备好流量。该 Condition 设置意味着数据平面已就绪说明一切都是有效的,很快就会就绪**。"很快"不同实现中可能含义不同
  • ResolvedRefs:该 Condition 表示资源部分****中所有引用都****有效且****指向的对象存在也****允许该引用。如果该 Condition 设置为 status: false那么资源或其部分至少有一个引用某种原因无效**, message 字段****应当指明一个无效的。

实施者应当检查每种类型的 godoc,了解这些 Condition 在每个资源(或其部分)上的具体细节。

此外,上游的 Conditions 结构体包含一个可选observedGeneration 字段——实现必须使用该字段,并在生成 status 把它设置为对象的 metadata.generation 字段。这 API 的使用者可以判断 status 是否当前版本的对象仍然相关**。

TLS

TLS 在 Gateway API 中是一个大主题,相关能力还在持续扩展。这里有一份 TLS 指南——用户视角深入地覆盖了这个话题;本节试图实施者视角补齐一些空白**。

Listener 隔离(Listener Isolation)

在 Gateway 内部,TLS 配置目前 Listener 绑定。为了让这种做法可控,我们鼓励****所有实现"完整的 Listener 隔离"这一目标迈进(定义见下):

请求应当至多匹配一个 Listener。例如,如果foo.example.com *.example.com 分别定义了 Listener,那么foo.example.com 的请求应当****只用**附加到 foo.example.com Listener 上的 Route 来路由(而****不是 *.example.com Listener 上的 Route)。

支持 Listener 隔离的实现必须****清楚地在文档中说明这一点。未来,我们计划新增 HTTPS Listener 隔离一致性测试,保证这一行为声明支持该特性的实现之间保持一致**。最新进展 #2803

间接配置

多种情况下,TLS 证书可能直接由 Gateway 拥有者管理。虽然这里打算穷举性的罗列,记录了一些我们期望看到的、用于通过 Gateway API 管理 TLS 证书的做法

1. 来自其他来源的证书

部分提供方支持把 TLS 证书配置并托管在 Kubernetes 之外能够连接这些外部提供方的实现,可以在 Gateway Listener TLS option 暴露**该能力,例如:

yaml
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      options:
        vendor.example.com/certificate-name: store-example-com

本例中,store-example-com 这个名字指代外部 vendor.example.com TLS 证书提供方存储的一张证书的名称

2. 自动生成的 TLS 证书(稍后填充)

许多用户愿意 TLS 证书自动为他们生成。一种可能的实现有一个控制器监听 Gateway HTTPRoute,自动生成 TLS 证书把它们挂到 Gateway 上。取决于实现细节,Gateway 拥有者****可能需要在 Gateway Listener 层级某些配置,显式开启该特性。例如,假设有人创建了 acme-cert-generator 按此模式生成证书。生成器可以选择那些 tls.options设置了 acme.io/cert-generator整个 Gateway 设置了类似的 annotation) Gateway Listener 上生成填充证书。

注意:这 [Cert Manager 目前的做法] (https://cert-manager.io/docs/usage/gateway/) 非常类似——那需要 Gateway 拥有者引用一张 Kubernetes Secret,由它填充。之所以必须这样因为 Gateway API v1.1 之前,TLS CertificateRefs 强制要求指定

伴随 v1.1 Gateway API 校验的放宽,TLS 证书创建时可以留空,从而使用自动生成 TLS 证书时配置更少

3. 由其他角色指定的证书

部分组织中,应用开发者负责管理 TLS 证书(角色与身份了解更多关于这一点以及其他角色)。

为了支持这种用例,可以创建一个的控制器和 CRD。该 CRD 主机名用户提供的证书关联起来,然后该控制器该 CRD 指定的证书填充到那些主机名匹配的 Gateway listener 。这可能得益于** Listener Gateway 级别显式开启机制。

TLS 扩展的总体指导

在 Gateway API 之上构建 TLS 扩展时,务必遵循以下指导:

  1. 任何 TLS Option Annotation,使用带有域名前缀你的实现唯一的名称。(例如,使用 example.com/certificate-name,而只是 certificate-name)。
  2. 要把证书等敏感信息编码进 option annotation 的值中。改为通过简洁易懂引用名称**。虽然这些值技术上可以长达 253 字符,我们强烈建议把值控制在 50 字符以内**,保持整体可读性 UX。
  3. 为了支持这些扩展,Gateway API v1.1+ 不再要求在 Gateway Listener 指定** TLS 配置。当一个 Gateway Listener 指定足够的 TLS 配置时,实现****必须把该 Listener Programmed Condition 设置为 False理由InvalidTLSConfig
  4. 无论选择支持哪些扩展,很重要的一点是**:支持****核心的 TLS 配置——这些核心配置所有实现间保持可移植性。扩展在 API 有其位置但****所有实现仍****必须支持** API 的****核心能力。

资源细节

每个当前可用的一致性 profile,有一组资源实现应当去协调的。

下面一节逐个每种 Gateway API 对象,说明期望的行为。

GatewayClass

GatewayClass 只有一个主要的 spec 字段——controllerName个实现期望申领一个带域名前缀的字符串值(如 example.com/example-ingress作为它的 controllerName

实现必须监听所有 GatewayClass,协调那些 controllerName 匹配的 GatewayClass。该实现必须这些 controllerName 匹配的 GatewayClass 集合至少选择一个兼容的 GatewayClass,通过该 GatewayClass 设置 Accepted Condition 为 status: true表示接受**该 GatewayClass 的处理任何 controllerName 匹配没有被 Accepted GatewayClass 必须Accepted Condition 设置为 status: false

实现可以一组本可接受 GatewayClass 挑出一个如果只能协调一个),或者——如果能力协调多个 GatewayClass——可以按需挑出任意数量

如果某个 GatewayClass 使其不兼容的内容(本文撰写唯一可能原因存在一个指向实现不支持paramsRef 对象的指针),那么该实现应当兼容的 GatewayClass 标记 Accepted

Gateway

Gateway 对象必须****在 spec.gatewayClassName 字段中引用一个存在某个实现 Accepted GatewayClass,实现会去协调它。

超出协调范围的 Gateway 对象(例如,因为它们引用的 GatewayClass 删除),实现可以作为删除流程的一部分清除它们的 status,不是****必须**的。

Routes

所有 Route 对象一些共性**:

  • 它们必须****被附加到一个在范围内的父级,实现认为它们协调。
  • 实现必须在范围内的 Route 更新 status(使用带命名空间的 parents 字段)。详见各 Route 类型的具体说明,通常包括 AcceptedProgrammed 以及 ResolvedRefs Conditions。
  • 超出范围的 Route 应当****不更新** status,因为这种更新可能覆盖任何拥有者observedGeneration 字段表明剩余的 status 过时的。

HTTPRoute

HTTPRoute 路由的是未加密的、被检视 HTTP 流量。包括 Gateway 处终止的 HTTPS 流量(因为被解密),从而让 HTTPRoute 能够使用 path、method headers HTTP 属性路由决策。

TLSRoute

TLSRoute 使用 SNI 加密的 TLS 流量路由——解密该流量流——对应**的后端。

TCPRoute

TCPRoute 到达 Listener TCP 流路由给定后端之一。

UDPRoute

UDPRoute 到达 Listener UDP 包路由给定后端之一。

ReferenceGrant

ReferenceGrant 是一种特殊资源,目标资源所在命名空间拥有者使用,用以****有选择地允许来自其他命名空间的 Gateway API 对象的**引用。

ReferenceGrant 创建在授权被引用资源所在那个命名空间内,从而允许来自其他命名空间、其他 Kind 两者的**访问。

支持跨命名空间引用的实现必须****监听 ReferenceGrant,协调任何指向某个对象的 ReferenceGrant——对象某个在范围内的 Gateway API 对象所引用**。

基于 MIT 协议发布