Kubernetes API 概念

Kubernetes API 是通过 HTTP 提供的基于资源(RESTful)的编程接口。它支持通过标准 HTTP 动词(POST、PUT、PATCH、DELETE、GET)检索、创建、更新和删除主要资源。

对于某些资源,API 包含额外的子资源,这些子资源允许进行细粒度授权(例如,对 Pod 详细信息和日志检索的不同视图),并且可以接受和提供这些资源的不同表示形式以方便或提高效率。

Kubernetes 通过 *watches* 支持对资源进行高效的更改通知。Kubernetes 还提供一致的列表操作,以便 API 客户端可以有效地缓存、跟踪和同步资源状态。

您可以在线查看 API 参考,或继续阅读以了解有关 API 的一般信息。

Kubernetes API 术语

Kubernetes 通常利用常见的 RESTful 术语来描述 API 概念

  • *资源类型* 是 URL 中使用的名称(podsnamespacesservices
  • 所有资源类型都有一个具体的表示形式(它们的 对象模式),称为 *类型*
  • 资源类型实例列表称为 *集合*
  • 资源类型的一个实例称为 *资源*,通常也表示一个 *对象*
  • 对于某些资源类型,API 包含一个或多个 *子资源*,这些子资源表示为资源下的 URI 路径

大多数 Kubernetes API 资源类型是 对象——它们代表集群上概念的具体实例,例如 Pod 或命名空间。较少的 API 资源类型是 *虚拟* 的,因为它们通常表示对对象的运算,而不是对象本身,例如权限检查(使用包含 SubjectAccessReview 的 JSON 编码体的 POST 请求到 subjectaccessreviews 资源),或 Pod 的 eviction 子资源(用于触发 API 触发的驱逐)。

对象名称

您可以通过 API 创建的所有对象都具有唯一的对象 名称 以允许幂等创建和检索,除了虚拟资源类型可能没有唯一的名称,如果它们不可检索或不依赖于幂等性。在 命名空间 中,一次只能有一个给定类型的对象具有给定名称。但是,如果您删除了对象,则可以创建一个具有相同名称的新对象。某些对象没有命名空间(例如:节点),因此它们的名称在整个集群中必须是唯一的。

API 动词

几乎所有对象资源类型都支持标准 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。Kubernetes 也使用自己的动词,这些动词通常写成小写以区分它们与 HTTP 动词。

Kubernetes 使用术语 **list** 来描述返回 资源集合 以区分检索单个资源,单个资源通常称为 **get**。如果您发送了带有 ?watch 查询参数的 HTTP GET 请求,Kubernetes 将其称为 **watch** 而不是 **get**(有关更多详细信息,请参阅 高效检测更改)。

对于 PUT 请求,Kubernetes 在内部将它们分类为 **create** 或 **update**,具体取决于现有对象的状态。**update** 与 **patch** 不同;**patch** 的 HTTP 动词是 PATCH。

资源 URI

所有资源类型都由集群(/apis/GROUP/VERSION/*)或命名空间(/apis/GROUP/VERSION/namespaces/NAMESPACE/*)限定。命名空间限定的资源类型将在其命名空间删除时被删除,对该资源类型的访问由命名空间范围的授权检查控制。

注意:核心资源使用 /api 而不是 /apis 并省略 GROUP 路径段。

示例

  • /api/v1/namespaces
  • /api/v1/pods
  • /api/v1/namespaces/my-namespace/pods
  • /apis/apps/v1/deployments
  • /apis/apps/v1/namespaces/my-namespace/deployments
  • /apis/apps/v1/namespaces/my-namespace/deployments/my-deployment

您还可以访问资源集合(例如:列出所有节点)。以下路径用于检索集合和资源

  • 集群范围资源

    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回资源类型资源的集合
    • GET /apis/GROUP/VERSION/RESOURCETYPE/NAME - 返回资源类型下具有 NAME 的资源
  • 命名空间范围资源

    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回所有命名空间中资源类型的所有实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE - 返回命名空间中资源类型的所有实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME - 返回命名空间中具有 NAME 的资源类型的实例

由于命名空间是集群范围的资源类型,您可以使用 GET /api/v1/namespaces 检索所有命名空间的列表(“集合”),并使用 GET /api/v1/namespaces/NAME 检索特定命名空间的详细信息。

  • 集群范围子资源:GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
  • 命名空间范围子资源:GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE

每个子资源支持的动词将根据对象有所不同 - 请参阅 API 参考 以了解更多信息。无法跨多个资源访问子资源 - 通常,如果需要,将使用新的虚拟资源类型。

高效检测更改

Kubernetes API 允许客户端对对象或集合进行初始请求,然后跟踪自该初始请求以来的更改:**watch**。客户端可以发送 **list** 或 **get**,然后发出后续 **watch** 请求。

为了使此更改跟踪成为可能,每个 Kubernetes 对象都有一个 resourceVersion 字段,表示该资源存储在底层持久性层中的版本。检索资源集合(命名空间或集群范围)时,API 服务器的响应包含一个 resourceVersion 值。客户端可以使用该 resourceVersion 来针对 API 服务器启动 **watch**。

当您发送 **watch** 请求时,API 服务器将以更改流的形式进行响应。这些更改详细说明了在您作为 **watch** 请求参数指定的 resourceVersion 之后发生的运算(例如 **create**、**delete** 和 **update**)的结果。总体的 **watch** 机制允许客户端获取当前状态,然后订阅后续更改,而不会错过任何事件。

如果客户端 **watch** 断开连接,则该客户端可以从上次返回的 resourceVersion 启动新的 **watch**;客户端也可以执行新的 **get** / **list** 请求并重新开始。有关更多详细信息,请参阅 资源版本语义

例如

  1. 列出给定命名空间中的所有 Pod。

    GET /api/v1/namespaces/test/pods
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {"resourceVersion":"10245"},
      "items": [...]
    }
    
  2. 从资源版本 10245 开始,接收影响 *test* 命名空间中 Pod 的任何 API 操作(例如 **create**、**delete**、**patch** 或 **update**)的通知。每个更改通知都是一个 JSON 文档。HTTP 响应主体(以 application/json 提供)包含一系列 JSON 文档。

    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 服务器只会保留一定时间内的更改历史记录。使用 etcd 3 的集群默认情况下会保留过去 5 分钟内的更改。当请求的 **watch** 操作由于该资源的历史版本不可用而失败时,客户端必须通过识别状态码 410 Gone、清除本地缓存、执行新的 **get** 或 **list** 操作并从返回的 resourceVersion 开始 **watch** 来处理这种情况。

对于订阅集合,Kubernetes 客户端库通常提供某种形式的标准工具来实现此 **list** 然后 **watch** 逻辑。(在 Go 客户端库中,这称为 Reflector,位于 k8s.io/client-go/tools/cache 包中。)

Watch 书签

为了减轻短历史窗口的影响,Kubernetes API 提供了一个名为 BOOKMARK 的 watch 事件。它是一种特殊类型的事件,用于标记客户端请求的所有更改已发送到给定的 resourceVersion。表示 BOOKMARK 事件的文档与请求类型相同,但只包含 .metadata.resourceVersion 字段。例如

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"} }
}

作为客户端,您可以通过将 allowWatchBookmarks=true 查询参数设置为 **watch** 请求来请求 BOOKMARK 事件,但您不应该假设书签会在任何特定间隔返回,也不应该假设 API 服务器即使在请求时也会发送任何 BOOKMARK 事件。

流式列表

功能状态: Kubernetes v1.27 [alpha]

在大型集群中,检索某些资源类型的集合可能会导致控制平面上的资源使用量(主要是 RAM)显着增加。为了减轻其影响并简化 **list** + **watch** 模式的用户体验,Kubernetes v1.27 引入了对请求初始状态(以前通过 **list** 请求请求)作为 **watch** 请求的一部分的支持,作为 alpha 功能。

只要启用了 WatchList 功能开关,就可以通过在 **watch** 请求中指定 sendInitialEvents=true 作为查询字符串参数来实现。如果设置,API 服务器将使用合成 init 事件(类型为 ADDED)启动 watch 流,以构建所有现有对象的完整状态,然后是一个 BOOKMARK 事件(如果通过 allowWatchBookmarks=true 选项请求)。书签事件包含已同步到的资源版本。发送书签事件后,API 服务器将继续进行其他任何 **watch** 请求。

当您在查询字符串中设置 sendInitialEvents=true 时,Kubernetes 还要求您将 resourceVersionMatch 设置为 NotOlderThan 值。如果您在查询字符串中提供 resourceVersion 而不提供值或根本不提供,则这被解释为请求 *一致读取*;书签事件在状态同步到请求开始处理时的时刻至少一致读取时发送。如果您指定了 resourceVersion(在查询字符串中),则当状态同步到提供的资源版本至少时,书签事件就会发送。

示例

例如,您想要查看一组 Pod。对于该集合,当前资源版本为 10245,并且有两个 Pod:foobar。然后发送以下请求(通过使用 resourceVersion= 设置空资源版本来明确请求一致读取)可能会导致以下事件序列

GET /api/v1/namespaces/test/pods?watch=1&sendInitialEvents=true&allowWatchBookmarks=true&resourceVersion=&resourceVersionMatch=NotOlderThan
---
200 OK
Transfer-Encoding: chunked
Content-Type: application/json

{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "8467", "name": "foo"}, ...}
}
{
  "type": "ADDED",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "5726", "name": "bar"}, ...}
}
{
  "type": "BOOKMARK",
  "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10245"} }
}
...
<followed by regular watch stream starting from resourceVersion="10245">

响应压缩

功能状态: Kubernetes v1.16 [beta]

APIResponseCompression 是一种允许 API 服务器压缩 getlist 请求的响应的选项,从而减少网络带宽并提高大型集群的性能。它从 Kubernetes 1.16 开始默认启用,可以通过在 API 服务器的 --feature-gates 标志中包含 APIResponseCompression=false 来禁用。

API 响应压缩可以显着减小响应的大小,特别是对于大型资源或 集合。例如,针对 Pod 的 list 请求可以返回数百 KB 甚至 MB 的数据,具体取决于 Pod 的数量及其属性。通过压缩响应,可以节省网络带宽并减少延迟。

要验证 APIResponseCompression 是否正常工作,您可以向 API 服务器发送带有 Accept-Encoding 标头的 getlist 请求,并检查响应大小和标头。例如

GET /api/v1/pods
Accept-Encoding: gzip
---
200 OK
Content-Type: application/json
content-encoding: gzip
...

content-encoding 标头表明响应已使用 gzip 压缩。

分块检索大型结果集

功能状态: Kubernetes v1.29 [stable]

在大型集群中,检索某些资源类型的集合可能会导致非常大的响应,这会影响服务器和客户端。例如,集群可能拥有数万个 Pod,每个 Pod 约等于 2 KiB 的编码 JSON。检索所有命名空间中的所有 Pod 可能会导致非常大的响应(10-20 MB)并消耗大量的服务器资源。

Kubernetes API 服务器支持将单个大型集合请求分解为许多较小的块,同时保持整个请求的一致性。每个块都可以按顺序返回,这既减少了请求的总大小,又允许面向用户的客户端逐步显示结果以提高响应速度。

您可以请求 API 服务器通过使用页面(Kubernetes 称为)来处理单个集合的 list。要分块检索单个集合,支持针对集合的请求的两个查询参数 limitcontinue,并且所有 list 操作在集合的 metadata 字段中返回一个响应字段 continue。客户端应使用 limit 指定希望在每个块中接收的最大结果数,服务器将返回结果中的最多 limit 个资源,如果集合中还有更多资源,则包含一个 continue 值。

作为 API 客户端,您可以在下一个请求中将此 continue 值传递给 API 服务器,以指示服务器返回结果的下一页()。通过一直继续,直到服务器返回一个空的 continue 值,您就可以检索整个集合。

类似于 watch 操作,continue 令牌将在短时间后过期(默认情况下为 5 分钟),如果无法返回更多结果,则返回 410 Gone。在这种情况下,客户端需要从头开始或省略 limit 参数。

例如,如果集群上有 1,253 个 Pod,并且您想一次接收 500 个 Pod 的块,请按如下方式请求这些块

  1. 列出集群上的所有 Pod,每次检索最多 500 个 Pod。

    GET /api/v1/pods?limit=500
    ---
    200 OK
    Content-Type: application/json
    
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {
        "resourceVersion":"10245",
        "continue": "ENCODED_CONTINUE_TOKEN",
        "remainingItemCount": 753,
        ...
      },
      "items": [...] // returns pods 1-500
    }
    
  2. 继续上一次调用,检索下一组 500 个 Pod。

    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",
        "remainingItemCount": 253,
        ...
      },
      "items": [...] // returns pods 501-1000
    }
    
  3. 继续上一次调用,检索最后 253 个 Pod。

    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
    }
    

请注意,集合的 resourceVersion 在每个请求中保持不变,表明服务器向您显示 Pod 的一致快照。在版本 10245 之后创建、更新或删除的 Pod 不会显示,除非您发出不带 continue 令牌的单独 list 请求。这使您能够将大型请求分解成较小的块,然后对整个集合执行 watch 操作,而不会错过任何更新。

remainingItemCount 是集合中未包含在此响应中的后续项目的数量。如果 list 请求包含标签或字段 选择器,则剩余项目数未知,API 服务器不会在其响应中包含 remainingItemCount 字段。如果 list 完成(因为不是分块,或者因为这是最后一个块),则没有更多剩余项目,API 服务器不会在其响应中包含 remainingItemCount 字段。remainingItemCount 的预期用途是估计集合的大小。

集合

在 Kubernetes 术语中,从 list 获取的响应称为集合。但是,Kubernetes 为不同类型资源的集合定义了具体的种类。集合的种类以资源种类命名,并在其后附加 List

当您查询 API 以获取特定类型时,该查询返回的所有项目都属于该类型。例如,当您 list Services 时,集合响应的 kind 设置为 ServiceList;该集合中的每个项目都代表一个单独的 Service。例如

GET /api/v1/services
{
  "kind": "ServiceList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "2947301"
  },
  "items": [
    {
      "metadata": {
        "name": "kubernetes",
        "namespace": "default",
...
      "metadata": {
        "name": "kube-dns",
        "namespace": "kube-system",
...

Kubernetes API 中定义了数十种集合类型(例如 PodListServiceListNodeList)。您可以从 Kubernetes API 文档中获取有关每种集合类型的更多信息。

某些工具(例如 kubectl)对 Kubernetes 集合机制的表示方式与 Kubernetes API 本身略有不同。由于 kubectl 的输出可能包含 API 级别多个 list 操作的响应,因此 kubectl 使用 kind: List 来表示项目列表。例如

kubectl get services -A -o yaml
apiVersion: v1
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
items:
- apiVersion: v1
  kind: Service
  metadata:
    creationTimestamp: "2021-06-03T14:54:12Z"
    labels:
      component: apiserver
      provider: kubernetes
    name: kubernetes
    namespace: default
...
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      prometheus.io/port: "9153"
      prometheus.io/scrape: "true"
    creationTimestamp: "2021-06-03T14:54:14Z"
    labels:
      k8s-app: kube-dns
      kubernetes.io/cluster-service: "true"
      kubernetes.io/name: CoreDNS
    name: kube-dns
    namespace: kube-system

以表格形式接收资源

当您运行 kubectl get 时,默认输出格式是特定资源类型的一个或多个实例的简单表格表示形式。过去,客户端需要重新生成 kubectl 中实现的表格和描述输出以执行对象的简单列表。该方法有一些局限性,包括在处理某些对象时涉及的非平凡逻辑。此外,由 API 聚合或第三方资源提供的类型在编译时是未知的。这意味着必须为客户端无法识别的类型提供通用实现。

为了避免上述潜在的局限性,客户端可以请求对象的表格表示形式,将打印的具体细节委托给服务器。Kubernetes API 实现标准 HTTP 内容类型协商:传递包含 application/json;as=Table;g=meta.k8s.io;v=v1 值的 Accept 标头以及 GET 调用将请求服务器以表格内容类型返回对象。

例如,以表格格式列出集群上的所有 Pod。

GET /api/v1/pods
Accept: application/json;as=Table;g=meta.k8s.io;v=v1
---
200 OK
Content-Type: application/json

{
    "kind": "Table",
    "apiVersion": "meta.k8s.io/v1",
    ...
    "columnDefinitions": [
        ...
    ]
}

对于控制平面不知道自定义表格定义的 API 资源类型,API 服务器返回一个默认表格响应,其中包含资源的 namecreationTimestamp 字段。

GET /apis/crd.example.com/v1alpha1/namespaces/default/resources
---
200 OK
Content-Type: application/json
...

{
    "kind": "Table",
    "apiVersion": "meta.k8s.io/v1",
    ...
    "columnDefinitions": [
        {
            "name": "Name",
            "type": "string",
            ...
        },
        {
            "name": "Created At",
            "type": "date",
            ...
        }
    ]
}

并非所有 API 资源类型都支持表格响应;例如,自定义资源定义 可能不会定义字段到表格的映射,并且 扩展核心 Kubernetes API 的 APIService 可能根本不提供表格响应。如果您正在实现使用表格信息的客户端并且必须针对所有资源类型(包括扩展)工作,则应发出在 Accept 标头中指定多个内容类型的请求。例如

Accept: application/json;as=Table;g=meta.k8s.io;v=v1, application/json

资源的替代表示形式

默认情况下,Kubernetes 以内容类型 application/json 返回序列化为 JSON 的对象。这是 API 的默认序列化格式。但是,客户端可以请求这些对象的更高效的 Protobuf 表示形式,以便在规模上实现更好的性能。Kubernetes API 实现标准 HTTP 内容类型协商:使用 GET 调用传递包含 Accept 标头的请求将要求服务器尝试以您首选的媒体类型返回响应,而将对象以 Protobuf 格式发送到服务器以进行 PUTPOST 调用意味着您必须适当地设置 Content-Type 标头。

如果支持请求的格式,服务器将返回带有 Content-Type 标头的响应,如果未支持您请求的任何媒体类型,则返回 406 Not acceptable 错误。所有内置资源类型都支持 application/json 媒体类型。

有关每个 API 支持的内容类型的列表,请参阅 Kubernetes API 参考

例如

  1. 以 Protobuf 格式列出集群上的所有 Pod。

    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 格式返回响应。

    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;具体来说,Protobuf 不适用于定义为 自定义资源定义 或通过 聚合层 提供的资源。作为客户端,如果您可能需要使用扩展类型,则应在请求的 Accept 标头中指定多个内容类型以支持回退到 JSON。例如

Accept: application/vnd.kubernetes.protobuf, application/json

Kubernetes Protobuf 编码

Kubernetes 使用一个信封包装器来对 Protobuf 响应进行编码。该包装器以一个 4 字节的魔数开头,以帮助识别磁盘或 etcd 中的内容为 Protobuf(而不是 JSON),然后是 Protobuf 编码的包装器消息,该消息描述了基础对象的编码和类型,然后包含该对象。

包装器格式为

A four byte magic number prefix:
  Bytes 0-3: "k8s\x00" [0x6b, 0x38, 0x73, 0x00]

An encoded Protobuf message with the following IDL:
  message Unknown {
    // typeMeta should have the string values for "kind" and "apiVersion" as set on the JSON object
    optional TypeMeta typeMeta = 1;

    // raw will hold the complete serialized object in protobuf. See the protobuf definitions in the client libraries for a given kind.
    optional bytes raw = 2;

    // contentEncoding is encoding used for the raw data. Unspecified means no encoding.
    optional string contentEncoding = 3;

    // contentType is the serialization method used to serialize 'raw'. Unspecified means application/vnd.kubernetes.protobuf and is usually
    // omitted.
    optional string contentType = 4;
  }

  message TypeMeta {
    // apiVersion is the group/version for this type
    optional string apiVersion = 1;
    // kind is the name of the object schema. A protobuf definition should exist for this object.
    optional string kind = 2;
  }

资源删除

当您 delete 资源时,此过程分为两个阶段。

  1. 最终化
  2. 移除
{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    "finalizers": ["url.io/neat-finalization", "other-url.io/my-finalizer"],
    "deletionTimestamp": nil,
  }
}

当客户端首次发送 delete 来请求删除资源时,.metadata.deletionTimestamp 将设置为当前时间。一旦设置了 .metadata.deletionTimestamp,在最终化器上操作的外部控制器就可以随时以任何顺序开始执行其清理工作。

最终化器之间不强制执行顺序,因为这会导致 .metadata.finalizers 卡住的风险显著增加。

.metadata.finalizers 字段是共享的:任何具有权限的参与者都可以重新排序它。如果最终器列表按顺序处理,那么这可能会导致一种情况,即负责列表中第一个最终器的组件正在等待由负责列表中后面最终器的组件产生的某些信号(字段值、外部系统或其他),从而导致死锁。

在没有强制排序的情况下,最终器可以自由地在彼此之间排序,并且不会受到列表中排序更改的影响。

一旦最后一个最终器被移除,资源就会从 etcd 中真正移除。

单个资源 API

Kubernetes API 动词 **get**、**create**、**update**、**patch**、**delete** 和 **proxy** 仅支持单个资源。这些支持单个资源的动词不支持将多个资源一起以有序或无序列表或事务的形式提交。

当客户端(包括 kubectl)对一组资源执行操作时,客户端会发出一系列针对单个资源的 API 请求,然后根据需要聚合响应。

相比之下,Kubernetes API 动词 **list** 和 **watch** 允许获取多个资源,而 **deletecollection** 允许删除多个资源。

字段验证

Kubernetes 始终验证字段的类型。例如,如果 API 中的字段定义为数字,则不能将该字段设置为文本值。如果字段定义为字符串数组,则只能提供数组。某些字段允许你省略它们,其他字段是必需的。从 API 请求中省略必需字段将导致错误。

如果你使用额外的字段发出请求,而集群控制平面无法识别该字段,那么 API 服务器的行为会更加复杂。

默认情况下,API 服务器会从其接收的输入(例如,PUT 请求的 JSON 主体)中删除它无法识别的字段。

有两种情况下,API 服务器会删除你在 HTTP 请求中提供的字段。

这些情况是

  1. 该字段无法识别,因为它不在资源的 OpenAPI 架构中。(对此的一个例外是 CRD,它们通过 x-kubernetes-preserve-unknown-fields 明确选择不修剪未知字段)。
  2. 该字段在对象中重复。

对无法识别或重复字段的验证

功能状态: Kubernetes v1.27 [稳定]

从 1.25 开始,当使用可以提交数据的 HTTP 动词(POSTPUTPATCH)时,对象中无法识别或重复的字段会在服务器上通过验证进行检测。可能的验证级别有 IgnoreWarn(默认)和 Strict

忽略
API 服务器成功处理请求,就像没有设置错误字段一样,删除所有未知和重复字段,并且不提供任何它已执行此操作的指示。
警告
(默认)API 服务器成功处理请求,并向客户端报告警告。警告使用 Warning: 响应标头发送,为每个未知或重复字段添加一个警告项。有关警告和 Kubernetes API 的更多信息,请参阅博客文章 警告:有用的警告
严格
当 API 服务器检测到任何未知或重复字段时,它会拒绝该请求,并返回 400 错误请求错误。API 服务器的响应消息会指定 API 服务器检测到的所有未知或重复字段。

字段验证级别由 fieldValidation 查询参数设置。

向服务器提交请求的工具(如 kubectl)可能会设置自己的默认值,这些默认值与 API 服务器默认使用的 Warn 验证级别不同。

kubectl 工具使用 --validate 标志来设置字段验证级别。它接受 ignorewarnstrict 值,同时还接受 true(等效于 strict)和 false(等效于 ignore)值。kubectl 的默认验证设置是 --validate=true,这意味着严格的服务器端字段验证。

当 kubectl 无法连接到具有字段验证的 API 服务器(Kubernetes 1.27 之前的 API 服务器)时,它将回退到使用客户端验证。客户端验证将在 kubectl 的未来版本中完全删除。

试运行

功能状态: Kubernetes v1.19 [稳定]

当你使用可以修改资源的 HTTP 动词(POSTPUTPATCHDELETE)时,你可以以试运行模式提交你的请求。试运行模式有助于通过典型的请求阶段(准入链、验证、合并冲突)评估请求,直到将对象持久化到存储。请求的响应主体尽可能接近非试运行响应。Kubernetes 保证试运行请求不会持久化到存储中,也不会产生任何其他副作用。

发出试运行请求

试运行是通过设置 dryRun 查询参数触发的。此参数是一个字符串,充当枚举,唯一接受的值是

[未设置值]
允许副作用。你可以使用诸如 ?dryRun?dryRun&pretty=true 之类的查询字符串请求此操作。响应是最终将被持久化的对象,或者如果请求无法满足,则返回错误。
全部
每个阶段都按正常运行,除了最终的存储阶段,该阶段会阻止副作用。

当你设置 ?dryRun=All 时,任何相关的 准入控制器 都将运行,验证准入控制器会检查请求的变异后的状态,在 PATCH 上执行合并,默认化字段,并进行模式验证。更改不会持久化到底层存储,但最终将被持久化的对象仍然会返回给用户,以及正常的状态代码。

如果请求的非试运行版本会触发具有副作用的准入控制器,则请求将失败,而不是冒着意外副作用的风险。所有内置的准入控制插件都支持试运行。此外,准入 Webhook 可以在其 配置对象 中声明它们没有副作用,方法是将其 sideEffects 字段设置为 None

以下是一个使用 ?dryRun=All 的试运行请求示例

POST /api/v1/namespaces/test/pods?dryRun=All
Content-Type: application/json
Accept: application/json

响应将与非试运行请求的响应相同,但一些生成字段的值可能会有所不同。

生成的值

对象的一些值通常在对象持久化之前生成。重要的是不要依赖试运行请求设置的这些字段的值,因为这些值在试运行模式下与发出真实请求时可能会有所不同。其中一些字段是

  • name:如果设置了 generateName,则 name 将具有唯一的随机名称
  • creationTimestamp / deletionTimestamp:记录创建/删除时间
  • UID唯一标识 对象,并且是随机生成的(非确定性的)
  • resourceVersion:跟踪对象的持久化版本
  • 任何由变异准入控制器设置的字段
  • 对于 Service 资源:kube-apiserver 分配给 Service 对象的端口或 IP 地址

试运行授权

试运行和非试运行请求的授权相同。因此,要发出试运行请求,你必须有权发出非试运行请求。

例如,要对部署执行试运行 **patch**,你必须有权执行该 **patch**。以下是一个 Kubernetes RBAC 规则示例,它允许修补部署

rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["patch"]

请参阅 授权概述

更新现有资源

Kubernetes 提供了几种更新现有对象的方法。你可以阅读 选择更新机制 以了解哪种方法最适合你的用例。

你可以使用 HTTP PUT 覆盖(**更新**)现有资源(例如,ConfigMap)。对于 PUT 请求,客户端有责任指定 resourceVersion(从要更新的对象中获取)。Kubernetes 使用该 resourceVersion 信息,以便 API 服务器可以检测到丢失的更新,并拒绝由与集群不一致的客户端发出的请求。如果资源已发生更改(客户端提供的 resourceVersion 已过期),则 API 服务器会返回 409 Conflict 错误响应。

客户端可以发送一条指令到 API 服务器,要求**修补**现有资源,而不是发送 PUT 请求。如果客户端想要进行的更改不依赖于现有数据,则通常应使用**修补**。需要有效检测丢失更新的客户端应该考虑根据现有 resourceVersion(HTTP PUT 或 HTTP PATCH)使他们的请求有条件,然后处理在发生冲突时需要的任何重试。

Kubernetes API 支持四种不同的 PATCH 操作,这些操作由其相应的 HTTP Content-Type 标头决定

application/apply-patch+yaml
服务器端应用 YAML(一个基于 YAML 的 Kubernetes 特定扩展)。所有 JSON 文档都是有效的 YAML,因此你也可以使用此媒体类型提交 JSON。有关更多详细信息,请参阅 服务器端应用序列化
对于 Kubernetes,如果对象不存在,则此操作为**创建**操作,如果对象已存在,则此操作为**修补**操作。
application/json-patch+json
JSON Patch,如 RFC6902 中所定义。JSON Patch 是一系列在资源上执行的操作;例如 {"op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ]}
对于 Kubernetes,此操作为**修补**操作。

使用 application/json-patch+json 的**修补**可以包含条件以验证一致性,允许操作在不满足这些条件时失败(例如,为了避免丢失更新)。

application/merge-patch+json
JSON 合并补丁,如 RFC7386 中所定义。JSON 合并补丁本质上是资源的局部表示。提交的 JSON 与当前资源结合以创建一个新的资源,然后保存新资源。
对于 Kubernetes,此操作为**修补**操作。
application/strategic-merge-patch+json
策略合并补丁(基于 JSON 的 Kubernetes 特定扩展)。策略合并补丁是 JSON 合并补丁的自定义实现。你只能将策略合并补丁用于内置 API,或用于具有对其特殊支持的聚合 API 服务器。你不能将 application/strategic-merge-patch+json 用于使用 CustomResourceDefinition 定义的任何 API。

Kubernetes 的 服务器端应用 功能允许控制平面跟踪新创建对象的管理字段。服务器端应用为管理字段冲突提供了一个清晰的模式,提供服务器端应用更新操作,并取代了 kubectl apply 的客户端功能。

对于服务器端应用,Kubernetes 将请求视为创建(如果对象尚不存在),否则视为补丁。对于在 HTTP 级别使用 PATCH 的其他请求,逻辑 Kubernetes 操作始终为补丁

有关详细信息,请参阅 服务器端应用

选择更新机制

HTTP PUT 以替换现有资源

更新(HTTP PUT)操作易于实现且灵活,但存在缺点

  • 你需要处理对象 resourceVersion 在客户端读取它并尝试将其写回时发生变化的冲突。Kubernetes 始终检测到冲突,但你作为客户端作者需要实现重试。
  • 如果你在本地解码对象(例如,使用 client-go,你可能会接收到客户端不知道如何处理的字段 - 然后在更新过程中将其删除),则可能会意外删除字段。
  • 如果对象(即使在你不尝试编辑的字段或字段集上)存在大量竞争,你可能会在发送更新时遇到问题。对于较大的对象和具有许多字段的对象,这个问题更严重。

使用 JSON 补丁的 HTTP PATCH

补丁更新很有用,因为

  • 由于你只发送差异,因此 PATCH 请求中需要发送的数据更少。
  • 你可以进行依赖于现有值的更改,例如将特定字段的值复制到注释中。
  • 更新(HTTP PUT)不同,即使无关字段频繁更改,也可以立即进行更改:通常不需要重试。
    • 你可能仍然需要指定 resourceVersion(以匹配现有对象),如果你想格外小心以避免更新丢失
    • 在发生错误时编写一些重试逻辑仍然是最佳实践。
  • 你可以使用测试条件来仔细构建特定的更新条件。例如,如果现有值与你的预期匹配,则可以递增计数器而无需读取它。你可以这样做,而不会有更新丢失的风险,即使对象自上次写入后在其他方面发生了更改。(如果测试条件失败,你可以回退到读取当前值,然后写回更改后的数字)。

然而

  • 你需要更多本地(客户端)逻辑来构建补丁;如果你有 JSON 补丁的库实现,甚至用于专门针对 Kubernetes 创建 JSON 补丁,这将非常有帮助
  • 作为客户端软件的作者,在构建补丁(HTTP 请求正文)时,你需要小心不要删除字段(操作顺序很重要)

使用服务器端应用的 HTTP PATCH

服务器端应用有一些明显的优势

  • 单次往返:它很少需要首先进行 GET 请求。
    • 并且你仍然可以检测意外更改的冲突
    • 你可以在适当的情况下选择强制覆盖冲突
  • 客户端实现易于制作
  • 你无需额外努力即可获得原子创建或更新操作(类似于某些 SQL 方言中的 UPSERT

然而

  • 服务器端应用根本不适用于依赖对象当前值的字段更改
  • 你只能将更新应用于对象。Kubernetes HTTP API 中的一些资源不是对象(它们没有 .metadata 字段),服务器端应用仅与 Kubernetes 对象相关。

资源版本

资源版本是标识服务器对象内部版本的字符串。客户端可以使用资源版本来确定对象何时更改,或在获取、列出和监视资源时表达数据一致性要求。客户端必须将资源版本视为不透明,并将其未修改地传递回服务器。

你不应假设资源版本是数字或可排序的。API 客户端只能比较两个资源版本以确定是否相等(这意味着你不应比较资源版本以确定它们是否大于或小于)。

元数据中的 resourceVersion 字段

客户端在资源中找到资源版本,包括来自监视响应流的资源,或在使用列表枚举资源时。

v1.meta/ObjectMeta - 资源实例的 metadata.resourceVersion 标识实例最后修改时的资源版本。

v1.meta/ListMeta - 资源集合(对列表的响应)的 metadata.resourceVersion 标识构建集合时的资源版本。

查询字符串中的 resourceVersion 参数

获取列出监视操作支持 resourceVersion 参数。从 v1.19 版本开始,Kubernetes API 服务器还支持列表请求上的 resourceVersionMatch 参数。

API 服务器根据你请求的操作和 resourceVersion 的值对 resourceVersion 参数进行不同的解释。如果你设置了 resourceVersionMatch,这也将影响匹配的方式。

获取列出的语义

对于获取列出resourceVersion 的语义如下

获取

resourceVersion 未设置resourceVersion="0"resourceVersion="{value other than 0}"
最新任何不早于

列表

从 v1.19 版本开始,Kubernetes API 服务器支持列表请求上的 resourceVersionMatch 参数。如果你同时设置了 resourceVersionresourceVersionMatch,则 resourceVersionMatch 参数决定 API 服务器如何解释 resourceVersion

列表请求上设置 resourceVersion 时,你应始终设置 resourceVersionMatch 参数。但是,要准备好处理响应的 API 服务器不知道 resourceVersionMatch 并忽略它的情况。

除非你有强一致性要求,否则使用 resourceVersionMatch=NotOlderThan 和已知的 resourceVersion 更好,因为它可以实现比不设置 resourceVersionresourceVersionMatch(需要仲裁读取才能提供服务)更好的集群性能和可扩展性。

在不设置 resourceVersion 的情况下设置 resourceVersionMatch 参数是无效的。

此表解释了具有 resourceVersionresourceVersionMatch 的各种组合的列表请求的行为

resourceVersionMatch 和列表的分页参数
resourceVersionMatch 参数分页参数resourceVersion 未设置resourceVersion="0"resourceVersion="{value other than 0}"
未设置limit 未设置最新任何不早于
未设置limit=<n>,continue 未设置最新任何精确
未设置limit=<n>,continue=<token>继续令牌,精确无效,视为继续令牌,精确无效,HTTP 400 错误请求
resourceVersionMatch=精确limit 未设置无效无效精确
resourceVersionMatch=精确limit=<n>,continue 未设置无效无效精确
resourceVersionMatch=不早于limit 未设置无效任何不早于
resourceVersionMatch=不早于limit=<n>,continue 未设置无效任何不早于

获取列出语义的含义是

任何
返回任何资源版本的 data。首选最新的可用资源版本,但不要求强一致性;可以提供任何资源版本的 data。请求可能会返回比客户端先前观察到的要旧得多的资源版本的 data,特别是在高可用性配置中,因为存在分区或陈旧的缓存。无法容忍这种情况的客户端不应使用这种语义。
最新
返回最新资源版本的 data。返回的 data 必须一致(详细地说:通过仲裁读取从 etcd 提供服务)。对于 etcd v3.4.31+ 和 v3.5.13+ Kubernetes 1.31,从监视缓存提供“最新”读取:API 服务器内部的内存存储,它缓存并镜像持久存储到 etcd 的数据状态。Kubernetes 请求进度通知以维护与 etcd 持久层的一致性。Kubernetes 版本 v1.28 到 v1.30 也支持此功能,尽管作为 Alpha,它不推荐用于生产环境,并且在 v1.31 版本之前默认情况下不会启用。
不早于
返回不早于提供的 resourceVersion 的 data。首选最新的可用 data,但可以提供任何不早于提供的 resourceVersion 的 data。对于服务器尊重 resourceVersionMatch 参数的列表请求,这保证集合的 .metadata.resourceVersion 不早于请求的 resourceVersion,但不对该集合中任何项目的 .metadata.resourceVersion 做出任何保证。
精确
返回提供的精确资源版本的 data。如果提供的 resourceVersion 不可用,则服务器将响应 HTTP 410“Gone”。对于服务器尊重 resourceVersionMatch 参数的列表请求,这保证集合的 .metadata.resourceVersion 与你在查询字符串中请求的 resourceVersion 相同。该保证不适用于该集合中任何项目的 .metadata.resourceVersion
继续令牌,精确
返回初始分页列表调用的资源版本。返回的继续令牌负责跟踪初始分页列表调用之后所有分页列表调用的初始提供的资源版本。

在使用 resourceVersionMatch=NotOlderThan 并且设置了 limit 时,客户端必须处理 HTTP 410“Gone”响应。例如,客户端可能会使用较新的 resourceVersion 重试,或者回退到 resourceVersion=""

当使用resourceVersionMatch=Exactlimit未设置时,客户端必须验证集合的.metadata.resourceVersion是否与请求的resourceVersion匹配,并处理不匹配的情况。例如,客户端可能回退到设置了limit的请求。

**watch**语义

对于**watch**,资源版本的语义是

watch

watch 的 resourceVersion
resourceVersion 未设置resourceVersion="0"resourceVersion="{value other than 0}"
获取状态并从最新版本开始获取状态并从任意版本开始从精确版本开始

这些**watch**语义的含义是

获取状态并从任意版本开始
从任何资源版本开始 **watch**;首选最 新的可用资源版本,但不是必需的。允许任何起始资源版本。由于分区或陈旧缓存,**watch** 可能会从客户端先前观察到的旧 的资源版本开始,特别是在高可用性配置中。无法容忍这种明显回溯的客户端不应使用此语义启动 **watch**。为了建立初始状态,**watch** 从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续 watch 事件都是针对起始资源版本之后发生的更改。
获取状态并从最新版本开始
从最新的资源版本开始 **watch**,该版本必须一致(详细 地说:通过 etcd 的 quorum 读取提供服务)。为了建立初始状态,**watch** 从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续 watch 事件都是针对起始资源版本之后发生的更改。
从精确版本开始
从精确的资源版本开始 **watch**。 watch 事件针对提供的资源版本之后的所有更改。与“获取状态并从最新版本开始”和“获取状态并从任意版本开始”不同,**watch** 不会从提供的资源版本的合成“Added”事件开始。假设客户端已经拥有起始资源版本的初始状态,因为客户端提供了资源版本。

“410 Gone” 响应

服务器不需要提供所有旧的资源版本,如果客户端请求的 resourceVersion 早于服务器保留的版本,则可能会返回 HTTP 410 (Gone) 状态代码。客户端必须能够容忍 410 (Gone) 响应。有关在监视资源时如何处理 410 (Gone) 响应的详细信息,请参阅有效检测更改

如果您请求的 resourceVersion 超出了适用限制,则 API 服务器可能会根据请求是从缓存中提供还是否,回复 410 Gone HTTP 响应。

不可用的资源版本

服务器不需要提供无法识别的资源版本。如果您请求资源版本的 **list** 或 **get**,而 API 服务器无法识别该资源版本,则 API 服务器可能会

  • 等待短暂时间使资源版本可用,然后在提供 的资源版本在合理的时间内没有变为可用时,超时并返回 504 (Gateway Timeout)
  • Retry-After 响应头响应,指示客户端应等待多 少秒后重试请求。

如果您请求 API 服务器无法识别的资源版本,kube-apiserver 还会在 其错误响应中 添加“资源版本太大”的消息。

如果您对无法识别的资源版本发出 **watch** 请求,API 服务器可能会无限期地等待(直到请求超时)使资源版本可用。

上次修改时间:2024 年 6 月 21 日太平洋标准时间下午 11:49:从缓存中一致读取的 Beta 文档 (4b71738c73)