Kubernetes API 概念

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

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

Kubernetes 通过监听支持资源上的高效变更通知

在 Kubernetes API 中,监听是一个动词,用于以流的形式跟踪 Kubernetes 中对象的更改。它用于高效检测更改。

Kubernetes 还提供一致的列表操作,以便 API 客户端可以有效地缓存、跟踪和同步资源的状态。

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

Kubernetes API 术语

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

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

大多数 Kubernetes API 资源类型是对象 – 它们表示集群上概念的具体实例,例如 Pod 或命名空间。较少数量的 API 资源类型是虚拟的,因为它们通常表示对对象的操作,而不是对象,例如权限检查(使用带有 JSON 编码的 SubjectAccessReview 主体的 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 会根据现有对象的状态在内部将其分类为 createupdateupdatepatch 不同;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 - 返回 NAMESPACE 中资源类型的所有实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME - 返回 NAMESPACE 中具有 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 参考。不可能跨多个资源访问子资源 - 如果需要,通常会使用新的虚拟资源类型。

HTTP 媒体类型

通过 HTTP,Kubernetes 支持 JSON 和 Protobuf 线编码。

默认情况下,Kubernetes 使用 application/json 媒体类型以JSON 序列化返回对象。尽管 JSON 是默认值,但客户端可以请求 YAML 格式的响应,或者使用更高效的二进制 Protobuf 表示形式以获得更好的规模性能。

Kubernetes API 实现了标准的 HTTP 内容类型协商:使用 GET 调用传递 Accept 标头将请求服务器尝试以你首选的媒体类型返回响应。如果你想为 PUTPOST 请求将 Protobuf 中的对象发送到服务器,你必须适当地设置 Content-Type 请求标头。

如果你请求可用的媒体类型,API 服务器将返回带有合适的 Content-Type 的响应;如果你请求的媒体类型都不支持,API 服务器将返回 406 Not acceptable 错误消息。所有内置资源类型都支持 application/json 媒体类型。

JSON 资源编码

Kubernetes API 默认使用JSON 来编码 HTTP 消息体。

例如

  1. 列出集群上的所有 Pod,而不指定首选格式

    GET /api/v1/pods
    
    200 OK
    Content-Type: application/json
    
    … JSON encoded collection of Pods (PodList object)
    
  2. 通过将 JSON 发送到服务器来创建一个 Pod,请求 JSON 响应。

    POST /api/v1/namespaces/test/pods
    Content-Type: application/json
    Accept: application/json
    … JSON encoded Pod object
    
    200 OK
    Content-Type: application/json
    
    {
      "kind": "Pod",
      "apiVersion": "v1",
      …
    }
    

YAML 资源编码

Kubernetes 还支持 application/yaml 媒体类型进行请求和响应。 YAML 可用于定义 Kubernetes 清单和 API 交互。

例如

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

    GET /api/v1/pods
    Accept: application/yaml
    
    200 OK
    Content-Type: application/yaml
    
    … YAML encoded collection of Pods (PodList object)
    
  2. 通过将 YAML 编码的数据发送到服务器来创建一个 Pod,请求 YAML 响应

    POST /api/v1/namespaces/test/pods
    Content-Type: application/yaml
    Accept: application/yaml
    … YAML encoded Pod object
    
    200 OK
    Content-Type: application/yaml
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: my-pod
      …
    

Kubernetes Protobuf 编码

Kubernetes 使用一个信封包装器来编码 Protobuf 响应。该包装器以 4 字节的幻数开头,以帮助识别磁盘或 etcd 中的内容为 Protobuf(而不是 JSON)。这 4 字节的幻数数据后面跟着一个 Protobuf 编码的包装器消息,该消息描述了底层对象的编码和类型。在 Protobuf 包装器消息中,内部对象数据使用 Unknown 的 raw 字段记录(有关更多详细信息,请参阅 IDL)。

例如

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

    GET /api/v1/pods
    Accept: application/vnd.kubernetes.protobuf
    
    200 OK
    Content-Type: application/vnd.kubernetes.protobuf
    
    … JSON encoded collection of Pods (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",
      ...
    }
    

你可以将这两种技术结合使用,并使用 Kubernetes 的 Protobuf 编码与任何支持它的 API 进行交互,无论是读取还是写入。只有一些 API 资源类型与 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;
  }

与 Kubernetes Protobuf 的兼容性

并非所有 API 资源类型都支持 Kubernetes 的 Protobuf 编码;具体来说,Protobuf 不适用于定义为 CustomResourceDefinitions 或通过 聚合层 提供的资源。

作为客户端,如果可能需要使用扩展类型,则应在请求的 Accept 标头中指定多种内容类型,以支持回退到 JSON。例如

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

CBOR 资源编码

功能状态: Kubernetes v1.32 [alpha](默认禁用:false)

启用 CBORServingAndStorage 功能门 后,所有内置资源类型和 CustomResourceDefinition 定义的所有资源的请求和响应主体都可以编码为 CBOR 二进制数据格式。如果 CBOR 在各个聚合 API 服务器中启用,则它也支持 聚合层

当请求正文包含单个 CBOR 编码的数据项时,客户端应在 Content-Type HTTP 请求标头中指示 IANA 媒体类型 application/cbor,并在准备接受响应中 CBOR 编码的数据项时,在 Accept HTTP 请求标头中指示。当响应正文包含 CBOR 编码的对象时,API 服务器将在 Content-Type HTTP 响应标头中使用 application/cbor

如果 API 服务器使用 CBOR 将其响应编码为 watch 请求,则响应正文将是一个 CBOR 序列,并且 Content-Type HTTP 响应标头将使用 IANA 媒体类型 application/cbor-seq。序列的每个条目(如果有)都是一个单独的 CBOR 编码的 watch 事件。

除了现有的 application/apply-patch+yaml 媒体类型用于 YAML 编码的 服务器端应用配置之外,启用 CBOR 的 API 服务器将接受 application/apply-patch+cbor 媒体类型用于 CBOR 编码的服务器端应用配置。application/json-patch+jsonapplication/merge-patch+jsonapplication/strategic-merge-patch+json 没有支持的 CBOR 等效项。

高效检测更改

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

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

当你发送一个 watch 请求时,API 服务器会响应一个更改流。这些更改列出了在您指定为 watch 请求参数的 resourceVersion 之后发生的(例如 createdeleteupdate)操作的结果。整个 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 操作(如 createdeletepatchupdate)的通知。每个更改通知都是一个 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、清除其本地缓存、执行新的 getlist 操作,并从返回的 resourceVersion 开始 watch 来处理这种情况。

对于订阅集合,Kubernetes 客户端库通常为此 list-then-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.32 [beta](默认启用:true)

在大型集群上,检索某些资源类型的集合可能会导致控制平面上资源使用量(主要是 RAM)的显著增加。为了减轻影响并简化 list + watch 模式的用户体验,Kubernetes v1.32 将允许请求初始状态(之前通过 list 请求请求)作为 watch 请求的一部分的功能提升为 beta 版。

在客户端,可以通过在 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](默认启用:true)

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](默认启用:true)

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

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

你可以请求 API 服务器通过使用页面(Kubernetes 称为 )来处理单个集合的 list。为了分块检索单个集合,在对集合的请求中支持两个查询参数 limitcontinue,并且在集合的 metadata 字段中,所有 list 操作都返回一个响应字段 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 的一致快照。除非您在不使用 continue 令牌的情况下发出单独的 list 请求,否则不会显示在版本 10245 之后创建、更新或删除的 Pod。这允许您将大型请求分成较小的块,然后对整个集合执行 watch 操作,而不会遗漏任何更新。

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

集合

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

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

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

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

资源删除

当您 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 中删除。

强制删除

功能状态: Kubernetes v1.32 [alpha](默认禁用:false)

通过启用删除选项 ignoreStoreReadErrorWithClusterBreakingPotential,用户可以对不可解密/损坏的资源执行不安全的强制 delete 操作。此选项位于 ALPHA 功能门之后,默认情况下处于禁用状态。为了使用此选项,集群操作员必须通过设置命令行选项 --feature-gates=AllowUnsafeMalformedObjectDeletion=true 来启用该功能。

如果由于 a) 转换错误(例如:解密失败)或 b) 对象无法解码而无法从存储中成功检索资源,则该资源被认为是损坏的。API 服务器首先尝试正常删除,如果删除失败并出现*损坏的资源*错误,则会触发强制删除。强制 delete 操作是不安全的,因为它会忽略终结器约束并跳过前提条件检查。

此选项的默认值为 false,这保持了向后兼容性。对于将 ignoreStoreReadErrorWithClusterBreakingPotential 设置为 truedelete 请求,必须将字段 dryRungracePeriodSecondsorphanDependentspreconditionspropagationPolicy 保持未设置状态。

单个资源 API

Kubernetes API 动词 getcreateupdatepatchdeleteproxy 仅支持单个资源。这些支持单个资源的动词不支持在有序或无序列表或事务中一起提交多个资源。

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

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

字段验证

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

如果您发出包含额外字段的请求,即集群的控制平面无法识别的字段,则 API 服务器的行为会更加复杂。

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

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

这两种情况是

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

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

功能状态: Kubernetes v1.27 [稳定](默认启用:true)

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

Ignore
API 服务器成功处理该请求,就像没有设置错误字段一样,删除所有未知和重复的字段,并且不给出它已这样做的任何指示。
Warn
(默认)API 服务器成功处理请求,并向客户端报告警告。警告通过 Warning: 响应头发送,为每个未知或重复的字段添加一个警告项。有关警告和 Kubernetes API 的更多信息,请参阅博客文章警告:前方有帮助性的警告
严格
当 API 服务器检测到任何未知或重复字段时,它会拒绝请求并返回 400 Bad Request 错误。来自 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 [稳定] (默认启用:true)

当您使用可以修改资源的 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 地址

模拟运行授权

模拟运行和非模拟运行请求的授权是相同的。因此,要发出模拟运行请求,您必须获得执行非模拟运行请求的授权。

例如,要为 Deployment 运行模拟运行 patch,您必须获得执行该 patch 的授权。这是一个 Kubernetes RBAC 规则的示例,该规则允许修补 Deployment

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

请参阅授权概述

更新现有资源

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

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

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

Kubernetes API 支持四种不同的 PATCH 操作,由它们相应的 HTTP Content-Type 标头确定

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

使用 application/json-patch+jsonpatch 可以包括验证一致性的条件,如果未满足这些条件,则操作将失败(例如,避免丢失更新)。

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

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

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

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

选择更新机制

HTTP PUT 替换现有资源

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

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

HTTP PATCH 使用 JSON Patch

patch 更新很有帮助,因为

  • 由于您只发送差异,因此在 PATCH 请求中发送的数据更少。
  • 您可以进行依赖现有值的更改,例如将特定字段的值复制到注释中。
  • 更新(HTTP PUT)不同,即使不相关的字段频繁更改,你的更改也可以立即生效:你通常不需要重试。
    • 如果你想格外小心以避免更新丢失,你可能仍然需要指定 resourceVersion(以匹配现有对象)
    • 为了防止出错,编写一些重试逻辑仍然是一个好习惯。
  • 你可以使用测试条件来精心设计特定的更新条件。例如,如果现有值与你期望的值匹配,你可以在不读取它的情况下递增一个计数器。即使自你上次写入以来对象以其他方式发生了更改,你也可以这样做,而不会有更新丢失的风险。(如果测试条件失败,你可以回退到读取当前值,然后再写回更改后的数字)。

然而

  • 你需要更多的本地(客户端)逻辑来构建补丁;如果你有一个 JSON Patch 的库实现,甚至专门针对 Kubernetes 创建 JSON Patch,这将有很大的帮助。
  • 作为客户端软件的作者,你在构建补丁(HTTP 请求体)时需要小心,不要丢弃字段(操作顺序很重要)。

使用服务器端应用的 HTTP PATCH

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

  • 一次往返:它很少需要先发出 GET 请求。
    • 你仍然可以检测到意外更改的冲突
    • 如果合适,你可以选择强制覆盖冲突
  • 客户端实现很容易创建。
  • 你可以获得一个原子创建或更新操作,而无需额外的努力(类似于某些 SQL 方言中的 UPSERT)。

然而

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

资源版本

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

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

metadata 中的 resourceVersion 字段

客户端可以在资源中找到资源版本,包括来自watch的响应流中的资源,或者在使用list枚举资源时找到。

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

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

查询字符串中的 resourceVersion 参数

getlistwatch 操作都支持 resourceVersion 参数。从 v1.19 版本开始,Kubernetes API 服务器还支持 list 请求中的 resourceVersionMatch 参数。

API 服务器根据你请求的操作以及 resourceVersion 的值以不同的方式解释 resourceVersion 参数。如果你设置了 resourceVersionMatch,那么这也会影响匹配的发生方式。

getlist 的语义

对于 getlistresourceVersion 的语义是

get

resourceVersion 未设置resourceVersion="0"resourceVersion="{0 以外的值}"
最新任意不早于

list

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

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

除非你具有强一致性要求,否则使用 resourceVersionMatch=NotOlderThan 和已知的 resourceVersion 是首选,因为它可以比不设置 resourceVersionresourceVersionMatch 更好地实现群集的性能和可伸缩性,后者需要提供仲裁读取。

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

下表说明了具有各种 resourceVersionresourceVersionMatch 组合的 list 请求的行为

list 的 resourceVersionMatch 和分页参数
resourceVersionMatch 参数分页参数resourceVersion 未设置resourceVersion="0"resourceVersion="{0 以外的值}"
未设置limit 未设置最新任意不早于
未设置limit=<n>,continue 未设置最新任意精确
未设置limit=<n>,continue=<token>继续令牌,精确无效,视为继续令牌,精确无效,HTTP 400 Bad Request
resourceVersionMatch=Exactlimit 未设置无效无效精确
resourceVersionMatch=Exactlimit=<n>,continue 未设置无效无效精确
resourceVersionMatch=NotOlderThanlimit 未设置无效任意不早于
resourceVersionMatch=NotOlderThanlimit=<n>,continue 未设置无效任意不早于

getlist 语义的含义是

任意
返回任何资源版本的数据。首选最新的可用资源版本,但不需要强一致性;可以提供任何资源版本的数据。由于分区或过时的缓存,请求可能会返回比客户端先前观察到的旧得多的资源版本的数据,尤其是在高可用性配置中。无法容忍这种情况的客户端不应使用此语义。
最新
返回最新资源版本的数据。返回的数据必须是一致的(详细地说:通过仲裁读取从 etcd 提供)。对于 etcd v3.4.31+ 和 v3.5.13+,Kubernetes 1.32 从观察缓存提供“最新”读取:API 服务器内部的内存存储,用于缓存和镜像持久化到 etcd 中的数据状态。Kubernetes 请求进度通知以维持缓存与 etcd 持久化层的一致性。Kubernetes 版本 v1.28 到 v1.30 也支持此功能,尽管作为 Alpha 版,不建议用于生产,直到 v1.31 版本才默认启用。
不早于
返回至少与提供的 resourceVersion 一样新的数据。首选最新的可用数据,但可以提供不早于提供的 resourceVersion 的任何数据。对于向遵守 resourceVersionMatch 参数的服务器发出的 list 请求,这保证了集合的 .metadata.resourceVersion 不早于请求的 resourceVersion,但不保证该集合中任何项的 .metadata.resourceVersion
精确
返回提供的确切资源版本的数据。如果提供的 resourceVersion 不可用,则服务器会以 HTTP 410 "Gone" 响应。对于向遵守 resourceVersionMatch 参数的服务器发出的 list 请求,这保证了集合的 .metadata.resourceVersion 与你在查询字符串中请求的 resourceVersion 相同。该保证不适用于该集合中任何项的 .metadata.resourceVersion
继续令牌,精确
返回初始分页 list 调用的资源版本的数据。返回的继续令牌负责跟踪所有分页 list 调用在初始分页 list 之后最初提供的资源版本。

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

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

watch 的语义

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

watch

watch 的 resourceVersion
resourceVersion 未设置resourceVersion="0"resourceVersion="{0 以外的值}"
获取状态并从最新开始获取状态并从任意开始从精确开始

这些 watch 语义的含义是

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

"410 Gone" 响应

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

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

不可用的资源版本

服务器不需要提供无法识别的资源版本。如果请求 listget 的资源版本是 API 服务器无法识别的,那么 API 服务器可能会

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

如果您请求的资源版本 API 服务器无法识别,kube-apiserver 还会使用“资源版本过大”的消息来标识其错误响应。

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