跳至内容

实施指南

您想知道关于构建网关 API 实现的任何事项,但又不敢问。

本文档旨在收集编写网关 API 实现的技巧和窍门,这些技巧和窍门在基础类型的 godoc 字段中没有直接的位置。

它还旨在记录一些指南,以帮助此 API 的实施者避免常见的错误。

如果您打算使用此 API 作为最终用户,而不是构建使用它的东西,那么它可能不太相关。

这是一份活文档,如果您发现缺少任何内容,欢迎提交 PR!

关于网关 API 的重要事项

希望这些内容大部分都不令人惊讶,但它们有时会有一些非显而易见的含义,我们将在本文档中阐述。

网关 API 是一个kubernetes.io API

网关 API 使用gateway.networking.k8s.io API 组。这意味着,与在核心 Kubernetes 二进制文件中提供的 API 一样,每次发布时,这些 API 都会由上游 Kubernetes 审核人员审查,就像在核心二进制文件中提供的 API 一样。

网关 API 使用 CRD 提供

网关 API 作为一组 CRD 提供,使用我们的版本控制策略进行版本控制。

该版本控制策略中最重要的一部分是,看似相同的对象(即具有相同的groupversionkind)可能具有略微不同的架构。我们以兼容的方式进行更改,因此事物通常会“正常工作”,但实现需要采取一些措施才能使“正常工作”更可靠;这些将在下面详细说明。

基于 CRD 的交付还意味着,如果实现尝试在安装 CRD 时使用(即获取、列出、监视等)网关 API 对象,那么您的 Kubernetes 客户端代码可能会返回严重错误。下面将详细介绍如何处理这些问题。

网关 API 对象的 CRD 定义都包含两个特定的注释

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

“捆绑版本”和“通道”(即“发布通道”的简称)的概念在我们的版本控制文档中进行了说明。

实现可以使用这些注释来确定集群中安装了哪些架构版本(如果有)。

对标准通道 CRD 的更改向后兼容

标准通道 CRD 的契约的一部分是,在一个 API 版本内的更改必须兼容。请注意,属于实验通道的 CRD 不提供任何向后兼容性保证。

尽管网关 API 版本控制策略在很大程度上与上游 Kubernetes API 一致,但它确实允许进行“验证修正”。例如,如果 API 规范指出某个值无效,但相应的验证没有涵盖该值,那么将来版本可能会添加验证以防止出现此无效输入。

此契约还意味着,实现不会因使用比编写时版本更高的 API 版本而失败,因为 Kubernetes 中存储的较新架构肯定能够被序列化为实现代码中使用的较旧版本。

类似地,如果实现使用的是更高版本,那么它所理解的较新值将永远不会被使用,因为它们不存在于较旧版本中。

实施规则和指南

CRD 管理

有关如何管理网关 API CRD 的信息,包括何时可以将 CRD 安装与您的实现捆绑在一起,请参阅我们的CRD 管理指南.

一致性和版本兼容性

符合规范的网关 API 实现是指通过每个网关 API 捆绑版本发布中包含的一致性测试的实现。

实现必须通过一致性套件跳过测试才能符合规范。在开发期间可以跳过测试,但您要使其符合规范的版本必须没有跳过测试。

扩展功能可以根据扩展状态的契约被禁用。

网关 API 一致性是版本特定的。通过版本 N 的一致性测试的实现,如果没有更改,可能无法通过版本 N+1 的一致性测试。

实现应该将一致性测试套件的报告提交到网关 API Github 仓库,其中包含其测试的详细信息。

一致性套件输出包括支持的网关 API 版本。

版本兼容性

一旦发布 v1.0,支持网关和 GatewayClass 的实现必须设置一个新的条件SupportedVersion,其中status: true 表示安装的 CRD 版本受支持,status: false 表示不受支持。

标准状态字段和条件

网关 API 有许多资源,但在设计时,我们已努力使状态体验在各个对象之间尽可能一致,使用 Condition 类型和status.conditions 字段。

大多数资源都有status.conditions 字段,但有些资源也具有包含conditions 字段的命名空间字段。

对于后者,网关的status.listeners 和路由的status.parents 字段是示例,其中切片中的每个项目都标识与某些配置子集关联的条件。

对于网关情况,这是为了允许每个监听器的条件,而在路由情况下,这是为了允许每个实现的条件(因为路由对象可以在多个网关中使用,并且这些网关可以由不同的实现协调)。

在所有这些情况下,有一些相对常见的条件类型具有相似的含义

  • Accepted - 资源或其部分包含可接受的配置,该配置将在实现控制的底层数据平面中生成一些配置。这并不意味着整个配置都是有效的,而只是足够有效以产生一些效果。
  • Programmed - 这表示操作的后期阶段,在Accepted之后,当资源或其部分已被接受并编程到底层数据平面中时。用户应期望配置在不久的将来某个时刻准备好进行流量流动。此条件表示数据平面在设置时已准备好,而只是表示一切都是有效的,并且它很快就会准备好。“很快”可能根据实现有不同的含义。
  • ResolvedRefs - 此条件指示资源或其部分中的所有引用都是有效的,并且指向一个既存在又允许该引用的对象。如果此条件设置为status: false,则资源或其部分中的至少一个引用由于某种原因无效,并且message字段应指示哪些无效。

实施者应检查每种类型的 godoc 以查看这些条件在每个资源或其部分上的确切详细信息。

此外,上游Conditions 结构体包含一个可选的observedGeneration 字段 - 实现必须使用此字段并将其设置为生成状态时对象的metadata.generation 字段。这允许 API 用户确定状态是否与对象的当前版本相关。

TLS

TLS 是网关 API 中的一个大话题,其功能集不断扩展。有一个TLS 指南从面向用户的角度更深入地介绍了这个话题,但本节尝试从实施者的角度填补一些空白。

监听器隔离

在网关内,TLS 配置目前专门绑定到监听器。为了使这种方法易于管理,我们鼓励所有实现朝着提供如下定义的完全“监听器隔离”的目标努力

请求应该最多匹配一个监听器。例如,如果为“foo.example.com”和“*.example.com”定义了监听器,则对“foo.example.com”的请求应该只使用附加到“foo.example.com”监听器的路由(而不是“*.example.com”监听器)。

不支持监听器隔离的实现必须明确记录这一点。将来,我们计划添加 HTTPS 监听器隔离一致性测试,以确保声称支持该功能的实现之间这种行为一致。请参阅#2803了解有关这些测试的最新更新。

间接配置

在许多情况下,TLS 证书可能不会由网关所有者直接管理。虽然这并非旨在成为一个详尽无遗的清单,但它记录了一些我们期望看到用于使用网关 API 管理 TLS 证书的方法。

1. 来自其他地方的证书

一些提供商提供在 Kubernetes 之外完全配置和托管 TLS 证书的能力。能够连接到这些外部提供商的实现可以通过网关监听器上的 TLS 选项公开这种能力,例如

  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 证书能够代表他们自动生成。一种潜在的实现方法将涉及一个控制器,该控制器监视网关和 HTTPRoutes,生成 TLS 证书并将它们附加到网关。根据实现细节,网关所有者可能需要在网关或监听器级别配置一些内容以明确选择加入此功能。例如,假设有人创建了acme-cert-generator 来按照这种模式生成证书。该生成器可以选择仅在tls.options 中设置了acme.io/cert-generator 或为整个网关设置了类似注释的网关监听器上生成和填充证书。

请注意,这实际上与Cert Manager 今天的运作方式非常相似,但这要求网关所有者引用一个 Kubernetes Secret,然后由它来填充。这种特定方法是必需的,因为在网关 API v1.1 之前,要求指定 TLS CertificateRefs。

随着网关 API v1.1 中验证的放宽,TLS 证书可以在创建时留空,从而在使用生成的 TLS 证书时允许更少的配置。

3. 由其他角色指定证书

在某些组织中,应用程序开发人员负责管理 TLS 证书(请参阅角色和角色以了解有关此角色和其他角色的更多信息)。

为了启用此用例,将创建一个新的控制器和 CRD。此 CRD 将主机名链接到用户提供的证书,然后控制器将在与这些主机名匹配的网关监听器上填充由该 CRD 指定的证书。这可能也从监听器或网关级别的行为选择加入中受益。

TLS 扩展的总体指南

在网关 API 之上构建 TLS 扩展时,遵循以下指南非常重要

  1. 为任何 TLS 选项或注释使用以域为前缀的名称,这些名称对您的实现是唯一的。(例如,使用example.com/certificate-name 而不是只使用certificate-name)。
  2. 不要在选项或注释值中编码敏感信息,例如证书。相反,应通过易于理解的简洁名称来引用。虽然这些值在技术上可以长达 253 个字符,但我们强烈建议将值保持在 50 个字符以下,以保持整体可读性和用户体验。
  3. 为了启用这些扩展,网关 API v1.1+ 将不再要求在网关监听器上指定 TLS 配置。当网关监听器没有指定足够的 TLS 配置时,实现必须将该监听器的Programmed 条件设置为False,原因是InvalidTLSConfig
  4. 无论您选择支持哪些扩展,重要的是要支持旨在跨所有实现移植的核心 TLS 配置。扩展在该 API 中有其位置,但所有实现都必须仍然支持 API 的核心功能。

资源详细信息

对于每个当前可用的符合性配置文件,都有一组资源,预计实现将协调这些资源。

以下部分将介绍每个网关 API 对象,并指示预期行为。

网关类

网关类有一个主要的spec 字段 - controllerName。每个实现都应声明一个以域为前缀的字符串值(例如example.com/example-ingress)作为其controllerName

实现必须监视所有网关类,并协调具有匹配controllerName 的网关类。该实现必须从具有匹配controllerName 的网关类集中选择至少一个兼容的网关类,并通过在每个网关类中将Accepted 条件设置为status: true 来表明它接受处理该网关类。任何具有匹配controllerName被接受的网关类必须将其Accepted 条件设置为status: false

实现可以从其他可接受的网关类池中选择一个网关类,如果它们只能协调一个网关类,或者,如果它们能够协调多个网关类,它们也可以根据需要选择多个网关类。

如果网关类中的某些内容使其不兼容(在撰写本文时,唯一可能的原因是存在指向paramsRef 对象的指针,该对象不受实现支持),则实现应将不兼容的网关类标记为未被Accepted

网关

网关对象必须在spec.gatewayClassName 字段中引用一个存在的网关类,并且该网关类已被实现接受,以便该实现协调它们。

超出范围的网关对象(例如,因为它们引用的网关类已被删除)可能在删除过程中由实现将其状态删除,但这并非必需。

路由

所有路由对象共享一些属性

  • 它们必须附加到一个在范围内父对象,以便实现将其视为可协调的。
  • 该实现必须使用命名空间的parents 字段更新每个在范围内的路由的状态,其中包含相关的条件。请参阅特定路由类型的详细信息,但这通常包括AcceptedProgrammedResolvedRefs 条件。
  • 超出范围的路由不应该更新状态,因为这些更新可能会覆盖任何新的所有者。observedGeneration 字段将指示任何剩余状态已过时。

HTTPRoute

HTTPRoutes 路由未加密且可供检查的 HTTP 流量。这包括在网关处终止的 HTTPS 流量(因为它随后被解密),并允许 HTTPRoute 在其路由指令中使用 HTTP 属性,例如路径、方法或标头。

TLSRoute

TLSRoutes 使用 SNI 标头路由加密的 TLS 流量,不解密流量流,而是路由到相关的后端。

TCPRoute

TCPRoutes 将到达监听器的 TCP 流路由到给定的后端之一。

UDPRoute

UDPRoutes 将到达监听器的 UDP 数据包路由到给定的后端之一。

引用授权

ReferenceGrant 是一种特殊的资源,由一个命名空间中的资源所有者使用来选择性地允许来自其他命名空间中网关 API 对象的引用。

ReferenceGrant 在与它授权引用访问的资源相同的命名空间中创建,并允许来自其他命名空间、其他种类或两者的访问。

支持跨命名空间引用的实现必须监视 ReferenceGrant 并协调任何指向由范围内的网关 API 对象引用的对象的 ReferenceGrant。