--- title: Kubernetes API 概念 content_type: concept weight: 20 --- 本页描述 Kubernetes API 的通用概念。 Kubernetes API 是基于资源的(RESTful)、通过 HTTP 提供的编程接口。 API 支持通过标准的 HTTP 动词(POST、PUT、PATCH、DELETE 和 GET) 检视、创建、更新和删除主要资源,为很多允许细粒度权限控制的对象提供子资源 (如将 Pod 绑定到节点上),并且出于便利性或效率考虑,支持并提供这些资源的 不同表示形式。 Kubernetes API 还通过 "watch" 和一致性的列表支持高效的资源变更通知, 从而允许其他组件对资源的状态进行高效的缓存和同步。 ## 标准 API 术语 {#standard-api-terminology} 大多数 Kubernetes API 资源类型都是 [对象](/zh/docs/concepts/overview/working-with-objects/kubernetes-objects/#kubernetes-objects): 它们代表的是集群中某一概念的具体实例,例如一个 Pod 或名字空间。 为数不多的几个 API 资源类型是“虚拟的” - 它们通常代表的是操作而非对象本身, 例如访问权限检查(使用 POST 请求发送一个 JSON 编码的 `SubjectAccessReview` 负载到 `subjectaccessreviews` 资源)。 所有对象都有一个唯一的名字,以便支持幂等的创建和检视操作,不过如果虚拟资源类型 不可检视或者不要求幂等,可以不具有唯一的名字。 Kubernetes 一般会利用标准的 RESTful 术语来描述 API 概念: * **资源类型(Resource Type)** 是在 URL 中使用的名称(`pods`、`namespaces`、`services`) * 所有资源类型都有具有一个 JSON 形式(其对象的模式定义)的具体表示,称作**类别(Kind)** * 某资源类型的实例的列表称作 **集合(Collection)** * 资源类型的单个实例被称作 **资源(Resource)** 所有资源类型要么是集群作用域的(`/apis/GROUP/VERSION/*`),要么是名字空间 作用域的(`/apis/GROUP/VERSION/namespaces/NAMESPACE/*`)。 名字空间作用域的资源类型会在其名字空间被删除时也被删除,并且对该资源类型的 访问是由定义在名字空间域中的授权检查来控制的。 下列路径用来检视集合和资源: * 集群作用域的资源: * `GET /apis/GROUP/VERSION/RESOURCETYPE` - 返回指定资源类型的资源的集合 * `GET /apis/GROUP/VERSION/RESOURCETYPE/NAME` - 返回指定资源类型下名称为 NAME 的资源 * 名字空间作用域的资源: * `GET /apis/GROUP/VERSION/RESOURCETYPE` - 返回所有名字空间中指定资源类型的全部实例的集合 * `GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE` - 返回名字空间 NAMESPACE 内给定资源类型的全部实例的集合 * `GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME` - 返回名字空间 NAMESPACE 中给定资源类型的名称为 NAME 的实例 由于名字空间本身是一个集群作用域的资源类型,你可以通过 `GET /api/v1/namespaces/` 检视所有名字空间的列表,使用 `GET /api/v1/namespaces/NAME` 查看特定名字空间的 详细信息。 几乎所有对象资源类型都支持标准的 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。 Kubernetes 使用术语 **list** 来描述返回资源集合的操作,以便与返回单个资源的、 通常称作 **get** 的操作相区分。 某些资源类型有一个或多个子资源(Sub-resource),表现为对应资源下面的子路径: * 集群作用域的子资源:`GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE` * 名字空间作用域的子资源:`GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE` 取决于对象是什么,每个子资源所支持的动词有所不同 - 参见 API 文档以了解更多信息。 跨多个资源来访问其子资源是不可能的 - 如果需要这一能力,则通常意味着需要一种 新的虚拟资源类型了。 ## 高效检测变更 {#efficient-detection-of-changes} 为了使客户端能够构造一个模型来表达集群的当前状态,所有 Kubernetes 对象资源类型 都需要支持一致的列表和一个称作 **watch** 的增量变更通知信源(feed)。 每个 Kubernetes 对象都有一个 `resourceVersion` 字段,代表该资源在下层数据库中 存储的版本。检视资源集合(名字空间作用域或集群作用域)时,服务器返回的响应 中会包含 `resourceVersion` 值,可用来向服务器发起 watch 请求。 服务器会返回所提供的 `resourceVersion` 之后发生的所有变更(创建、删除和更新)。 这使得客户端能够取回当前的状态并监视其变更,且不会错过任何变更事件。 客户端的监视连接被断开时,可以从最后返回的 `resourceVersion` 重启新的监视连接, 或者执行一个新的集合请求之后从头开始监视操作。 参阅[资源版本语义](#resource-versions)以了解更多细节。 例如: 1. 列举给定名字空间中的所有 Pods: ```console GET /api/v1/namespaces/test/pods --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": {"resourceVersion":"10245"}, "items": [...] } ``` 2. 从资源版本 10245 开始,以 JSON 对象的形式接收所有创建、删除或更新操作的通知: ```console GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245 --- 200 OK Transfer-Encoding: chunked Content-Type: application/json { "type": "ADDED", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...} } { "type": "MODIFIED", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "11020", ...}, ...} } ... ``` 给定的 Kubernetes 服务器只会保留一定的时间内发生的历史变更列表。 使用 etcd3 的集群默认保存过去 5 分钟内发生的变更。 当所请求的 watch 操作因为资源的历史版本不存在而失败,客户端必须能够处理 因此而返回的状态代码 `410 Gone`,清空其本地的缓存,重新执行 list 操作, 并基于新的 list 操作所返回的 `resourceVersion` 来开始新的 watch 操作。 大多数客户端库都能够提供某种形式的、包含此逻辑的工具。 (在 Go 语言客户端库中,这一设施称作 `Reflector`,位于 `k8s.io/client-go/cache` 包中。) ### 监视书签 {#Watch-bookmark} 为了处理历史窗口过短的问题,我们引入了 `bookmark(书签)` 监视事件的概念。 该事件是一种特殊事件,用来标示客户端所请求的、指定的 `resourceVersion` 之前 的所有变更都以被发送。该事件中返回的对象是所请求的资源类型,但其中仅包含 `resourceVersion` 字段,例如: ```console GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245&allowWatchBookmarks=true --- 200 OK Transfer-Encoding: chunked Content-Type: application/json { "type": "ADDED", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...} } ... { "type": "BOOKMARK", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "12746"} } } ``` 通过在 watch 请求中设置 `allowWatchBookmarks=true` 选项,可以请求 `bookmark` 事件,但是客户端不能假定服务器端会按某特定时间间隔返回书签事件,甚至也不能 假定服务器一定会发送 `bookmark` 事件。 ## 分块检视大体量结果 {#retrieving-large-results-sets-in-chunks} {{< feature-state for_k8s_version="v1.9" state="beta" >}} 在较大规模的集群中,检视某些资源类型的集合时可能会返回较大体量的响应数据,对 服务器和客户端都会造成影响。例如,某集群可能包含数万个 Pod,每个 Pod 的 JSON 编码都有 1-2 KB 的大小。返回所有名字空间的全部 Pod 时,其结果可能体量很大 (10-20 MB)且耗用大量的服务器资源。 从 Kubernetes 1.9 开始,服务器支持将单一的大体量集合请求分解成多个小数据块 同时还保证整个请求的一致性的能力。 各个数据块可以按顺序返回,进而降低请求的尺寸,允许面向用户的客户端以增量形式 呈现返回结果,改进系统响应效果。 为了用分块的形式返回一个列表,集合请求上可以设置两个新的参数 `limit` 和 `continue`,并且所有 list 操作的返回结果列表的 `metadata` 字段中会包含一个 新的 `continue` 字段。 客户端应该将 `limit` 设置为希望在每个数据块中收到的结果个数上限,而服务器则 会在结果中至多返回 `limit` 个资源并在集合中还有更多资源的时候包含一个 `continue` 值。客户端在下次请求时则可以将此 `continue` 值传递给服务器, 告知后者要从何处开始返回结果的下一个数据块。 通过重复这一操作直到服务器端返回空的 `continue` 值,客户端可以受到结果的 全集。 与 watch 操作类似,`continue` 令牌也会在很短的时间(默认为 5 分钟)内过期, 并在无法返回更多结果时返回 `410 Gone` 代码。 这时,客户端需要从头开始执行上述检视操作或者忽略 `limit` 参数。 例如,如果集群上有 1253 个 Pods,客户端希望每次收到包含至多 500 个 Pod 的 数据块,它应按下面的步骤来请求数据块: 1. 列举集群中所有 Pod,每次接收至多 500 个 Pods: ```console GET /api/v1/pods?limit=500 --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion":"10245", "continue": "ENCODED_CONTINUE_TOKEN", ... }, "items": [...] // returns pods 1-500 } ``` 2. 继续前面的调用,返回下一组 500 个 Pods: ```console GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion":"10245", "continue": "ENCODED_CONTINUE_TOKEN_2", ... }, "items": [...] // returns pods 501-1000 } ``` 3. 继续前面的调用,返回最后 253 个 Pods: ```console GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN_2 --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion":"10245", "continue": "", // continue token is empty because we have reached the end of the list ... }, "items": [...] // returns pods 1001-1253 } ``` 注意 list 操作的 `resourceVersion` 在每个请求中都设置的是同一个数值, 这表明服务器要向我们展示一个一致的 Pods 快照视图。 在版本 `10245` 之后创建、更新或删除的 Pods 都不会显示出来,除非用户发出 list 请求时不指定 `continue` 令牌。 这一设计使得客户端能够将较大的响应切分为较小的数据块,且能够对较大的集合 执行监视动作而不会错失任何更新事件。 ## 以表格形式接收资源 {#receiving-resources-as-tables} `kubectl get` 命令的输出是一个包含一个或多个资源的简单表格形式。 过去,客户端需要重复 `kubectl` 中所实现的表格输出和描述输出逻辑,以执行 简单的对象列表操作。 这一方法在处理某些对象时,需要引入不容忽视的逻辑。 此外,[API 聚合](/zh/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/) 和[定制资源](/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/) 所提供的资源类型都是编译时不可预知的。这意味着,客户端必须针对无法 识别的类型提供通用的实现逻辑。 为了避免上述各种潜在的局限性,客户端可以请求服务器端返回对象的表格(Table) 表现形式,从而将打印输出的特定细节委托给服务器。 Kubernetes API 实现标准的 HTTP 内容类型(Content Type)协商:为 `GET` 调用 传入一个值为 `application/json;as=Table;g=meta.k8s.io;v=v1beta1` 的 `Accept` 头部即可请求服务器以 Table 的内容类型返回对象。 例如,以 Table 格式列举集群中所有 Pods: ```console GET /api/v1/pods Accept: application/json;as=Table;g=meta.k8s.io;v=v1beta1 --- 200 OK Content-Type: application/json { "kind": "Table", "apiVersion": "meta.k8s.io/v1beta1", ... "columnDefinitions": [ ... ] } ``` 对于在服务器上不存在定制的 Table 定义的 API 资源类型而言,服务器会返回 一个默认的 Table 响应,其中包含资源的 `name` 和 `creationTimestamp` 字段。 ```console GET /apis/crd.example.com/v1alpha1/namespaces/default/resources --- 200 OK Content-Type: application/json ... { "kind": "Table", "apiVersion": "meta.k8s.io/v1beta1", ... "columnDefinitions": [ { "name": "Name", "type": "string", ... }, { "name": "Created At", "type": "date", ... } ] } ``` `kube-apiserver` 从 1.10 版本开始提供 Table 响应。 因此,并非所有 API 资源类型都支持 Table 响应,尤其是使用客户端访问较老的集群时。 如果客户端需要能够处理所有资源类型,或者有可能需要与较老的集群交互, 则需要在其 `Accept` 头部设定多个内容类型值,以便可以回退到非表格形式的 JSON 表示。 ``` Accept: application/json;as=Table;g=meta.k8s.io;v=v1beta1, application/json ``` ## 资源的其他表示形式 {#alternate-representations-of-resources} 默认情况下,Kubernetes 返回 JSON 序列化的的对象并设定内容类型为 `application/json`。这是 API 的默认序列化格式。 不过,客户端也可出于大规模环境中更佳性能的需求而请求对象的更为高效的 Protobuf 表现形式。 Kubernetes API 实现了标准的 HTTP 内容类型协商:为 `GET` 调用传递一个 `Accept` 头部来请求服务器以所指定的内容类型返回对象,同时在通过 `PUT` 或 `POST` 调用 向服务器发送 Protobuf 格式的对象时提供 `Content-Type` 头部。 服务器会能够支持所请求的格式时返回 `Content-Type` 头部,并在所提供的内容类型 不合法时返回 `406 Not acceptable(无法接受)` 错误。 请参阅 API 文档了解每个 API 所支持的内容类型。 例如: 1. 以 Protobuf 格式列举集群上的所有 Pods: ```console GET /api/v1/pods Accept: application/vnd.kubernetes.protobuf --- 200 OK Content-Type: application/vnd.kubernetes.protobuf ... binary encoded PodList object ``` 2. 通过向服务器发送 Protobuf 编码的数据创建 Pod,但请求以 JSON 形式接收响应: ```console POST /api/v1/namespaces/test/pods Content-Type: application/vnd.kubernetes.protobuf Accept: application/json ... binary encoded Pod object --- 200 OK Content-Type: application/json { "kind": "Pod", "apiVersion": "v1", ... } ``` 并非所有 API 资源类型都支持 Protobuf,尤其是那些通过定制资源定义(CRD)或通过 API 扩展而加入的资源。如果客户端必须能够处理所有资源类型,则应在其 `Accept` 头部指定多种内容类型以便可以回退到 JSON 格式: ```console Accept: application/vnd.kubernetes.protobuf, application/json ``` ### Protobuf encoding Kubernetes 使用封套形式来对 Protobuf 响应进行编码。 封套外层由 4 个字节的特殊数字开头,便于从磁盘文件或 etcd 中辩识 Protobuf 格式的(而不是 JSON)数据。 接下来存放的是 Protobuf 编码的封套消息,其中描述下层对象的编码和类型,最后 才是对象本身。 封套格式如下: ```console 四个字节的特殊数字前缀: 字节 0-3: "k8s\x00" [0x6b, 0x38, 0x73, 0x00] 使用下面 IDL 来编码的 Protobuf 消息: message Unknown { // typeMeta 应该包含 "kind" 和 "apiVersion" 的字符串值,就像 // 对应的 JSON 对象中所设置的那样 optional TypeMeta typeMeta = 1; // raw 中将保存用 protobuf 序列化的完整对象。 // 参阅客户端库中为指定 kind 所作的 protobuf 定义 optional bytes raw = 2; // contentEncoding 用于 raw 数据的编码格式。未设置此值意味着没有特殊编码。 optional string contentEncoding = 3; // contentType 包含 raw 数据所采用的序列化方法。 // 未设置此值意味着 application/vnd.kubernetes.protobuf,且通常被忽略 optional string contentType = 4; } message TypeMeta { // apiVersion 是 type 对应的组名/版本 optional string apiVersion = 1; // kind 是对象模式定义的名称。此对象应该存在一个 protobuf 定义。 optional string kind = 2; } ``` 收到 `application/vnd.kubernetes.protobuf` 格式响应的客户端在响应与预期的前缀 不匹配时应该拒绝响应,因为将来的版本可能需要以某种不兼容的方式更改序列化格式, 并且这种更改是通过变更前缀完成的。 ## 资源删除 {#resource-deletion} 资源删除要经过两个阶段:1) 终止(finalization),和 2)去除。 ```json { "kind": "ConfigMap", "apiVersion": "v1", "metadata": { "finalizers": {"url.io/neat-finalization", "other-url.io/my-finalizer"}, "deletionTimestamp": nil, } } ``` 当客户端首先删除某资源时,其 `.metadata.deletionTimestamp` 会被设置为当前时间。 一旦 `.metadata.deletionTimestamp` 被设置,则对终结器(finalizers)执行动作 的外部控制器就可以在任何时候、以任何顺序执行其清理工作。 这里不强调顺序是因为很可能带来 `.metadata.finalizers` 被锁定的风险。 `.metadata.finalizers` 是一个共享的字段,任何具有相关权限的主体都可以对其 执行重排序的操作。如果终结器列表要按顺序处理,则很可能导致负责列表中第一个 终结器的组件要等待负责列表中排序靠后的终结器的组件的信号(可能是字段值变更、 外部系统或者其他形式),从而导致死锁行为。 在不对终结器顺序作强制要求的情况下,终结器可以自行排序,且不会因为其在列表 中的顺序而引入任何不稳定因素。 当最后一个终结器也被移除时,资源才真正从 etcd 中移除。 ## 单个资源 API {#single-resource-api} API 动词 GET、CREATE、UPDATE、PATCH、DELETE 和 PROXY 仅支持单个资源。 这些支持单一资源的动词不支持以有序或无序列表甚或事务的形式同时提交给 多个资源。 包括 kubectl 在内的客户端将解析资源的列表,并执行单一资源的 API 请求。 API 动词 LIST 和 WATCH 支持获取多个资源,而 DELETECOLLECTION 支持删除多个 资源。 ## 试运行 {#dry-run} {{< feature-state for_k8s_version="v1.18" state="stable" >}} 修改性质的动词(`POST`、`PUT`、`PATCH` 和 `DELETE`)可以支持 _试运行(dry run)_ 模式的请求。试运行模式可帮助通过典型的请求阶段(准入控制链、合法性 检查、合并冲突)来评估请求,只是最终的对象不会写入存储。请求的响应主体与 非试运行模式下的响应尽可能接近。系统会保证试运行模式的请求不会被写入到存储 中,也不会产生其他副作用。 ### 发起试运行请求 {#make-a-dry-run-request} 通过设置 `dryRun` 查询参数可以触发试运行模式。此参数是一个字符串,以枚举值 的形式工作且可接受的值只有: * `All`:每个阶段被会正常运行,除了最后的存储阶段。准入控制器会被运行来检查请求 是否合法,变更性(Mutating)控制器会变更请求,`PATCH` 请求也会触发合并操作, 对象字段的默认值也会被设置,且基于模式定义的合法性检查也会被执行。 所生成的变更不会被写入到下层的持久性存储中,但本来会写入到数据库中的最终对象 会和正常的状态代码一起被返回给用户。如果请求会触发准入控制器而该准入控制器 带有一定的副作用,则请求会失败而不是冒险产生不希望的副作用。 所有的内置准入控制器插件都支持试运行模式。此外,准入控制 Webhook 也可在其 [配置对象](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#webhook-v1beta1-admissionregistration-k8s-io) 中通过将 `sideEffects` 字段设置为 "None" 来声明自身不会产生副作用。 如果某 Webhook 确实会产生副作用,那么 `sideEffects` 字段应该设置为 "NoneOnDryRun", 并且 Webhook 应该被更改以支持 AdmissionReview 中的 `dryRun` 字段,从而避免 在试运行时产生副作用。 * 空字符串(也即默认值):保留默认的修改行为。 例如: ```console POST /api/v1/namespaces/test/pods?dryRun=All Content-Type: application/json Accept: application/json ``` 响应会与非试运行模式请求的响应看起来相同,只是某些生成字段的值可能会不同。 ### 试运行的授权 {#dry-run-authorization} 试运行和非试运行请求的鉴权是完全相同的。因此,要发起一个试运行请求,用户必须 被授权执行非试运行请求。 例如,要在 Deployment 对象上试运行 `PATCH` 操作,你必须具有对 Deployment 执行 `PATCH` 操作的访问权限,如下面的 RBAC 规则所示: ```yaml rules: - apiGroups: ["extensions", "apps"] resources: ["deployments"] verbs: ["patch"] ``` 参阅[鉴权概述](/zh/docs/reference/access-authn-authz/authorization/)以了解鉴权细节。 ### 生成的值 {#generated-values} 对象的某些值通常是在对象被写入数据库之前生成的。很重要的一点是不要依赖试运行 请求为这些字段所设置的值,因为试运行模式下所得到的这些值与真实请求所获得的 值很可能不同。这类字段有: * `name`:如果设置了 `generateName` 字段,则 `name` 会获得一个唯一的随机名称 * `creationTimestamp`/`deletionTimestamp`:记录对象的创建/删除时间 * `UID`:唯一性标识对象,取值随机生成(非确定性) * `resourceVersion`: 跟踪对象的持久化(存储)版本 * 变更性准入控制器所设置的字段 * 对于 `Service` 资源:`kube-apiserver` 为 `v1.Service` 对象分配的端口和 IP ## 服务器端应用 {#server-side-apply} {{< feature-state for_k8s_version="v1.16" state="beta" >}} 从 Kubernetes v1.18 开始,可以启用[服务器端应用](/zh/docs/reference/using-api/server-side-apply/)功能 特性,启用该特性后,控制面会跟踪所有新创建的对象的托管字段。服务器端应用提供了一种简洁的模式来管理字段冲突,提供服务器端的 `Apply` 和 `Update` 操作,并取代了 `kubectl apply` 的客户端功能。有关该特性的详细描述,请参见[服务器端应用](/zh/docs/reference/using-api/server-side-apply/)章节 ## 资源版本 {#resource-versions} 资源版本采用字符串来表达,用来标示对象的服务器端内部版本。 客户端可以使用资源版本来判定对象是否被更改,或者在读取、列举或监视资源时 用来表达数据一致性需求。 客户端必需将资源版本视为不透明的对象,将其原封不动地传递回服务器端。 例如,客户端一定不能假定资源版本是某种数值标识,也不可以对两个资源版本值 进行比较看其是否相同(也就是不可以比较两个版本值以判断其中一个比另一个 大或小)。 ### `metadata` 中的 `resourceVersion` {#resourceVersion-in-metadata} 客户端可以在资源中看到资源版本信息,这里的资源包括从服务器返回的 Watch 事件 以及 list 操作响应: [v1.meta/ObjectMeta](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#objectmeta-v1-meta) - 资源 的 `metadata.resourceVersion` 值标明该实例上次被更改时的资源版本。 [v1.meta/ListMeta](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#listmeta-v1-meta) - 资源集合 (即 list 操作的响应)的 `metadata.resourceVersion` 所标明的是 list 响应被构造 时的资源版本。 ### `resourceVersion` 参数 {#the-resourceversion-parameter} GET、LIST 和 WATCH 操作都支持 `resourceVersion` 参数。 参数的具体含义取决于所执行的操作和所给的 `resourceVersion` 值: 对于 GET 和 LIST 而言,资源版本的语义为: **GET:** | resourceVersion 未设置 | resourceVersion="0" | resourceVersion="\<非零值\>" | |------------------------|---------------------|----------------------------------------| | 最新版本 | 任何版本 | 不老于给定版本 | **LIST:** v1.19 及以上版本的 API 服务器支持 `resourceVersionMatch` 参数,用以确定如何对 LIST 调用应用 resourceVersion 值。 强烈建议在为 LIST 调用设置了 `resourceVersion` 时也设置 `resourceVersionMatch`。 如果 `resourceVersion` 未设置,则 `resourceVersionMatch` 是不允许设置的。 为了向后兼容,客户端必须能够容忍服务器在某些场景下忽略 `resourceVersionMatch` 的行为: - 当设置 `resourceVersionMatch=NotOlderThan` 且指定了 `limit` 时,客户端必须能够 处理 HTTP 410 "Gone" 响应。例如,客户端可以使用更新一点的 `resourceVersion` 来重试,或者回退到 `resourceVersion=""` (即允许返回任何版本)。 - 当设置了 `resourceVersionMatch=Exact` 且未指定 `limit` 时,客户端必须验证 响应数据中 `ListMeta` 的 `resourceVersion` 与所请求的 `resourceVersion` 匹配, 并处理二者可能不匹配的情况。例如,客户端可以重试设置了 `limit` 的请求。 除非你对一致性有着非常强烈的需求,使用 `resourceVersionMatch=NotOlderThan` 同时为 `resourceVersion` 设定一个已知值是优选的交互方式,因为与不设置 `resourceVersion` 和 `resourceVersionMatch` 相比,这种配置可以取得更好的 集群性能和可扩缩性。后者需要提供带票选能力的读操作。 {{< table caption="list 操作的 resourceVersionMatch 与分页参数" >}} | resourceVersionMatch 参数 | 分页参数 | resourceVersion 未设置 | resourceVersion="0" | resourceVersion="\<非零值\>" | |-----------------------------------------|---------------------------------|-------------------------|-----------------------------------------|----------------------------------| | resourceVersionMatch 未设置 | limit 未设置 | 最新版本 | 任意版本 | 不老于指定版本 | | resourceVersionMatch 未设置 | limit=\, continue 未设置 | 最新版本 | 任意版本 | 精确匹配 | | resourceVersionMatch 未设置 | limit=\, continue=\ | 从 token 开始、精确匹配 | 非法请求,视为从 token 开始、精确匹配 | 非法请求,返回 HTTP `400 Bad Request` | | resourceVersionMatch=Exact [1] | limit 未设置 | 非法请求 | 非法请求 | 精确匹配 | | resourceVersionMatch=Exact [1] | limit=\, continue 未设置 | 非法请求 | 非法请求 | 精确匹配 | | resourceVersionMatch=NotOlderThan [1] | limit 未设置 | 非法请求 | 任意版本 | 不老于指定版本 | | resourceVersionMatch=NotOlderThan [1] | limit=\, continue 未设置 | 非法请求 | 任意版本 | 不老于指定版本 | {{< /table >}} **脚注:** [1] 如果服务器无法正确处理 `resourceVersionMatch` 参数,其行为与未设置该参数相同。 GET 和 LIST 操作的语义含义如下: - **最新版本:** 返回资源版本为最新的数据。所返回的数据必须一致 (通过票选读操作从 etcd 中取出)。 - **任意版本:** 返回任意资源版本的数据。优选最新可用的资源版本,不过不能保证 强一致性;返回的数据可能是任何资源版本的。请求返回的数据有可能是客户端以前 看到过的很老的资源版本。尤其在某些高可用配置环境中,网络分区或者高速缓存 未被更新等状态都可能导致这种状况。不能容忍这种不一致性的客户端不应采用此 语义。 - **不老于指定版本:** 返回至少比所提供的 `resourceVersion` 还要新的数据。 优选最新的可用数据,不过最终提供的可能是不老于所给 `resourceVersion` 的任何版本。 对于发给能够正确处理 `resourceVersionMatch` 参数的服务器的 LIST 请求,此语义 保证 `ListMeta` 中的 `resourceVersion` 不老于请求的 `resourceVersion`,不过 不对列表条目之 `ObjectMeta` 的 `resourceVersion` 提供任何保证。 这是因为 `ObjectMeta.resourceVersion` 所跟踪的是列表条目对象上次更新的时间, 而不是对象被返回时是否是最新。 - **确定版本:** 返回精确匹配所给资源版本的数据。如果所指定的 resourceVersion 的数据不可用,服务器会响应 HTTP 410 "Gone"。 对于发送给能够正确处理 `resourceVersionMatch` 参数的服务器的 LIST 请求而言, 此语义会保证 ListMeta 中的 `resourceVersion` 与所请求的 `resourceVersion` 匹配, 不过不对列表条目之 `ObjectMeta` 的 `resourceVersion` 提供任何保证。 这是因为 `ObjectMeta.resourceVersion` 所跟踪的是列表条目对象上次更新的时间, 而不是对象被返回时是否是最新。 - **Continue 令牌、精确匹配:** 返回原先带分页参数的 LIST 调用中指定的资源版本的数据。 在最初的带分页参数的 LIST 调用之后,所有分页式的 LIST 调用都使用所返回的 Continue 令牌来跟踪最初提供的资源版本, 对于 WATCH 操作而言,资源版本的语义如下: **WATCH:** {{< table caption="watch 操作的 resourceVersion 设置" >}} | resourceVersion 未设置 | resourceVersion="0" | resourceVersion="\<非零值\>" | |---------------------------|--------------------------|------------------------------| | 读取状态并从最新版本开始 | 读取状态并从任意版本开始 | 从指定版本开始 | {{< /table >}} WATCH 操作语义的含义如下: - **读取状态并从最新版本开始:** 从最新的资源版本开始 WATCH 操作。这里的 最新版本必须是一致的(即通过票选读操作从 etcd 中取出)。为了建立初始状态, WATCH 首先会处理一组合成的 "Added" 事件,这些事件涵盖在初始资源版本中存在 的所有资源实例。 所有后续的 WATCH 事件都是关于 WATCH 开始时所处资源版本之后发生的变更。 - **读取状态并从任意版本开始:** 警告:通过这种方式初始化的 WATCH 操作可能会 返回任何状态的停滞数据。请在使用此语义之前执行复核,并在可能的情况下采用其他 语义。此语义会从任意资源版本开始执行 WATCH 操作,优选最新的可用的资源版本, 不过不是必须的;采用任何资源版本作为起始版本都是被允许的。 WATCH 操作有可能起始于客户端已经观测到的很老的版本。在高可用配置环境中,因为 网络分裂或者高速缓存未及时更新的原因都会造成此现象。 如果客户端不能容忍这种不一致性,就不要使用此语义来启动 WATCH 操作。 为了建立初始状态,WATCH 首先会处理一组合成的 "Added" 事件,这些事件涵盖在 初始资源版本中存在的所有资源实例。 所有后续的 WATCH 事件都是关于 WATCH 开始时所处资源版本之后发生的变更。 - **从指定版本开始:** 从某确切资源版本开始执行 WATCH 操作。WATCH 事件都是 关于 WATCH 开始时所处资源版本之后发生的变更。与前面两种语义不同,WATCH 操作 开始的时候不会生成或处理为所提供资源版本合成的 "Added" 事件。 我们假定客户端既然能够提供确切资源版本,就应该已经拥有了起始资源版本对应的初始状态。 ### "410 Gone" 响应 {#410-gone-responses} 服务器不需要提供所有老的资源版本,在客户端请求的是早于服务器端所保留版本的 `resourceVersion` 时,可以返回 HTTP `410 (Gone)` 状态码。 客户端必须能够容忍 `410 (Gone)` 响应。 参阅[高效检测变更](#efficient-detection-of-changes)以了解如何在监测资源时 处理 `410 (Gone)` 响应。 如果所请求的 `resourceVersion` 超出了可应用的 `limit`,那么取决于请求是否 是通过高速缓存来满足的,API 服务器可能会返回一个 `410 Gone` HTTP 响应。 ### 不可用的资源版本 {#unavailable-resource-versions} 服务器不必未无法识别的资源版本提供服务。针对无法识别的资源版本的 LIST 和 GET 请求 可能会短暂等待,以期资源版本可用。如果所给的资源版本在一定的时间段内仍未变得 可用,服务器应该超时并返回 `504 (Gateway Timeout)`,且可在响应中添加 `Retry-After` 响应头部字段,标明客户端在再次尝试之前应该等待多少秒钟。 目前,`kube-apiserver` 也能使用 `Too large resource version(资源版本过高)` 消息来标识这类响应。针对某无法识别的资源版本的 WATCH 操作可能会无限期 (直到请求超时)地等待下去,直到资源版本可用。