这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

API 概述

本文提供了 Kubernetes API 的参考信息。

REST API 是 Kubernetes 的基本结构。 所有操作和组件之间的通信及外部用户命令都是调用 API 服务器处理的 REST API。 因此,Kubernetes 平台视一切皆为 API 对象, 且它们在 API 中有相应的定义。

Kubernetes API 参考 列出了 Kubernetes v1.31 版本的 API。

如需了解一般背景信息,请查阅 Kubernetes APIKubernetes API 控制访问描述了客户端如何向 Kubernetes API 服务器进行身份认证以及他们的请求如何被鉴权。

API 版本控制

JSON 和 Protobuf 序列化模式遵循相同的模式更改原则。 以下描述涵盖了这两种格式。

API 版本控制和软件版本控制是间接相关的。 API 和发布版本控制提案描述了 API 版本控制和软件版本控制间的关系。

不同的 API 版本代表着不同的稳定性和支持级别。 你可以在 API 变更文档 中查看到更多的不同级别的判定标准。

下面是每个级别的摘要:

  • Alpha:
    • 版本名称包含 alpha(例如:v1alpha1)。
    • 内置的 Alpha API 版本默认被禁用且必须在 kube-apiserver 配置中显式启用才能使用。
    • 软件可能会有 Bug。启用某个特性可能会暴露出 Bug。
    • 对某个 Alpha API 特性的支持可能会随时被删除,恕不另行通知。
    • API 可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
    • 由于缺陷风险增加和缺乏长期支持,建议该软件仅用于短期测试集群。
  • Beta:

    • 版本名称包含 beta(例如:v2beta3)。
    • 内置的 Beta API 版本默认被禁用且必须在 kube-apiserver 配置中显式启用才能使用 (例外情况是 Kubernetes 1.22 之前引入的 Beta 版本的 API,这些 API 默认被启用)。
    • 内置 Beta API 版本从引入到弃用的最长生命周期为 9 个月或 3 个次要版本(以较长者为准), 从弃用到移除的最长生命周期为 9 个月或 3 个次要版本(以较长者为准)。
    • 软件被很好的测试过。启用某个特性被认为是安全的。
    • 尽管一些特性会发生细节上的变化,但它们将会被长期支持。
    • 在随后的 Beta 版或 Stable 版中,对象的模式和(或)语义可能以不兼容的方式改变。 当这种情况发生时,将提供迁移说明。 适配后续的 Beta 或 Stable API 版本可能需要编辑或重新创建 API 对象,这可能并不简单。 对于依赖此功能的应用程序,可能需要停机迁移。
    • 该版本的软件不建议生产使用。 后续发布版本可能会有不兼容的变动。 一旦 Beta API 版本被弃用且不再提供服务, 则使用 Beta API 版本的用户需要转为使用后续的 Beta 或 Stable API 版本。
  • Stable:
    • 版本名称如 vX,其中 X 为整数。
    • 特性的 Stable 版本会出现在后续很多版本的发布软件中。 Stable API 版本仍然适用于 Kubernetes 主要版本范围内的所有后续发布, 并且 Kubernetes 的主要版本当前没有移除 Stable API 的修订计划。

API 组

API 组能够简化对 Kubernetes API 的扩展。API 组信息出现在 REST 路径中,也出现在序列化对象的 apiVersion 字段中。

以下是 Kubernetes 中的几个组:

  • 核心(core)(也被称为 legacy)组的 REST 路径为 /api/v1。 核心组并不作为 apiVersion 字段的一部分,例如, apiVersion: v1
  • 指定的组位于 REST 路径 /apis/$GROUP_NAME/$VERSION, 并且使用 apiVersion: $GROUP_NAME/$VERSION (例如,apiVersion: batch/v1)。 你可以在 Kubernetes API 参考文档 中查看全部的 API 组。

启用或禁用 API 组

资源和 API 组是在默认情况下被启用的。 你可以通过在 API 服务器上设置 --runtime-config 参数来启用或禁用它们。 --runtime-config 参数接受逗号分隔的 <key>[=<value>] 对, 来描述 API 服务器的运行时配置。如果省略了 =<value> 部分,那么视其指定为 =true。 例如:

  • 禁用 batch/v1,对应参数设置 --runtime-config=batch/v1=false
  • 启用 batch/v2alpha1,对应参数设置 --runtime-config=batch/v2alpha1
  • 要启用特定版本的 API,如 storage.k8s.io/v1beta1/csistoragecapacities,可以设置 --runtime-config=storage.k8s.io/v1beta1/csistoragecapacities

持久化

Kubernetes 通过 API 资源来将序列化的状态写到 etcd 中存储。

接下来

1 - Kubernetes API 概念

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

对于某些资源,API 包括额外的子资源,允许细粒度授权(例如:将 Pod 的详细信息与检索日志分开), 为了方便或者提高效率,可以以不同的表示形式接受和服务这些资源。

Kubernetes 支持通过 watch 实现高效的资源变更通知:

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

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

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

Kubernetes API 术语

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

  • 资源类型(Resource Type) 是 URL 中使用的名称(podsnamespacesservices
  • 所有资源类型都有一个具体的表示(它们的对象模式),称为 类别(Kind)
  • 资源类型的实例的列表称为 集合(Collection)
  • 资源类型的单个实例称为 资源(Resource),通常也表示一个 对象(Object)
  • 对于某些资源类型,API 包含一个或多个 子资源(sub-resources),这些子资源表示为资源下的 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 在内部根据现有对象的状态将它们分类为 createupdateupdate 不同于 patchpatch 的 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

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

  • 集群作用域的资源:

    • 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 编码的 Pod 集合(PodList 对象)
    
  1. 通过向服务器发送 JSON 并请求 JSON 响应来创建 Pod。

    POST /api/v1/namespaces/test/pods
    Content-Type: application/json
    Accept: application/json
    … JSON 编码的 Pod 对象
    
    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 编码的 Pod 集合(PodList 对象)
    
  1. 通过向服务器发送 YAML 编码的数据并请求 YAML 响应来创建 Pod:

    POST /api/v1/namespaces/test/pods
    Content-Type: application/yaml
    Accept: application/yaml
    … YAML 编码的 Pod 对象
    
    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 编码的 Pod 集合(PodList 对象)
    
  1. 通过向服务器发送 Protobuf 编码的数据创建 Pod,但请求以 JSON 形式接收响应:

    POST /api/v1/namespaces/test/pods
    Content-Type: application/vnd.kubernetes.protobuf
    Accept: application/json
    … 二进制编码的 Pod 对象
    
    200 OK
    Content-Type: application/json
    
    {
      "kind": "Pod",
      "apiVersion": "v1",
      ...
    }
    

你可以将这两种技术结合使用,利用 Kubernetes 的 Protobuf 编码与所有支持的 API 进行读写交互。 只有某些 API 资源类型与 Protobuf 兼容

封套格式如下:

四个字节的特殊数字前缀:
  字节 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;
  }

与 Kubernetes Protobuf 的兼容性

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

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

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

高效检测变更

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": [...]
    }
    
  1. 从资源版本 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 服务器只会保留一定的时间内发生的历史变更列表。 使用 etcd3 的集群默认保存过去 5 分钟内发生的变更。 当所请求的 watch 操作因为资源的历史版本不存在而失败, 客户端必须能够处理因此而返回的状态代码 410 Gone,清空其本地的缓存, 重新执行 get 或者 list 操作, 并基于新返回的 resourceVersion 来开始新的 watch 操作。

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

监视书签

为了减轻短历史窗口的影响,Kubernetes API 提供了一个名为 BOOKMARK 的监视事件。 这是一种特殊的事件,用于标记客户端请求的给定 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"} }
}

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

流式列表

特性状态: Kubernetes v1.27 [alpha] (enabled by default: false)

在大型集群检索某些资源类型的集合可能会导致控制平面的资源使用量(主要是 RAM)显著增加。 为了减轻其影响并简化 list + watch 模式的用户体验, Kubernetes 1.27 版本引入了一个 alpha 功能,支持在 watch 请求中请求初始状态 (之前在 list 请求中请求)。

如果启用了 WatchList 特性门控, 可以通过在 watch 请求中指定 sendInitialEvents=true 作为查询字符串参数来实现这一功能。 如果指定了这个参数,API 服务器将使用合成的初始事件(类型为 ADDED)来启动监视流, 以构建所有现有对象的完整状态;如果请求还带有 allowWatchBookmarks=true 选项, 则继续发送 BOOKMARK 事件。 BOOKMARK 事件包括已被同步的资源版本。 发送 BOOKMARK 事件后,API 服务器会像处理所有其他 watch 请求一样继续执行。

当你在查询字符串中设置 sendInitialEvents=true 时, Kubernetes 还要求你将 resourceVersionMatch 的值设置为 NotOlderThan。 如果你在查询字符串中提供 resourceVersion 而没有提供值或者根本没有提供这个参数, 这一请求将被视为 一致性读(Consistent Read) 请求; 当状态至少被同步到开始处理一致性读操作时,才会发送 BOOKMARK 事件。 如果你(在查询字符串中)指定了 resourceVersion,则只要需要等状态同步到所给资源版本时, BOOKMARK 事件才会被发送。

示例

举个例子:你想监视一组 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] (enabled by default: true)

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

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

要验证 APIResponseCompression 是否正常工作,你可以使用一个 Accept-Encoding 头向 API 服务器发送一个 getlist 请求,并检查响应大小和头信息。例如:

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

content-encoding 头表示响应使用 gzip 进行了压缩。

分块检视大体量结果

特性状态: Kubernetes v1.29 [stable] (enabled by default: true)

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

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

你可以请求 API 服务器通过使用页(Kubernetes 将其称为“块(Chunk)”)的方式来处理 list, 完成单个集合的响应。 要以块的形式检索单个集合,针对集合的请求支持两个查询参数 limitcontinue, 并且从集合元 metadata 字段中的所有 list 操作返回响应字段 continue。 客户端应该指定他们希望在每个带有 limit 的块中接收的条目数上限,如果集合中有更多资源, 服务器将在结果中返回 limit 资源并包含一个 continue 值。

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

watch 操作类似,continue 令牌也会在很短的时间(默认为 5 分钟)内过期, 并在无法返回更多结果时返回 410 Gone 代码。 这时,客户端需要从头开始执行上述检视操作或者忽略 limit 参数。

例如,如果集群上有 1253 个 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
    }
    
  1. 继续前面的调用,返回下一组 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
    }
    
  1. 继续前面的调用,返回最后 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 将不会显示, 除非你在没有继续令牌的情况下发出单独的 list 请求。 这使你可以将大请求分成更小的块,然后对整个集合执行 watch 操作,而不会丢失任何更新。

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

集合

在 Kubernetes 术语中,你从 list 中获得的响应是一个“集合(Collections)”。 然而,Kubernetes 为不同类型资源的集合定义了具体类型。 集合的类别名是针对资源类别的,并附加了 List

当你查询特定类型的 API 时,该查询返回的所有项目都属于该类型。 例如,当你 list Service 对象时,集合响应的 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 聚合或第三方资源提供的类型在编译时是未知的。 这意味着必须为客户端无法识别的类型提供通用实现。

为了避免上述各种潜在的局限性,客户端可以请求服务器端返回对象的表格(Table) 表现形式,从而将打印输出的特定细节委托给服务器。 Kubernetes API 实现标准的 HTTP 内容类型(Content Type)协商:为 GET 调用传入一个值为 application/json;as=Table;g=meta.k8s.io;v=v1Accept 头部即可请求服务器以 Table 的内容类型返回对象。

例如,以 Table 格式列举集群中所有 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": [
        ...
    ]
}

对于在控制平面上不存在定制的 Table 定义的 API 资源类型而言,服务器会返回一个默认的 Table 响应,其中包含资源的 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 资源类型都支持 Table 响应; 例如,CustomResourceDefinitions 可能没有定义字段到表的映射, 扩展核心 Kubernetes API 的 APIService 可能根本不提供 Table 响应。 如果你正在实现使用 Table 信息并且必须针对所有资源类型(包括扩展)工作的客户端, 你应该在 Accept 请求头中指定多种内容类型的请求。例如:

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

资源删除

当你 delete 资源时,操作将分两个阶段进行。

  1. 终结(finalization)
  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 动词 getcreateupdatepatchdeleteproxy 仅支持单一资源。 这些具有单一资源支持的动词不支持在有序或无序列表或事务中一起提交多个资源。

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

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

字段校验

Kubernetes 总是校验字段的类型。例如,如果 API 中的某个字段被定义为数值, 你就不能将该字段设置为文本类型的值。如果某个字段被定义为字符串数组,你只能提供数组。 有些字段可以忽略,有些字段必须填写。忽略 API 请求中的必填字段会报错。

如果请求中带有集群控制面无法识别的额外字段,API 服务器的行为会更加复杂。

默认情况下,如果接收到的输入信息中含有 API 服务器无法识别的字段,API 服务器会丢弃该字段 (例如:PUT 请求中的 JSON 主体)。

API 服务器会在两种情况下丢弃 HTTP 请求中提供的字段。

这些情况是:

  1. 相关资源的 OpenAPI 模式定义中没有该字段,因此无法识别该字段(有种例外情形是, CRD 通过 x-kubernetes-preserve-unknown-fields 显式选择不删除未知字段)。
  1. 字段在对象中重复出现。

检查无法识别或重复的字段

特性状态: Kubernetes v1.27 [stable] (enabled by default: true)

从 1.25 开始,当使用可以提交数据的 HTTP 动词(POSTPUTPATCH)时, 将通过服务器上的校验检测到对象中无法识别或重复的字段。 校验的级别可以是 IgnoreWarn(默认值) 和 Strict 之一。

Ignore
使 API 服务器像没有遇到错误字段一样成功处理请求,丢弃所有的未知字段和重复字段,并且不发送丢弃字段的通知。

Warn :(默认值)使 API 服务器成功处理请求,并向客户端发送告警信息。告警信息通过 Warning: 响应头发送, 并为每个未知字段或重复字段添加一条告警信息。有关告警和相关的 Kubernetes API 的信息, 可参阅博文告警:增加实用告警功能

Strict
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 [stable] (enabled by default: true)

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

发起试运行请求

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

[未设置值]
允许副作用。你可以使用 ?dryRun?dryRun&pretty=true 之类的查询字符串请求此操作。 响应是最终会被持久化的对象,或者如果请求不能被满足则会出现一个错误。
All
每个阶段都正常运行,除了防止副作用的最终存储阶段。

当你设置 ?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-apiserverService 对象分配的端口和 IP 地址

试运行的授权

试运行和非试运行请求的鉴权是完全相同的。因此,要发起一个试运行请求, 你必须被授权执行非试运行请求。

例如,要在 Deployment 对象上试运行 patch 操作,你必须具有对 Deployment 执行 patch 操作的访问权限, 如下面的 RBAC 规则所示:

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

参阅鉴权概述以了解鉴权细节。

更新现有资源

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

你可以使用 HTTP PUT 覆盖(update)ConfigMap 等现有资源。 对于 PUT 请求,客户端需要指定 resourceVersion(从要更新的对象中获取此项)。 Kubernetes 使用该 resourceVersion 信息,这样 API 服务器可以检测丢失的更新并拒绝对集群来说过期的客户端所发出的请求。 如果资源已发生变化(即客户端提供的 resourceVersion 已过期),API 服务器将返回 409 Conflict 错误响应。

客户端除了发送 PUT 请求之外,还可以发送指令给 API 服务器对现有资源执行 patch 操作。 patch 通常适用于客户端希望进行的更改并不依赖于现有数据的场景。 需要有效检测丢失更新的客户端应该考虑根据现有 resourceVersion 来进行有条件的请求 (HTTP PUT 或 HTTP PATCH),并在存在冲突时作必要的重试。

Kubernetes API 支持四种不同的 PATCH 操作,具体取决于它们所对应的 HTTP Content-Type 标头:

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

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

application/merge-patch+json
JSON Merge Patch,如 RFC7386 中定义。 JSON Merge Patch 实质上是资源的部分表示。提交的 JSON 与当前资源合并以创建一个新资源,然后将其保存。 对于 Kubernetes,这个 PATCH 请求是一个 patch 操作。
application/strategic-merge-patch+json
Strategic Merge Patch(基于 JSON 的 Kubernetes 扩展)。 Strategic Merge Patch 是 JSON Merge Patch 的自定义实现。 你只能在内置 API 或具有特殊支持的聚合 API 服务器中使用 Strategic Merge Patch。 你不能针对任何使用 CustomResourceDefinition 定义的 API 来使用 application/strategic-merge-patch+json

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

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

更多细节参阅服务器端应用

选择更新机制

HTTP PUT 替换现有资源

update(HTTP PUT)操作实现简单且灵活,但也存在一些缺点:

  • 你需要处理对象的 resourceVersion 在客户端读取和写回之间发生变化所造成的冲突。 Kubernetes 总是会检测到此冲突,但你作为客户端开发者需要实现重试机制。
  • 如果你在本地解码对象,可能会意外丢失字段。例如你在使用 client-go 时, 可能会收到客户端不知道如何处理的一些字段,而客户端在构造更新时会将这些字段丢弃。
  • 如果对象上存在大量争用(即使是在你不打算编辑的某字段或字段集上),你可能会难以发送更新。 对于体量较大或字段较多的对象,这个问题会更为严重。

使用 JSON Patch 的 HTTP PATCH

patch 更新很有帮助,因为:

  • 由于你只发送差异,所以你在 PATCH 请求中需要发送的数据较少。
  • 你可以依赖于现有值进行更改,例如将特定字段的值复制到注解中。
  • update(HTTP PUT)不同,即使存在对无关字段的频繁更改,你的更改也可以立即生效: 你通常无需重试。
    • 如果你要特别小心避免丢失更新,仍然可能需要指定 resourceVersion(以匹配现有对象)。
    • 编写一些重试逻辑以处理错误仍然是一个良好的实践。
  • 你可以通过测试条件来精确地构造特定的更新条件。 例如,如果现有值与你期望的值匹配,你可以递增计数器而无需读取它。 即使自上次写入以来对象以其他方式发生了更改,你也可以做到这一点而不会遇到丢失更新的风险。 (如果测试条件失败,你可以回退为读取当前值,然后写回更改的数字)。

然而:

  • 你需要更多本地(客户端)逻辑来构建补丁;如果你拥有实现了 JSON Patch 的库, 或者针对 Kubernetes 生成特定的 JSON Patch 的库,将非常有帮助。
  • 作为客户端软件的开发者,你在构建补丁(HTTP 请求体)时需要小心,避免丢弃字段(操作顺序很重要)。

使用服务器端应用的 HTTP PATCH

服务器端应用(Server-Side Apply)具有一些明显的优势:

  • 仅需一次轮询:通常无需先执行 GET 请求。
    • 并且你仍然可以检测到意外更改造成的冲突
    • 合适的时候,你可以选择强制覆盖冲突
  • 客户端实现简单。
  • 你可以轻松获得原子级别的 create 或 update 操作,无需额外工作 (类似于某些 SQL 语句中的 UPSERT)。

然而:

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

资源版本

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

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

metadata 中的 resourceVersion

客户端在资源中查找资源版本,这些资源包括来自用于 watch 的响应流资源,或者使用 list 枚举的资源。

v1.meta/ObjectMeta - 资源的 metadata.resourceVersion 值标明该实例上次被更改时的资源版本。

v1.meta/ListMeta - 资源集合即 list 操作的响应)的 metadata.resourceVersion 所标明的是 list 响应被构造时的资源版本。

查询字符串中的 resourceVersion 参数

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

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

getlist 语义

对于 getlist 而言,resourceVersion 的语义为:

get:

resourceVersion 未设置 resourceVersion="0" resourceVersion="<非零值>"
最新版本 任何版本 不老于给定版本

list:

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

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

除非你对一致性有着非常强烈的需求,使用 resourceVersionMatch=NotOlderThan 同时为 resourceVersion 设定一个已知值是优选的交互方式,因为与不设置 resourceVersionresourceVersionMatch 相比,这种配置可以取得更好的集群性能和可扩缩性。 后者需要提供带票选能力的读操作。

设置 resourceVersionMatch 参数而不设置 resourceVersion 参数是不合法的。

下表解释了具有各种 resourceVersionresourceVersionMatch 组合的 list 请求的行为:

list 操作的 resourceVersionMatch 与分页参数
resourceVersionMatch 参数 分页参数 resourceVersion 未设置 resourceVersion="0" resourceVersion="<非零值>"
未设置 limit 未设置 最新版本 任意版本 不老于指定版本
未设置 limit=<n>, continue 未设置 最新版本 任意版本 精确匹配
未设置 limit=<n>, continue=<token> 从 token 开始、精确匹配 非法请求,视为从 token 开始、精确匹配 非法请求,返回 HTTP 400 Bad Request
resourceVersionMatch=Exact [1] limit 未设置 非法请求 非法请求 精确匹配
resourceVersionMatch=Exact [1] limit=<n>, continue 未设置 非法请求 非法请求 精确匹配
resourceVersionMatch=NotOlderThan [1] limit 未设置 非法请求 任意版本 不老于指定版本
resourceVersionMatch=NotOlderThan [1] limit=<n>, continue 未设置 非法请求 任意版本 不老于指定版本

getlist 的语义是:

任意版本
返回任何资源版本的数据。最新可用资源版本优先,但不需要强一致性; 可以提供任何资源版本的数据。由于分区或过时的缓存, 请求可能返回客户端先前观察到的更旧资源版本的数据,特别是在高可用性配置中。 不能容忍这种情况的客户不应该使用这种语义。
最新版本
返回最新资源版本的数据。 返回的数据必须一致(详细说明:通过仲裁读取从 etcd 提供)。 对于 etcd v3.4.31+ 和 v3.5.13+ Kubernetes 1.31 使用监视缓存来为“最新”读取提供服务: 监视缓存是 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
从 token 开始、精确匹配
返回初始分页 list 调用的资源版本的数据。 返回的 Continue 令牌负责跟踪最初提供的资源版本,最初提供的资源版本用于在初始分页 list 之后的所有分页 list 中。

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

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

watch 语义

对于 watch 操作而言,资源版本的语义如下:

watch:

watch 操作的 resourceVersion 设置
resourceVersion 未设置 resourceVersion="0" resourceVersion="<非零值>"
读取状态并从最新版本开始 读取状态并从任意版本开始 从指定版本开始

watch 操作语义的含义如下:

读取状态并从任意版本开始
在任何资源版本开始 watch;首选可用的最新资源版本,但不是必需的。允许任何起始资源版本。 由于分区或过时的缓存,watch 可能从客户端之前观察到的更旧的资源版本开始, 特别是在高可用性配置中。不能容忍这种明显倒带的客户不应该用这种语义启动 watch。 为了建立初始状态,watch 从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在 watch 开始的资源版本之后发生的所有更改。
读取状态并从最新版本开始
从最近的资源版本开始 watch, 它必须是一致的(详细说明:通过仲裁读取从 etcd 提供服务)。 为了建立初始状态,watch 从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在 watch 开始的资源版本之后发生的所有更改。
从指定版本开始
以确切的资源版本开始 watch。监视事件适用于提供的资源版本之后的所有更改。 与 “Get State and Start at Most Recent” 和 “Get State and Start at Any” 不同, watch 不会以所提供资源版本的合成 “添加” 事件启动。 由于客户端提供了资源版本,因此假定客户端已经具有起始资源版本的初始状态。

"410 Gone" 响应

服务器不需要提供所有老的资源版本,在客户端请求的是早于服务器端所保留版本的 resourceVersion 时,可以返回 HTTP 410 (Gone) 状态码。 客户端必须能够容忍 410 (Gone) 响应。 参阅高效检测变更以了解如何在监测资源时处理 410 (Gone) 响应。

如果所请求的 resourceVersion 超出了可应用的 limit, 那么取决于请求是否是通过高速缓存来满足的,API 服务器可能会返回一个 410 Gone HTTP 响应。

不可用的资源版本

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

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

如果你请求 API 服务器无法识别的资源版本, kube-apiserver 还会使用 “Too large resource version” 消息额外标识其错误响应。

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

2 - 服务器端应用(Server-Side Apply)

特性状态: Kubernetes v1.22 [stable] (enabled by default: true)

Kubernetes 支持多个应用程序协作管理一个对象的字段。 服务器端应用为集群的控制平面提供了一种可选机制,用于跟踪对对象字段的更改。 在特定资源级别,服务器端应用记录并跟踪有关控制该对象字段的信息。

服务器端应用协助用户和控制器通过声明式配置的方式管理他们的资源。 客户提交他们完整描述的意图,声明式地创建和修改对象

一个完整描述的意图并不是一个完整的对象,仅包括能体现用户意图的字段和值。 该意图可以用来创建一个新对象(未指定的字段使用默认值), 也可以通过 API 服务器来实现与现有对象的合并

与客户端应用对比小节解释了服务器端应用与最初的客户端 kubectl apply 实现的区别。

字段管理

Kubernetes API 服务器跟踪所有新建对象的受控字段(Managed Fields)

当尝试应用对象时,由另一个管理器拥有的字段且具有不同值,将导致冲突。 这样做是为了表明操作可能会撤消另一个合作者的更改。 可以强制写入具有托管字段的对象,在这种情况下,任何冲突字段的值都将被覆盖,并且所有权将被转移。

每当字段的值确实发生变化时,所有权就会从其当前管理器转移到进行更改的管理器。

服务器端应用会检查是否存在其他字段管理器也拥有该字段。 如果该字段不属于任何其他字段管理器,则该字段将被设置为其默认值(如果有),或者以其他方式从对象中删除。 同样的规则也适用于作为列表(list)、关联列表或键值对(map)的字段。

用户管理字段这件事,在服务器端应用的场景中,意味着用户依赖并期望字段的值不要改变。 最后一次对字段值做出断言的用户将被记录到当前字段管理器。 这可以通过发送 POSTcreate)、PUTupdate)、或非应用的 PATCHpatch) 显式更改字段管理器详细信息来实现。 你还可以通过在服务器端应用操作中包含字段的值来声明和记录字段管理器。

服务器端应用场景中的 patch 请求要求客户端提供自身的标识作为字段管理器(Field Manager)。 使用服务器端应用时,如果尝试变更由别的管理器来控制的字段,会导致请求被拒绝,除非客户端强制要求进行覆盖。 关于覆盖操作的细节,可参阅冲突节。

如果两个或以上的应用者均把同一个字段设置为相同值,他们将共享此字段的所有权。 后续任何改变共享字段值的尝试,不管由那个应用者发起,都会导致冲突。 共享字段的所有者可以放弃字段的所有权,这只需发出不包含该字段的服务器端应用 patch 请求即可。

字段管理的信息存储在 managedFields 字段中,该字段是对象的 metadata 中的一部分。

如果从清单中删除某个字段并应用该清单,则服务器端应用会检查是否有其他字段管理器也拥有该字段。 如果该字段不属于任何其他字段管理器,则服务器会将其从活动对象中删除,或者重置为其默认值(如果有)。 同样的规则也适用于关联列表(list)或键值对(map)。

与(旧版)由 kubectl 所管理的注解 kubectl.kubernetes.io/last-applied configuration 相比,服务器端应用使用了一种更具声明式的方法, 它跟踪用户(或客户端)的字段管理,而不是用户上次应用的状态。 作为服务器端应用的副作用,哪个字段管理器管理的对象的哪个字段的相关信息也会变得可用。

示例

服务器端应用创建对象的简单示例如下:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply # 注意大写:“Apply” (或者 “Update”)
    apiVersion: v1
    time: "2010-10-10T0:00:00Z"
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:labels:
          f:test-label: {}
      f:data:
        f:key: {}
data:
  key: some value

示例的 ConfigMap 对象在 .metadata.managedFields 中包含字段管理记录。 字段管理记录包括关于管理实体本身的基本信息,以及关于被管理的字段和相关操作(ApplyUpdate)的详细信息。 如果最后更改该字段的请求是服务器端应用的 patch 操作,则 operation 的值为 Apply;否则为 Update

还有另一种可能的结果。客户端会提交无效的请求体。 如果完整描述的意图没有构造出有效的对象,则请求失败。

但是,可以通过 update 或不使用服务器端应用的 patch 操作去更新 .metadata.managedFields。 强烈不鼓励这么做,但当发生如下情况时, 比如 managedFields 进入不一致的状态(显然不应该发生这种情况), 这么做也是一个合理的尝试。

managedFields 的格式在 Kubernetes API 参考中描述

冲突

冲突是一种特定的错误状态, 发生在执行 Apply 改变一个字段,而恰巧该字段被其他用户声明过主权时。 这可以防止一个应用者不小心覆盖掉其他用户设置的值。 冲突发生时,应用者有三种办法来解决它:

  • 覆盖前值,成为唯一的管理器: 如果打算覆盖该值(或应用者是一个自动化部件,比如控制器), 应用者应该设置查询参数 force 为 true(对 kubectl apply 来说,你可以使用命令行参数 --force-conflicts),然后再发送一次请求。 这将强制操作成功,改变字段的值,从所有其他管理器的 managedFields 条目中删除指定字段。
  • 不覆盖前值,放弃管理权: 如果应用者不再关注该字段的值, 应用者可以从资源的本地模型中删掉它,并在省略该字段的情况下发送请求。 这就保持了原值不变,并从 managedFields 的应用者条目中删除该字段。
  • 不覆盖前值,成为共享的管理器: 如果应用者仍然关注字段值,并不想覆盖它, 他们可以更改资源的本地模型中该字段的值,以便与服务器上对象的值相匹配, 然后基于本地更新发出新请求。这样做会保持值不变, 并使得该字段的管理由应用者与已经声称管理该字段的所有其他字段管理者共享。

字段管理器

管理器识别出正在修改对象的工作流程(在冲突时尤其有用), 并且可以作为修改请求的一部分,通过 fieldManager 查询参数来指定。 当你 Apply 某个资源时,需要指定 fieldManager 参数。 对于其他更新,API 服务器使用 “User-Agent:” HTTP 头(如果存在)推断字段管理器标识。

当你使用 kubectl 工具执行服务器端应用操作时,kubectl 默认情况下会将管理器标识设置为 “kubectl”

序列化

在协议层面,Kubernetes 用 YAML 来表示服务器端应用的消息体, 媒体类型为 application/apply-patch+yaml

序列化与 Kubernetes 对象相同,只是客户端不需要发送完整的对象。

以下是服务器端应用消息正文的示例(完整描述的意图):

{
  "apiVersion": "v1",
  "kind": "ConfigMap"
}

(这个请求将导致无更改的更新,前提是它作为 patch 请求的主体发送到有效的 v1/configmaps 资源, 并且请求中设置了合适的 Content-Type)。

字段管理范围内的操作

考虑字段管理的 Kubernetes API 操作包括:

  1. 服务器端应用(HTTP PATCH,内容类型为 application/apply-patch+yaml
  2. 替换现有对象(对 Kubernetes 而言是 update;HTTP 层面表现为 PUT

这两种操作都会更新 .metadata.managedFields,但行为略有不同。

除非你指定要强制重写,否则应用操作在遇到字段级冲突时总是会失败; 相比之下,如果你使用 update 进行的更改会影响托管字段,那么冲突从来不会导致操作失败。

所有服务器端应用的 patch 请求都必须提供 fieldManager 查询参数来标识自己, 而此参数对于 update 操作是可选的。 最后,使用 Apply 操作时,不能在提交的请求主体中设置 managedFields

一个包含多个管理器的对象,示例如下:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply
    apiVersion: v1
    fields:
      f:metadata:
        f:labels:
          f:test-label: {}
  - manager: kube-controller-manager
    operation: Update
    apiVersion: v1
    time: '2019-03-30T16:00:00.000Z'
    fields:
      f:data:
        f:key: {}
data:
  key: new value

在这个例子中, 第二个操作被管理器 kube-controller-managerupdate 的方式运行。 更新操作执行成功,并更改了 data 字段中的一个值, 并使得该字段的管理器被改为 kube-controller-manager

如果尝试把更新操作改为服务器端应用,那么这一尝试会因为所有权冲突的原因,导致操作失败。

合并策略

由服务器端应用实现的合并策略,提供了一个总体更稳定的对象生命周期。 服务器端应用试图依据负责管理它们的主体来合并字段,而不是根据值来否决。 这么做是为了多个主体可以更新同一个对象,且不会引起意外的相互干扰。

当用户发送一个完整描述的意图对象到服务器端应用的服务端点时, 服务器会将它和当前对象做一次合并,如果两者中有重复定义的值,那就以请求体中的为准。 如果请求体中条目的集合不是此用户上一次操作条目的超集, 所有缺失的、没有其他应用者管理的条目会被删除。 关于合并时用来做决策的对象规格的更多信息,参见 sigs.k8s.io/structured-merge-diff.

Kubernetes API(以及为 Kubernetes 实现该 API 的 Go 代码)都允许定义合并策略标记(Merge Strategy Markers)。 这些标记描述 Kubernetes 对象中各字段所支持的合并策略。 Kubernetes 1.16 和 1.17 中添加了一些标记, 对一个 CustomResourceDefinition 来说,你可以在定义自定义资源时设置这些标记。

Golang 标记 OpenAPI 扩展 可接受的值 描述
//+listType x-kubernetes-list-type atomic/set/map 适用于 list。set 适用于仅包含标量元素的列表。其中的元素不可重复。map 仅适用于嵌套了其他类型的列表。列表中的键(参见 listMapKey)不可以重复。atomic 适用于所有类型的列表。如果配置为 atomic,则合并时整个列表会被替换掉。任何时候,只有一个管理器负责管理指定列表。如果配置为 setmap,不同的管理器也可以分开管理不同条目。
//+listMapKey x-kubernetes-list-map-keys 字段名称的列表,例如,["port", "protocol"] 仅当 +listType=map 时适用。取值为字段名称的列表,这些字段值的组合能够唯一标识列表中的条目。尽管可以存在多个键,listMapKey 是单数的,这是因为键名需要在 Go 类型中各自独立指定。键字段必须是标量。
//+mapType x-kubernetes-map-type atomic/granular 适用于 map。 atomic 表示 map 只能被某个管理器整体替换。 granular 表示 map 支持多个管理器各自更新自己的字段。
//+structType x-kubernetes-map-type atomic/granular 适用于 structs;此外,起用法和 OpenAPI 注释与 //+mapType 相同。

若未指定 listType,API 服务器将 patchStrategy=merge 标记解释为 listType=map 并且视对应的 patchMergeKey 标记为 listMapKey 取值。

atomic 列表类型是递归的。

(在 Kubernetes 的 Go 代码中, 这些标记以注释的形式给出,代码作者不需要用字段标记的形式重复指定它们)。

自定义资源和服务器端应用

默认情况下,服务器端应用将自定义资源视为无结构的数据。 所有键被视为 struct 数据类型的字段,所有列表都被视为 atomic 形式。

如果 CustomResourceDefinition 定义了的 schema 包含在上一小节合并策略中定义的注解, 那么在合并此类型的对象时,就会使用这些注解。

拓扑变化时的兼容性

在极少的情况下,CustomResourceDefinition(CRD)的作者或者内置类型可能希望更改其资源中的某个字段的 拓扑配置,同时又不提升版本号。 通过升级集群或者更新 CRD 来更改类型的拓扑信息,与更新现有对象的结果不同。 变更的类型有两种:一种是将字段从 map/set/granular 更改为 atomic, 另一种是做逆向改变。

listTypemapTypestructTypemap/set/granular 改为 atomic 时,现有对象的整个列表、映射或结构的属主都会变为这些类型的 元素之一的属主。这意味着,对这些对象的进一步变更会引发冲突。

当某 listTypemapTypestructTypeatomic 改为 map/set/granular 之一时, API 服务器无法推导这些字段的新的属主。因此,当对象的这些字段 再次被更新时不会引发冲突。出于这一原因,不建议将某类型从 atomic 改为 map/set/granular

以下面的自定义资源为例:

---
apiVersion: example.com/v1
kind: Foo
metadata:
  name: foo-sample
  managedFields:
  - manager: "manager-one"
    operation: Apply
    apiVersion: example.com/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:spec:
        f:data: {}
spec:
  data:
    key1: val1
    key2: val2

spec.dataatomic 改为 granular 之前, manager-onespec.data 字段及其所包含字段(key1key2)的属主。 当对应的 CRD 被更改,使得 spec.data 变为 granular 拓扑时, manager-one 继续拥有顶层字段 spec.data(这意味着其他管理器想删除名为 data 的映射而不引起冲突是不可能的),但不再拥有 key1key2。 因此,其他管理器可以在不引起冲突的情况下更改或删除这些字段。

在控制器中使用服务器端应用

控制器的开发人员可以把服务器端应用作为简化控制器的更新逻辑的方式。 读-改-写 和/或 patch 的主要区别如下所示:

  • 应用的对象必须包含控制器关注的所有字段。
  • 对于在控制器没有执行过应用操作之前就已经存在的字段,不能删除。 (控制器在这种用例环境下,依然可以发送一个 patchupdate
  • 对象不必事先读取,resourceVersion 不必指定。

强烈推荐:设置控制器始终在其拥有和管理的对象上强制冲突,这是因为冲突发生时,它们没有其他解决方案或措施。

转移所有权

除了通过冲突解决方案提供的并发控制, 服务器端应用提供了一些协作方式来将字段所有权从用户转移到控制器。

最好通过例子来说明这一点。 让我们来看看,在使用 Horizo​​ntalPodAutoscaler 资源和与之配套的控制器, 且开启了 Deployment 的自动水平扩展功能之后, 怎么安全的将 replicas 字段的所有权从用户转移到控制器。

假设用户定义了 Deployment,且 replicas 字段已经设置为期望的值:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2

并且,用户使用服务器端应用,像这样创建 Deployment:

kubectl apply -f https://k8s.io/examples/application/ssa/nginx-deployment.yaml --server-side

然后,为 Deployment 启用自动扩缩,例如:

kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=1 --max=10

现在,用户希望从他们的配置中删除 replicas,从而避免与 HorizontalPodAutoscaler(HPA)及其控制器发生冲突。 然而,这里存在一个竞态:在 HPA 需要调整 .spec.replicas 之前会有一个时间窗口, 如果在 HPA 写入字段并成为新的属主之前,用户删除了 .spec.replicas, 那 API 服务器就会把 .spec.replicas 的值设为 1(Deployment 的默认副本数)。 这不是用户希望发生的事情,即使是暂时的——它很可能会导致正在运行的工作负载降级。

这里有两个解决方案:

  • (基本操作)把 replicas 留在配置文件中;当 HPA 最终写入那个字段, 系统基于此事件告诉用户:冲突发生了。在这个时间点,可以安全的删除配置文件。
  • (高级操作)然而,如果用户不想等待,比如他们想为合作伙伴保持集群清晰, 那他们就可以执行以下步骤,安全的从配置文件中删除 replicas

首先,用户新定义一个只包含 replicas 字段的新清单:

# 将此文件另存为 'nginx-deployment-replicas-only.yaml'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3

用户使用私有字段管理器名称应用该清单。在本例中,用户选择了 handover-to-hpa

kubectl apply -f nginx-deployment-replicas-only.yaml \
  --server-side --field-manager=handover-to-hpa \
  --validate=false

如果应用操作和 HPA 控制器产生冲突,那什么都不做。 冲突表明控制器在更早的流程中已经对字段声明过所有权。

在此时间点,用户可以从清单中删除 replicas

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2

注意,只要 HPA 控制器为 replicas 设置了一个新值, 该临时字段管理器将不再拥有任何字段,会被自动删除。 这里无需进一步清理。

在管理器之间转移所有权

通过在配置文件中把一个字段设置为相同的值,多个字段管理器可以在彼此之间转移字段的所有权, 从而实现字段所有权的共享。 当某管理器共享了字段的所有权,管理器中任何一个成员都可以从其应用的配置中删除该字段, 从而放弃所有权,并完成了所有权向其他字段管理器的转移。

与客户端应用的对比

服务器端应用意味着既可以替代原来 kubectl apply 子命令的客户端实现, 也可供控制器作为实施变更的简单有效机制。

kubectl 管理的 last-applied 注解相比, 服务器端应用使用一种更具声明性的方法来跟踪对象的字段管理,而不是记录用户最后一次应用的状态。 这意味着,使用服务器端应用的副作用,就是字段管理器所管理的对象的每个字段的相关信息也会变得可用。

由服务器端应用实现的冲突检测和解决方案的一个结果就是, 应用者总是可以在本地状态中得到最新的字段值。 如果得不到最新值,下次执行应用操作时就会发生冲突。 解决冲突三个选项的任意一个都会保证:此应用过的配置文件是服务器上对象字段的最新子集。

这和客户端应用(Client-Side Apply)不同,如果有其他用户覆盖了此值, 过期的值被留在了应用者本地的配置文件中。 除非用户更新了特定字段,此字段才会准确, 应用者没有途径去了解下一次应用操作是否会覆盖其他用户的修改。

另一个区别是使用客户端应用的应用者不能改变他们正在使用的 API 版本,但服务器端应用支持这个场景。

客户端应用和服务器端应用的迁移

从客户端应用升级到服务器端应用

客户端应用方式时,用户使用 kubectl apply 管理资源, 可以通过使用下面标记切换为使用服务器端应用。

kubectl apply --server-side [--dry-run=server]

默认情况下,对象的字段管理从客户端应用方式迁移到 kubectl 触发的服务器端应用时,不会发生冲突。

此操作以 kubectl 作为字段管理器来应用到服务器端应用。 作为例外,可以指定一个不同的、非默认字段管理器停止的这种行为,如下面的例子所示。 对于 kubectl 触发的服务器端应用,默认的字段管理器是 kubectl

kubectl apply --server-side --field-manager=my-manager [--dry-run=server]

从服务器端应用降级到客户端应用

如果你用 kubectl apply --server-side 管理一个资源, 可以直接用 kubectl apply 命令将其降级为客户端应用。

降级之所以可行,这是因为 kubectl server-side apply 会保存最新的 last-applied-configuration 注解。

此操作以 kubectl 作为字段管理器应用到服务器端应用。 作为例外,可以指定一个不同的、非默认字段管理器停止这种行为,如下面的例子所示。 对于 kubectl 触发的服务器端应用,默认的字段管理器是 kubectl

kubectl apply --server-side --field-manager=my-manager [--dry-run=server]

API 实现

支持服务器端应用的资源的 PATCH 动词可以接受非官方的 application/apply-patch+yaml 内容类型。 服务器端应用的用户可以将部分指定的对象以 YAML 格式作为 PATCH 请求的主体发送到资源的 URI。 应用配置时,你应该始终包含对要定义的结果(如所需状态)重要的所有字段。

所有 JSON 消息都是有效的 YAML。一些客户端使用 YAML 请求体指定服务器端应用请求, 而这些 YAML 同样是合法的 JSON。

访问控制和权限

由于服务端应用是一种 PATCH 类型的操作, 所以一个主体(例如 Kubernetes RBAC 的 Role)需要 patch 权限才能编辑存量资源,还需要 create 权限才能使用服务器端应用创建新资源。

清除 managedFields

通过使用 patch(JSON Merge Patch, Strategic Merge Patch, JSON Patch)覆盖对象, 或者通过 update(HTTP PUT),可以从对象中剥离所有 managedFields; 换句话说,通过除了 apply 之外的所有写操作均可实现这点。 清除 managedFields 字段的操作可以通过用空条目覆盖 managedFields 字段的方式实现。以下是两个示例:

PATCH /api/v1/namespaces/default/configmaps/example-cm
Accept: application/json
Content-Type: application/merge-patch+json

{
  "metadata": {
    "managedFields": [
      {}
    ]
  }
}
PATCH /api/v1/namespaces/default/configmaps/example-cm
Accept: application/json
Content-Type: application/json-patch+json
If-Match: 1234567890123456789

[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]

这一操作将用只包含一个空条目的列表来覆盖 managedFields, 从而实现从对象中整体去除 managedFields。 注意,只把 managedFields 设置为空列表并不会重置该字段。 这一设计是有意为之的,目的是避免 managedFields 被与该字段无关的客户删除。

在某些场景中,执行重置操作的同时还会给出对 managedFields 之外的别的字段的变更, 对于这类操作,managedFields 首先会被重置,其他变更被压后处理。 其结果是,应用者取得了同一个请求中所有字段的所有权。

接下来

你可以阅读 Kubernetes API 参考中的 metadata 顶级字段的 managedFields

3 - 客户端库

本页面概要介绍了基于各种编程语言使用 Kubernetes API 的客户端库。

在使用 Kubernetes REST API 编写应用程序时, 你并不需要自己实现 API 调用和 “请求/响应” 类型。 你可以根据自己的编程语言需要选择使用合适的客户端库。

客户端库通常为你处理诸如身份验证之类的常见任务。 如果 API 客户端在 Kubernetes 集群中运行,大多数客户端库可以发现并使用 Kubernetes 服务账号进行身份验证, 或者能够理解 kubeconfig 文件 格式来读取凭据和 API 服务器地址。

官方支持的 Kubernetes 客户端库

以下客户端库由 Kubernetes SIG API Machinery 正式维护。

语言 客户端库 样例程序
C github.com/kubernetes-client/c 浏览
dotnet github.com/kubernetes-client/csharp 浏览
Go github.com/kubernetes/client-go/ 浏览
Haskell github.com/kubernetes-client/haskell 浏览
Java github.com/kubernetes-client/java 浏览
JavaScript github.com/kubernetes-client/javascript 浏览
Perl github.com/kubernetes-client/perl/ 浏览
Python github.com/kubernetes-client/python/ 浏览
Ruby github.com/kubernetes-client/ruby/ 浏览

社区维护的客户端库

以下 Kubernetes API 客户端库是由社区,而非 Kubernetes 团队支持、维护的。

语言 客户端库
Clojure github.com/yanatan16/clj-kubernetes-api
DotNet github.com/tonnyeremin/kubernetes_gen
DotNet (RestSharp) github.com/masroorhasan/Kubernetes.DotNet
Elixir github.com/obmarg/kazan
Elixir github.com/coryodaniel/k8s
Java (OSGi) bitbucket.org/amdatulabs/amdatu-kubernetes
Java (Fabric8, OSGi) github.com/fabric8io/kubernetes-client
Java github.com/manusa/yakc
Lisp github.com/brendandburns/cl-k8s
Lisp github.com/xh4/cube
Node.js (TypeScript) github.com/Goyoo/node-k8s-client
Node.js github.com/ajpauwels/easy-k8s
Node.js github.com/godaddy/kubernetes-client
Node.js github.com/tenxcloud/node-kubernetes-client
Perl metacpan.org/pod/Net::Kubernetes
PHP github.com/allansun/kubernetes-php-client
PHP github.com/maclof/kubernetes-client
PHP github.com/travisghansen/kubernetes-client-php
PHP github.com/renoki-co/php-k8s
Python github.com/fiaas/k8s
Python github.com/gtsystem/lightkube
Python github.com/kr8s-org/kr8s
Python github.com/mnubo/kubernetes-py
Python github.com/tomplus/kubernetes_asyncio
Python github.com/Frankkkkk/pykorm
Ruby github.com/abonas/kubeclient
Ruby github.com/k8s-ruby/k8s-ruby
Ruby github.com/kontena/k8s-client
Rust github.com/kube-rs/kube
Rust github.com/ynqa/kubernetes-rust
Scala github.com/hagay3/skuber
Scala github.com/hnaderi/scala-k8s
Scala github.com/joan38/kubernetes-client
Swift github.com/swiftkube/client

4 - Kubernetes 中的通用表达式语言

通用表达式语言 (Common Expression Language, CEL) 用于声明 Kubernetes API 的验证规则、策略规则和其他限制或条件。

CEL 表达式在 API 服务器中直接进行处理, 这使得 CEL 成为许多可扩展性用例的便捷替代方案,而无需使用类似 Webhook 这种进程外机制。 只要控制平面的 API 服务器组件保持可用状态,你的 CEL 表达式就会继续执行。

语言概述

CEL 语言的语法直观简单, 类似于 C、C++、Java、JavaScript 和 Go 中的表达式。

CEL 的设计目的是嵌入应用程序中。每个 CEL "程序" 都是一个单独的表达式,其评估结果为单个值。 CEL 表达式通常是短小的 "一行式",可以轻松嵌入到 Kubernetes API 资源的字符串字段中。

对 CEL 程序的输入是各种 “变量”。包含 CEL 的每个 Kubernetes API 字段都在 API 文档中声明了字段可使用哪些变量。例如,在 CustomResourceDefinition 的 x-kubernetes-validations[i].rules 字段中,selfoldSelf 变量可用, 并且分别指代要由 CEL 表达式验证的自定义资源数据的前一个状态和当前状态。 其他 Kubernetes API 字段可能声明不同的变量。请查阅 API 字段的 API 文档以了解该字段可使用哪些变量。

CEL 表达式示例:

CEL 表达式例子和每个表达式的用途
规则 用途
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas 验证定义副本的三个字段被正确排序
'Available' in self.stateCounts 验证映射中存在主键为 'Available' 的条目
(self.list1.size() == 0) != (self.list2.size() == 0) 验证两个列表中有一个非空,但不是两个都非空
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')) 验证 listMap 条目的 'value' 字段,其主键字段 'name' 是 'MY_ENV'
has(self.expired) && self.created + self.ttl < self.expired 验证 'expired' 日期在 'create' 日期加上 'ttl' 持续时间之后
self.health.startsWith('ok') 验证 'health' 字符串字段具有前缀 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10) 验证具有键 'x' 的 listMap 项的 'foo' 属性小于 10
type(self) == string ? self == '99%' : self == 42 验证 int-or-string 字段是否同时具备 int 和 string 的属性
self.metadata.name == 'singleton' 验证某对象的名称与特定的值匹配(使其成为一个特例)
self.set1.all(e, !(e in self.set2)) 验证两个 listSet 不相交
self.names.size() == self.details.size() && self.names.all(n, n in self.details) 验证 'details' 映射是由 'names' listSet 中的各项键入的
self.details.all(key, key.matches('^[a-zA-Z]*$')) 验证 'details' 映射的主键
self.details.all(key, self.details[key].matches('^[a-zA-Z]*$')) 验证 'details' 映射的值

CEL 选项、语言特性和库

CEL 配置了以下选项、库和语言特性,这些特性是在所列的 Kubernetes 版本中引入的:

CEL 选项、库或语言特性 包含的内容 可用性
标准宏 hasallexistsexists_onemapfilter 所有 Kubernetes 版本
标准函数 参见官方标准定义列表 所有 Kubernetes 版本
同质聚合字面量 所有 Kubernetes 版本
默认 UTC 时区 所有 Kubernetes 版本
迫切验证声明 所有 Kubernetes 版本
扩展字符串库,v1 charAtindexOflastIndexOflowerAsciiupperAsciireplacesplitjoinsubstringtrim 所有 Kubernetes 版本
Kubernetes 列表库 参见 Kubernetes 列表库 所有 Kubernetes 版本
Kubernetes 正则表达式库 参见 Kubernetes 正则表达式库 所有 Kubernetes 版本
[Kubernetes URL 库] 参见 Kubernetes URL 库 所有 Kubernetes 版本
Kubernetes 鉴权组件库 参见 Kubernetes 鉴权组件库 所有 Kubernetes 版本
Kubernetes 数量库 参见 Kubernetes 数量库 Kubernetes v1.29+
CEL 可选类型 参见 CEL 可选类型 Kubernetes v1.29+
CEL CrossTypeNumericComparisons 参见 CEL CrossTypeNumericComparisons Kubernetes v1.29+

CEL 函数、特性和语言设置支持 Kubernetes 控制平面回滚。 例如,CEL 可选值(Optional Values) 是在 Kubernetes 1.29 引入的,因此只有该版本或更新的 API 服务器才会接受使用 CEL Optional Values 的 CEL 表达式的写入请求。 但是,当集群回滚到 Kubernetes 1.28 时,已经存储在 API 资源中的使用了 "CEL Optional Values" 的 CEL 表达式将继续正确评估。

Kubernetes CEL 库

除了 CEL 社区库之外,Kubernetes 还包括在 Kubernetes 中使用 CEL 时所有可用的 CEL 库。

Kubernetes 列表库

列表库包括 indexOflastIndexOf,这两个函数的功能类似于同名的字符串函数。 这些函数返回提供的元素在列表中的第一个或最后一个位置索引。

列表库还包括 minmaxsumsum 可以用于所有数字类型以及持续时间类型。 minmax 可用于所有可比较的类型。

isSorted 也作为一个便捷的函数提供,并且支持所有可比较的类型。

例如:

使用列表库函数的 CEL 表达式例子
CEL 表达式 用途
names.isSorted() 验证名称列表是否按字母顺序排列
items.map(x, x.weight).sum() == 1.0 验证对象列表的 “weight” 总和为 1.0
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min() 验证两组优先级不重叠
names.indexOf('should-be-first') == 1 如果是特定值,则使用列表中的第一个名称

更多信息请查阅 Go 文档: Kubernetes 列表库

Kubernetes 正则表达式库

除了 CEL 标准库提供的 matches 函数外,正则表达式库还提供了 findfindAll, 使得更多种类的正则表达式运算成为可能。

例如:

使用正则表达式库函数的 CEL 表达式例子
CEL 表达式 用途
"abc 123".find('[0-9]+') 找到字符串中的第一个数字
"1, 2, 3, 4".findAll('[0-9]+').map(x, int(x)).sum() < 100 验证字符串中的数字之和小于 100

更多信息请查阅 Go 文档: Kubernetes 正则表达式库

Kubernetes URL 库

为了更轻松、更安全地处理 URL,添加了以下函数:

  • isURL(string) 按照 Go 的 net/url 检查字符串是否是一个有效的 URL。该字符串必须是一个绝对 URL。
  • url(string) URL 将字符串转换为 URL,如果字符串不是一个有效的 URL,则返回错误。

一旦通过 url 函数解析,所得到的 URL 对象就具有 getSchemegetHostgetHostnamegetPortgetEscapedPathgetQuery 访问器。

例如:

使用 URL 库函数的 CEL 表达式例子
CEL 表达式 用途
url('https://example.com:80/').getHost() 获取 URL 的 'example.com:80' 主机部分
url('https://example.com/path with spaces/').getEscapedPath() 返回 '/path%20with%20spaces/'

更多信息请查阅 Go 文档: Kubernetes URL 库

Kubernetes 鉴权组件库

在 API 中使用 CEL 表达式,可以使用类型为 Authorizer 的变量, 这个鉴权组件可用于对请求的主体(已认证用户)执行鉴权检查。

API 资源检查的过程如下:

  1. 指定要检查的组和资源:Authorizer.group(string).resource(string) ResourceCheck
  2. 可以调用以下任意组合的构建器函数(Builder Function),以进一步缩小鉴权检查范围。 注意这些函数将返回接收者的类型,并且可以串接起来:
    • ResourceCheck.subresource(string) ResourceCheck
    • ResourceCheck.namespace(string) ResourceCheck
    • ResourceCheck.name(string) ResourceCheck
  3. 调用 ResourceCheck.check(verb string) Decision 来执行鉴权检查。
  4. 调用 allowed() boolreason() string 来查验鉴权检查的结果。

对非资源访问的鉴权过程如下:

  1. 仅指定路径:Authorizer.path(string) PathCheck
  2. 调用 PathCheck.check(httpVerb string) Decision 来执行鉴权检查。
  3. 调用 allowed() boolreason() string 来查验鉴权检查的结果。

对于服务账号执行鉴权检查的方式:

  • Authorizer.serviceAccount(namespace string, name string) Authorizer
使用 URL 库函数的 CEL 表达式示例
CEL 表达式 用途
authorizer.group('').resource('pods').namespace('default').check('create').allowed() 如果主体(用户或服务账号)被允许在 default 名字空间中创建 Pod,返回 true。
authorizer.path('/healthz').check('get').allowed() 检查主体(用户或服务账号)是否有权限向 /healthz API 路径发出 HTTP GET 请求。
authorizer.serviceAccount('default', 'myserviceaccount').resource('deployments').check('delete').allowed() 检查服务账号是否有权限删除 Deployment。
特性状态: Kubernetes v1.31 [alpha]

启用 Alpha 级别的 AuthorizeWithSelectors 特性后,字段和标签选择算符可以被添加到鉴权检查中。

使用选择算符鉴权函数的 CEL 表达式示例
CEL 表达式 用途
authorizer.group('').resource('pods').fieldSelector('spec.nodeName=mynode').check('list').allowed() 如果主体(用户或服务账号)被允许使用字段选择算符 spec.nodeName=mynode 列举 Pod,返回 true。
authorizer.group('').resource('pods').labelSelector('example.com/mylabel=myvalue').check('list').allowed() 如果主体(用户或服务账号)被允许使用标签选择算符 example.com/mylabel=myvalue 列举 Pod,返回 true。

更多信息请参阅 Go 文档: Kubernetes Authz libraryKubernetes AuthzSelectors library

Kubernetes 数量库

Kubernetes 1.28 添加了对数量字符串(例如 1.5G、512k、20Mi)的操作支持。

  • isQuantity(string) 根据 Kubernetes 的 resource.Quantity, 检查字符串是否是有效的 Quantity。
  • quantity(string) Quantity 将字符串转换为 Quantity,如果字符串不是有效的数量,则会报错。

一旦通过 quantity 函数解析,得到的 Quantity 对象将具有以下成员函数库:

Quantity 的可用成员函数
成员函数 CEL 返回值 描述
isInteger() bool 仅当 asInteger 可以被安全调用且不出错时,才返回 true
asInteger() int 将当前值作为 int64 的表示返回,如果转换会导致溢出或精度丢失,则会报错
asApproximateFloat() float 返回数量的 float64 表示,可能会丢失精度。如果数量的值超出了 float64 的范围,则返回 +Inf/-Inf
sign() int 如果数量为正,则返回 1;如果数量为负,则返回 -1;如果数量为零,则返回 0
add(<Quantity>) Quantity 返回两个数量的和
add(<int>) Quantity 返回数量和整数的和
sub(<Quantity>) Quantity 返回两个数量的差
sub(<int>) Quantity 返回数量减去整数的差
isLessThan(<Quantity>) bool 如果接收值小于操作数,则返回 true
isGreaterThan(<Quantity>) bool 如果接收值大于操作数,则返回 true
compareTo(<Quantity>) int 将接收值与操作数进行比较,如果它们相等,则返回 0;如果接收值大于操作数,则返回 1;如果接收值小于操作数,则返回 -1

例如:

使用 URL 库函数的 CEL 表达式示例
CEL 表达式 用途
quantity("500000G").isInteger() 测试转换为整数是否会报错
quantity("50k").asInteger() 精确转换为整数
quantity("9999999999999999999999999999999999999G").asApproximateFloat() 松散转换为浮点数
quantity("50k").add(quantity("20k")) 两个数量相加
quantity("50k").sub(20000) 从数量中减去整数
quantity("50k").add(20).sub(quantity("100k")).sub(-50000) 链式相加和减去整数和数量
quantity("200M").compareTo(quantity("0.2G")) 比较两个数量
quantity("150Mi").isGreaterThan(quantity("100Mi")) 测试数量是否大于接收值
quantity("50M").isLessThan(quantity("100M")) 测试数量是否小于接收值

类型检查

CEL 是一种逐渐类型化的语言

一些 Kubernetes API 字段包含完全经过类型检查的 CEL 表达式。 例如,CustomResourceDefinition 验证规则就是完全经过类型检查的。

一些 Kubernetes API 字段包含部分经过类型检查的 CEL 表达式。 部分经过类型检查的表达式是指一些变量是静态类型,而另一些变量是动态类型的表达式。 例如在 ValidatingAdmissionPolicy 的 CEL 表达式中,request 变量是有类型的,但 object 变量是动态类型的。 因此,包含 request.namex 的表达式将无法通过类型检查,因为 namex 字段未定义。 然而,即使对于 object 所引用的资源种类没有定义 namex 字段, object.namex 也会通过类型检查,因为 object 是动态类型。

在 CEL 中,has() 宏可用于检查动态类型变量的字段是否可访问,然后再尝试访问该字段的值。 例如:

has(object.namex) ? object.namex == 'special' : request.name == 'special'

类型系统集成

表格显示了 OpenAPIv3 类型和 CEL 类型之间的关系
OpenAPIv3 类型 CEL 类型
设置了 properties 的 'object' object / "message type"(type(<object>) 评估为 selfType<uniqueNumber>.path.to.object.from.self
设置了 AdditionalProperties 的 'object' map
设置了 x-kubernetes-embedded-type 的 'object' object / "message type",'apiVersion'、'kind'、'metadata.name' 和 'metadata.generateName' 被隐式包含在模式中
设置了 x-kubernetes-preserve-unknown-fields 的 'object' object / "message type",CEL 表达式中不可访问的未知字段
x-kubernetes-int-or-string int 或 string 的并集,self.intOrString < 100 || self.intOrString == '50%' 对于 50"50%"都评估为 true
'array' list
设置了 x-kubernetes-list-type=map 的 'array' list,具有基于 Equality 和唯一键保证的 map
设置了 x-kubernetes-list-type=set 的 'array' list,具有基于 Equality 和唯一条目保证的 set
'boolean' boolean
'number' (所有格式) double
'integer' (所有格式) int (64)
**非等价 ** uint (64)
'null' null_type
'string' string
设置了 format=byte 的 'string'(以 base64 编码) bytes
设置了 format=date 的 'string' timestamp (google.protobuf.Timestamp)
设置了 format=datetime 的 'string' timestamp (google.protobuf.Timestamp)
设置了 format=duration 的 'string' duration (google.protobuf.Duration)

另见:CEL 类型OpenAPI 类型Kubernetes 结构化模式

x-kubernetes-list-typesetmap 的数组进行相等比较时会忽略元素顺序。 例如,如果这些数组代表 Kubernetes 的 set 值,则 [1, 2] == [2, 1]

使用 x-kubernetes-list-type 的数组进行串接时,使用 list 类型的语义:

set
X + Y 执行并集操作,保留 X 中所有元素的数组位置, 将 Y 中非交集元素追加到 X 中,保留它们的部分顺序。
map
X + Y 执行合并操作,保留 X 中所有键的数组位置, 但是当 XY 的键集相交时,将 Y 中的值覆盖 X 中的值。 将 Y 中非交集键的元素附加到 X 中,保留它们的部分顺序。

转义

仅形如 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的 Kubernetes 资源属性名可以从 CEL 中访问。 当在表达式中访问可访问的属性名时,会根据以下规则进行转义:

CEL 标识符转义规则表
转义序列 等价的属性名
__underscores__ __
__dot__ .
__dash__ -
__slash__ /
__{keyword}__ CEL 保留的 关键字

当你需要转义 CEL 的任一 保留的 关键字时,你需要使用下划线转义来完全匹配属性名 (例如,sprint 这个单词中的 int 不会被转义,也不需要被转义)。

转义示例:

转义的 CEL 标识符例子
属性名称 带有转义的属性名称的规则
namespace self.__namespace__ > 0
x-prop self.x__dash__prop > 0
redact__d self.redact__underscores__d > 0
string self.startsWith('kube')

资源约束

CEL 不是图灵完备的,提供了多种生产安全控制手段来限制执行时间。 CEL 的资源约束特性提供了关于表达式复杂性的反馈,并帮助保护 API 服务器免受过度的资源消耗。 CEL 的资源约束特性用于防止 CEL 评估消耗过多的 API 服务器资源。

资源约束特性的一个关键要素是 CEL 定义的成本单位,它是一种跟踪 CPU 利用率的方式。 成本单位独立于系统负载和硬件。成本单位也是确定性的;对于任何给定的 CEL 表达式和输入数据, 由 CEL 解释器评估表达式将始终产生相同的成本。

CEL 的许多核心运算具有固定成本。例如比较(例如 <)这类最简单的运算成本为 1。 有些运算具有更高的固定成本,例如列表字面声明具有 40 个成本单位的固定基础成本。

调用本地代码实现的函数时,基于运算的时间复杂度估算其成本。 举例而言:matchfind 这类使用正则表达式的运算使用 length(regexString)*length(inputString) 的近似成本进行估算。 这个近似的成本反映了 Go 的 RE2 实现的最坏情况的时间复杂度。

运行时成本预算

所有由 Kubernetes 评估的 CEL 表达式都受到运行时成本预算的限制。 运行时成本预算是通过在解释 CEL 表达式时增加成本单元计数器来计算实际 CPU 利用率的估算值。 如果 CEL 解释器执行的指令太多,将超出运行时成本预算,表达式的执行将停止,并将出现错误。

一些 Kubernetes 资源定义了额外的运行时成本预算,用于限制多个表达式的执行。 如果所有表达式的成本总和超过预算,表达式的执行将停止,并将出现错误。 例如,自定义资源的验证具有针对验证自定义资源所评估的所有 验证规则每个验证 运行时成本预算。

估算的成本限制

对于某些 Kubernetes 资源,API 服务器还可能检查 CEL 表达式的最坏情况估计运行时间是否过于昂贵而无法执行。 如果是,则 API 服务器会拒绝包含 CEL 表达式的创建或更新操作,以防止 CEL 表达式被写入 API 资源。 此特性提供了更强的保证,即写入 API 资源的 CEL 表达式将在运行时进行评估,而不会超过运行时成本预算。

5 - Kubernetes 弃用策略

本文档详细解释系统中各个层面的弃用策略(Deprecation Policy)。

Kubernetes 是一个组件众多、贡献者人数众多的大系统。 就像很多类似的软件,所提供的功能特性集合会随着时间推移而自然发生变化, 而且有时候某个功能特性可能需要被移除。被移除的可能是一个 API、 一个参数标志或者甚至某整个功能特性。为了避免影响到现有用户, Kubernetes 对于其中渐次移除的各个方面规定了一种弃用策略并遵从此策略。

弃用 API 的一部分

由于 Kubernetes 是一个 API 驱动的系统,API 会随着时间推移而演化, 以反映人们对问题空间的认识的变化。Kubernetes API 实际上是一个 API 集合, 其中每个成员称作“API 组(API Group)”,并且每个 API 组都是独立管理版本的。 API 版本会有三类, 每类有不同的废弃策略:

示例 分类
v1 正式发布(Generally available,GA,稳定版本)
v1beta1 Beta (预发布)
v1alpha1 Alpha (试验性)

给定的 Kubernetes 发布版本中可以支持任意数量的 API 组,且每组可以包含任意个数的版本。

下面的规则负责指导 API 元素的弃用,具体元素包括:

  • REST 资源(也即 API 对象)
  • REST 资源的字段
  • REST 资源的注解,包含“beta”类注解,但不包含“alpha”类注解
  • 枚举值或者常数值
  • 组件配置结构

以下是跨正式发布版本时要实施的规则,不适用于对 master 或发布分支上不同提交之间的变化。

规则 #1:只能在增加 API 组版本号时删除 API 元素。

一旦在某个特定 API 组版本中添加了 API 元素,则该元素不可从该版本中删除, 且其行为也不能大幅度地变化,无论属于哪一类(GA、Alpha 或 Beta)。

规则 #2:在给定的发布版本中,API 对象必须能够在不同的 API 版本之间来回转换且不造成信息丢失,除非整个 REST 资源在某些版本中完全不存在。

例如,一个对象可被用 v1 来写入之后用 v2 来读出并转换为 v1,所得到的 v1 必须与原来的 v1 对象完全相同。v2 中的表现形式可能与 v1 不同,但系统知道如何在两个版本之间执行双向转换。 此外,v2 中添加的所有新字段都必须能够转换为 v1 再转换回来。这意味着 v1 必须添加一个新的等效字段或者将其表现为一个注解。

规则 #3:给定类别的 API 版本不可被弃用以支持稳定性更差的 API 版本。

  • 一个正式发布的(GA)API 版本可替换 Beta 或 Alpha API 版本。
  • Beta API 版本可以替换早期的 Beta 和 Alpha API 版本,但 不可以 替换正式的 API 版本。
  • Alpha API 版本可以替换早期的 Alpha API 版本,但 不可以 替换 Beta 或正式的 API 版本。

规则 #4a:API 生命周期由 API 稳定性级别决定

  • GA API 版本可以被标记为已弃用,但不得在 Kubernetes 的主要版本中删除
  • Beta API 版本在引入后不超过 9 个月或 3 个次要版本(以较长者为准)将被弃用, 并且在弃用后 9 个月或 3 个次要版本(以较长者为准)不再提供服务
  • Alpha API 版本可能会在任何版本中被删除,不另行通知

这确保了 Beta API 支持涵盖了最多 2 个版本的支持版本偏差, 并且这些 API 不会在不稳定的 Beta 版本上停滞不前,积累的生产使用数据将在对 Beta API 的支持结束时中断。

规则 #4b:标记为“preferred(优选的)” API 版本和给定 API 组的 “storage version(存储版本)”在既支持老版本也支持新版本的 Kubernetes 发布版本出来以前不可以提升其版本号。

用户必须能够升级到 Kubernetes 新的发行版本,之后再回滚到前一个发行版本, 且整个过程中无需针对新的 API 版本做任何转换,也不允许出现功能损坏的情况, 除非用户显式地使用了仅在较新版本中才存在的功能特性。 就对象的存储表示而言,这一点尤其是不言自明的。

以上所有规则最好通过例子来说明。假定现有 Kubernetes 发行版本为 X,其中引入了新的 API 组。 大约每隔 4 个月会有一个新的 Kubernetes 版本被发布(每年 3 个版本)。 下面的表格描述了在一系列后续的发布版本中哪些 API 版本是受支持的。

发布版本 API 版本 优选/存储版本 注释
X v1alpha1 v1alpha1
X+1 v1alpha2 v1alpha2
  • v1alpha1 被移除。查阅发布说明了解要采取的行动。
X+2 v1beta1 v1beta1
  • v1alpha2 被移除。查阅发布说明了解要采取的行动。
X+3 v1beta2、v1beta1(已弃用) v1beta1
  • v1beta1 被弃用。查阅发布说明了解要采取的行动。
X+4 v1beta2、v1beta1(已弃用) v1beta2
X+5 v1、v1beta1(已弃用)、v1beta2(已弃用) v1beta2
  • v1beta2 被弃用。查阅发布说明了解要采取的行动。
X+6 v1、v1beta2(已弃用) v1
  • v1beta1 被移除。查阅发布说明了解要采取的行动。
X+7 v1、v1beta2(已弃用) v1
X+8 v2alpha1、v1 v1
  • v1beta2 被移除。查阅发布说明了解要采取的行动。
X+9 v2alpha2、v1 v1
  • v2alpha1 被移除。查阅发布说明了解要采取的行动。
X+10 v2beta1、v1 v1
  • v2alpha2 被移除。查阅发布说明了解要采取的行动。
X+11 v2beta2、v2beta1(已弃用)、v1 v1
  • v2beta1 被弃用。查阅发布说明了解要采取的行动。
X+12 v2、v2beta2(已弃用)、v2beta1(已弃用)、v1(已弃用) v1
  • v2beta2 已被弃用。查阅发布说明了解要采取的行动。
  • v1 已被弃用,取而代之的是 v2,但不会被移除
X+13 v2、v2beta1(已弃用)、v2beta2(已弃用)、v1(已弃用) v2
X+14 v2、v2beta2(已弃用)、v1(已弃用) v2
  • v2beta1 被移除。查阅发布说明了解要采取的行动。
X+15 v2、v1(已弃用) v2
  • v2beta2 被移除。查阅发布说明了解要采取的行动。

REST 资源(也即 API 对象)

考虑一个假想的名为 Widget 的 REST 资源,在上述时间线中位于 API v1,而现在打算将其弃用。 我们会在文档和公告中与 X+1 版本的发布同步记述此弃用决定。 Widget 资源仍会在 API 版本 v1(已弃用)中存在,但不会出现在 v2alpha1 中。 Widget 资源会 X+8 发布版本之前(含 X+8)一直存在并可用。 只有在发布版本 X+9 中,API v1 寿终正寝时,Widget 才彻底消失,相应的资源行为也被移除。

从 Kubernetes v1.19 开始,当 API 请求被发送到一个已弃用的 REST API 末端时:

  1. API 响应中会包含一个 Warning 头部字段(如 RFC7234 5.5 节所定义);

  2. 该请求会导致对应的审计事件中会增加一个注解 "k8s.io/deprecated":"true"

  3. kube-apiserver 进程的 apiserver_requested_deprecated_apis 度量值会被设置为 1。 该度量值还附带 groupversionresourcesubresource 标签 (可供添加到度量值 apiserver_request_total 上), 和一个 removed_release 标签,标明该 API 将消失的 Kubernetes 发布版本。 下面的 Prometheus 查询会返回对 v1.22 中将移除的、已弃用的 API 的请求的信息:

    apiserver_requested_deprecated_apis{removed_release="1.22"} * on(group,version,resource,subresource) group_right() apiserver_request_total
    

REST 资源的字段

就像整个 REST 资源一样,在 API v1 中曾经存在的各个字段在 API v1 被移除之前必须一直存在且起作用。 与整个资源上的规定不同,v2 API 可以选择为字段提供不同的表示方式, 只要对应的资源对象可在不同版本之间来回转换即可。 例如,v1 版本中一个名为 "magnitude" 的已弃用字段可能在 API v2 中被命名为 "deprecatedMagnitude"。 当 v1 最终被移除时,废弃的字段也可以从 v2 中移除。

枚举值或常数值

就像前文讲述的 REST 资源及其中的单个字段一样,API v1 中所支持的常数值必须在 API v1 被移除之前一直存在且起作用。

组件配置结构

组件的配置也是有版本的,并且按 REST 资源的方式来管理。

将来的工作

随着时间推移,Kubernetes 会引入粒度更细的 API 版本。 到那时,这里的规则会根据需要进行调整。

弃用一个标志或 CLI 命令

Kubernetes 系统中包含若干不同的、相互协作的程序。 有时,Kubernetes 可能会删除这些程序的某些标志或 CLI 命令(统称“命令行元素”)。 这些程序可以天然地划分到两个大组中:面向用户的和面向管理员的程序。 二者之间的弃用策略略有不同。 除非某个标志显示地通过前缀或文档来标明其为“alpha”或“beta”, 该标志要被视作正式发布的(GA)。

命令行元素相当于系统的 API 的一部分,不过因为它们并没有采用 REST API 一样的方式来管理版本,其弃用规则规定如下:

规则 #5a:面向用户的命令行元素(例如,kubectl)必须在其宣布被弃用其在以下时长内仍能使用:

  • GA:12 个月或者 2 个发布版本(取其较长者)
  • Beta:3 个月或者 1 个发布版本(取其较长者)
  • Alpha:0 发布版本

规则 #5b:面向管理员的命令行元素(例如,kubelet)必须在其被宣布弃用之后以下时长内保持可用:

  • GA:6 个月或 1 个发行版本(取其较长者)
  • Beta: 3 个月或 1 个发行版本(取其较长者)
  • Alpha: 0 个发布版本

规则 #5c:不可以为了支持稳定性更差的 CLI 元素而弃用现有命令行(CLI)元素

类似于 API 的规则 #3,如果命令行的某个元素被替换为另一种实现方式, 例如通过重命名现有元素或者通过使用来自文件的配置替代命令行参数, 那么推荐的替代方式的稳定性必须相同或更高。

规则 #6:被弃用的 CLI 元素在被用到时必须能够产生警告,而警告的产生是可以被禁止的。

弃用某功能特性或行为

在一些较偶然的情形下,某 Kubernetes 发行版本需要弃用系统的某项功能特性或者行为, 而对应的功能特性或行为并不受 API 或 CLI 控制。在这种情况下,其弃用规则如下:

规则 #7:被弃用的行为必须在被宣布弃用之后至少 1 年时间内必须保持能用。

如果特性或行为正在替换为需要处理才能适应变更的替代实现,你应尽可能简化过渡。 如果替代实现在 Kubernetes 组织的控制下,则适用以下规则:

规则 #8:不得因为偏好稳定性更差的替代实现而弃用现有特性或行为。

例如,不可以因为偏好某 Beta 阶段的替代方式而弃用对应的已正式发布(GA)的特性。 然而,Kubernetes 项目鼓励用户在替代实现达到相同成熟水平之前就采用并过渡到替代实现。 这对于探索某特性的全新用例或对替代实现提供早期反馈尤为重要。

替代实现有时可能是外部工具或产品,例如某特性可能从 kubelet 迁移到不受 Kubernetes 项目控制的容器运行时。 在这种情况下,此规则不再适用,但你必须努力确保存在一种过渡途径能够不影响组件的成熟水平。 以容器运行时为例,这个努力可能包括尝试确保流行的容器运行时在实现对应的替代行为时,能够提供相同稳定性水平的版本。

特性和行为的弃用规则并不意味着对系统的所有更改都受此策略约束。 这些规则仅适用于重大的、用户可见的行为;这些行为会影响到在 Kubernetes 中运行的应用的正确性,或者影响到 Kubernetes 集群的管理。 这些规则也适用于那些被整个移除的功能特性或行为。

上述规则的一个例外是 特性门控(Feature Gate)。特性门控是一些键值偶对, 允许用户启用或禁用一些试验性的功能特性。

特性门控意在覆盖功能特性的整个开发周期,它们无意成为长期的 API。 因此,它们会在某功能特性正式发布或被抛弃之后被弃用和删除。

随着一个功能特性经过不同的成熟阶段,相关的特性门控也会演化。 与功能特性生命周期对应的特性门控状态为:

  • Alpha:特性门控默认被禁用,只能由用户显式启用。
  • Beta:特性门控默认被弃用,可被用户显式禁用。
  • GA: 特性门控被弃用(参见弃用),并且不再起作用。
  • GA,弃用窗口期结束:特性门控被移除,不再接受调用。

弃用

功能特性在正式发布之前的生命周期内任何时间点都可被移除。 当未正式发布的功能特性被移除时,它们对应的特性门控也被弃用。

当尝试禁用一个不再起作用的特性门控时,该调用会失败,这样可以避免毫无迹象地执行一些不受支持的场景。

在某些场合,移除一个即将正式发布的功能特性需要很长时间。 特性门控可以保持其功能,直到对应的功能特性被彻底移除,直到那时特性门控自身才可被弃用。

由于移除一个已经正式发布的功能特性对应的特性门控也需要一定时间,对特性门控的调用可能一直被允许, 前提是特性门控对功能本身无影响且特性门控不会引发任何错误。

意在允许用户禁用的功能特性应该包含一个在相关联的特性门控中禁用该功能的机制。

特性门控的版本管理与之前讨论的组件版本管理不同,因此其对应的弃用策略如下:

规则 #9:特性门控所对应的功能特性经历下面所列的成熟性阶段转换时,特性门控必须被弃用。 特性门控弃用时必须在以下时长内保持其功能可用:

  • Beta 特性转为 GA:6 个月或者 2 个发布版本(取其较长者)
  • Beta 特性转为丢弃:3 个月或者 1 个发布版本(取其较长者)
  • Alpha 特性转为丢弃:0 个发布版本

规则 #10:已弃用的特色门控再被使用时必须给出警告回应。当特性门控被弃用时, 必须在发布说明和对应的 CLI 帮助信息中通过文档宣布。 警告信息和文档都要标明是否某特性门控不再起作用。

弃用度量值

Kubernetes 控制平面的每个组件都公开度量值(通常是 /metrics 端点),它们通常由集群管理员使用。 并不是所有的度量值都是同样重要的:一些度量值通常用作 SLIs 或被使用来确定 SLOs,这些往往比较重要。 其他度量值在本质上带有实验性,或者主要用于 Kubernetes 开发过程。

因此,度量值分为三个稳定性类别(ALPHABETASTABLE); 此分类会影响在 Kubernetes 发布版本中移除某度量值。 所对应的分类取决于对该度量值重要性的预期。 弃用和移除度量值的规则如下:

规则 #11a: 对于相应的稳定性类别,度量值起作用的周期必须不小于:

  • STABLE: 4 个发布版本或者 12 个月 (取其较长者)
  • BETA: 2 个发布版本或者 8 个月 (取其较长者)
  • ALPHA: 0 个发布版本

规则 #11b: 在度量值被宣布启用之后,它起作用的周期必须不小于:

  • STABLE: 3 个发布版本或者 9 个月 (取其较长者)
  • BETA: 1 个发布版本或者 4 个月 (取其较长者)
  • ALPHA: 0 个发布版本

已弃用的度量值将在其描述文本前加上一个已弃用通知字符串 '(Deprecated from x.y)', 并将在度量值被记录期间发出警告日志。就像稳定的、未被弃用的度量指标一样, 被弃用的度量值将自动注册到 metrics 端点,因此被弃用的度量值也是可见的。

在随后的版本中(当度量值 deprecatedVersion 等于 当前 Kubernetes 版本 - 3), 被弃用的度量值将变成 隐藏(Hidden) metric 度量值。 与被弃用的度量值不同,隐藏的度量值将不再被自动注册到 metrics 端点(因此被隐藏)。 但是,它们可以通过可执行文件的命令行标志显式启用(--show-hidden-metrics-for-version=)。 如果集群管理员不能对早期的弃用警告作出反应,这一设计就为他们提供了抓紧迁移弃用度量值的途径。 隐藏的度量值应该在再过一个发行版本后被删除。

例外

没有策略可以覆盖所有情况。此策略文档是一个随时被更新的文档,会随着时间推移演化。 在实践中,会有一些情况无法很好地匹配到这里的弃用策略, 或者这里的策略变成了很严重的羁绊。这类情形要与 SIG 和项目领导讨论, 寻求对应场景的最佳解决方案。请一直铭记,Kubernetes 承诺要成为一个稳定的系统, 至少会尽力做到不会影响到其用户。此弃用策略的任何例外情况都会在所有相关的发布说明中公布。

6 - 已弃用 API 的迁移指南

随着 Kubernetes API 的演化,API 会周期性地被重组或升级。 当 API 演化时,老的 API 会被弃用并被最终删除。 本页面包含你在将已弃用 API 版本迁移到新的更稳定的 API 版本时需要了解的知识。

各发行版本中移除的 API

v1.32

v1.32 发行版本将停止提供以下已弃用的 API 版本:

流控制资源

FlowSchema 和 PriorityLevelConfiguration 的 flowcontrol.apiserver.k8s.io/v1beta3 API 版本将不再在 v1.32 中提供。

  • 迁移清单和 API 客户端以使用 flowcontrol.apiserver.k8s.io/v1 API 版本(自 v1.29 起可用)。
  • 所有现有的持久对象都可以通过新的 API 访问。
  • flowcontrol.apiserver.k8s.io/v1 中的显着变化:
    • PriorityLevelConfiguration 的 spec.limited.nominalConcurrencyShares 字段仅在未指定时默认为 30,并且显式值 0 时不会更改为 30。

v1.29

v1.29 发行版本停止支持以下已弃用的 API 版本:

流控制资源

从 v1.29 版本开始不再提供 flowcontrol.apiserver.k8s.io/v1beta2 API 版本的 FlowSchema 和 PriorityLevelConfiguration。

  • 迁移清单和 API 客户端使用 flowcontrol.apiserver.k8s.io/v1 API 版本(自 v1.29 版本开始可用), 或 flowcontrol.apiserver.k8s.io/v1beta3 API 版本(自 v1.26 起可用);
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • flowcontrol.apiserver.k8s.io/v1 中的显着变化:
    • PriorityLevelConfiguration 的 spec.limited.assuredConcurrencyShares 字段已被重命名为 spec.limited.nominalConcurrencyShares,仅在未指定时默认为 30, 并且显式值 0 不会更改为 30。
  • flowcontrol.apiserver.k8s.io/v1beta3 中需要额外注意的变更:
    • PriorityLevelConfiguration 的 spec.limited.assuredConcurrencyShares 字段已被更名为 spec.limited.nominalConcurrencyShares

v1.27

v1.27 发行版本停止支持以下已弃用的 API 版本:

CSIStorageCapacity

从 v1.27 版本开始不再提供 storage.k8s.io/v1beta1 API 版本的 CSIStorageCapacity。

  • 自 v1.24 版本起,迁移清单和 API 客户端使用 storage.k8s.io/v1 API 版本
  • 所有现有的持久化对象都可以通过新的 API 访问
  • 没有需要额外注意的变更

v1.26

v1.26 发行版本中将去除以下已弃用的 API 版本:

流控制资源

从 v1.26 版本开始不再提供 flowcontrol.apiserver.k8s.io/v1beta1 API 版本的 FlowSchema 和 PriorityLevelConfiguration。

  • 迁移清单和 API 客户端使用 flowcontrol.apiserver.k8s.io/v1beta2 API 版本;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

HorizontalPodAutoscaler

从 v1.26 版本开始不再提供 autoscaling/v2beta2 API 版本的 HorizontalPodAutoscaler。

  • 迁移清单和 API 客户端使用 autoscaling/v2 API 版本, 此 API 从 v1.23 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问。
  • 值得注意的变更:

v1.25

v1.25 发行版本将停止提供以下已废弃 API 版本:

CronJob

从 v1.25 版本开始不再提供 batch/v1beta1 API 版本的 CronJob。

  • 迁移清单和 API 客户端使用 batch/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

EndpointSlice

从 v1.25 版本开始不再提供 discovery.k8s.io/v1beta1 API 版本的 EndpointSlice。

  • 迁移清单和 API 客户端使用 discovery.k8s.io/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • discovery.k8s.io/v1 中值得注意的变更有:
    • 使用每个 Endpoint 的 nodeName 字段而不是已被弃用的 topology["kubernetes.io/hostname"] 字段;
    • 使用每个 Endpoint 的 zone 字段而不是已被弃用的 topology["kubernetes.io/zone"] 字段;
    • topology 字段被替换为 deprecatedTopology,并且在 v1 版本中不可写入。

Event

从 v1.25 版本开始不再提供 events.k8s.io/v1beta1 API 版本的 Event。

  • 迁移清单和 API 客户端使用 events.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • events.k8s.io/v1 中值得注意的变更有:
    • type 字段只能设置为 NormalWarning 之一;
    • involvedObject 字段被更名为 regarding
    • actionreasonreportingControllerreportingInstance 字段 在创建新的 events.k8s.io/v1 版本 Event 时都是必需的字段;
    • 使用 eventTime 而不是已被弃用的 firstTimestamp 字段 (该字段已被更名为 deprecatedFirstTimestamp,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 series.lastObservedTime 而不是已被弃用的 lastTimestamp 字段 (该字段已被更名为 deprecatedLastTimestamp,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 series.count 而不是已被弃用的 count 字段 (该字段已被更名为 deprecatedCount,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 reportingController 而不是已被弃用的 source.component 字段 (该字段已被更名为 deprecatedSource.component,且不允许出现在新的 events.k8s.io/v1 Event 对象中);
    • 使用 reportingInstance 而不是已被弃用的 source.host 字段 (该字段已被更名为 deprecatedSource.host,且不允许出现在新的 events.k8s.io/v1 Event 对象中)。

HorizontalPodAutoscaler

从 v1.25 版本开始不再提供 autoscaling/v2beta1 API 版本的 HorizontalPodAutoscaler。

  • 迁移清单和 API 客户端使用 autoscaling/v2 API 版本,此 API 从 v1.23 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问。
  • 值得注意的变更:

PodDisruptionBudget

从 v1.25 版本开始不再提供 policy/v1beta1 API 版本的 PodDisruptionBudget。

  • 迁移清单和 API 客户端使用 policy/v1 API 版本,此 API 从 v1.21 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • policy/v1 中需要额外注意的变更有:
    • policy/v1 版本的 PodDisruptionBudget 中将 spec.selector 设置为空({})时会选择名字空间中的所有 Pod(在 policy/v1beta1 版本中,空的 spec.selector 不会选择任何 Pod)。如果 spec.selector 未设置,则在两个 API 版本下都不会选择任何 Pod。

PodSecurityPolicy

从 v1.25 版本开始不再提供 policy/v1beta1 API 版本中的 PodSecurityPolicy, 并且 PodSecurityPolicy 准入控制器也会被删除。

迁移到 Pod 安全准入第三方准入 Webhook。 有关迁移指南,请参阅从 PodSecurityPolicy 迁移到内置 PodSecurity 准入控制器。 有关弃用的更多信息,请参阅 PodSecurityPolicy 弃用:过去、现在和未来

RuntimeClass

从 v1.25 版本开始不再提供 node.k8s.io/v1beta1 API 版本中的 RuntimeClass。

  • 迁移清单和 API 客户端使用 node.k8s.io/v1 API 版本,此 API 从 v1.20 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

v1.22

v1.22 发行版本停止提供以下已废弃 API 版本:

Webhook 资源

admissionregistration.k8s.io/v1beta1 API 版本的 MutatingWebhookConfiguration 和 ValidatingWebhookConfiguration 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 admissionregistration.k8s.io/v1 API 版本, 此 API 从 v1.16 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • webhooks[*].failurePolicy 在 v1 版本中默认值从 Ignore 改为 Fail
    • webhooks[*].matchPolicy 在 v1 版本中默认值从 Exact 改为 Equivalent
    • webhooks[*].timeoutSeconds 在 v1 版本中默认值从 30s 改为 10s
    • webhooks[*].sideEffects 的默认值被删除,并且该字段变为必须指定; 在 v1 版本中可选的值只能是 NoneNoneOnDryRun 之一
    • webhooks[*].admissionReviewVersions 的默认值被删除,在 v1 版本中此字段变为必须指定(AdmissionReview 的被支持版本包括 v1v1beta1
    • webhooks[*].name 必须在通过 admissionregistration.k8s.io/v1 创建的对象列表中唯一

CustomResourceDefinition

apiextensions.k8s.io/v1beta1 API 版本的 CustomResourceDefinition 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 apiextensions.k8s.io/v1 API 版本,此 API 从 v1.16 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:

    • spec.scope 的默认值不再是 Namespaced,该字段必须显式指定
    • spec.version 在 v1 版本中被删除;应改用 spec.versions
    • spec.validation 在 v1 版本中被删除;应改用 spec.versions[*].schema
    • spec.subresources 在 v1 版本中被删除;应改用 spec.versions[*].subresources
    • spec.additionalPrinterColumns 在 v1 版本中被删除;应改用 spec.versions[*].additionalPrinterColumns
    • spec.conversion.webhookClientConfig 在 v1 版本中被移动到 spec.conversion.webhook.clientConfig
    • spec.conversion.conversionReviewVersions 在 v1 版本中被移动到 spec.conversion.webhook.conversionReviewVersions
    • spec.versions[*].schema.openAPIV3Schema 在创建 v1 版本的 CustomResourceDefinition 对象时变成必需字段,并且其取值必须是一个 结构化的 Schema
    • spec.preserveUnknownFields: true 在创建 v1 版本的 CustomResourceDefinition 对象时不允许指定;该配置必须在 Schema 定义中使用 x-kubernetes-preserve-unknown-fields: true 来设置
    • 在 v1 版本中,additionalPrinterColumns 的条目中的 JSONPath 字段被更名为 jsonPath(补丁 #66531

APIService

apiregistration/v1beta1 API 版本的 APIService 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 apiregistration.k8s.io/v1 API 版本,此 API 从 v1.10 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

TokenReview

authentication.k8s.io/v1beta1 API 版本的 TokenReview 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 authentication.k8s.io/v1 API 版本,此 API 从 v1.6 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

SubjectAccessReview resources

authorization.k8s.io/v1beta1 API 版本的 LocalSubjectAccessReview、 SelfSubjectAccessReview、SubjectAccessReview、SelfSubjectRulesReview 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 authorization.k8s.io/v1 API 版本,此 API 从 v1.6 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 需要额外注意的变更:
    • spec.group 在 v1 版本中被更名为 spec.groups (补丁 #32709

CertificateSigningRequest

certificates.k8s.io/v1beta1 API 版本的 CertificateSigningRequest 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 certificates.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • certificates.k8s.io/v1 中需要额外注意的变更:
    • 对于请求证书的 API 客户端而言:
      • spec.signerName 现在变成必需字段(参阅 已知的 Kubernetes 签署者), 并且通过 certificates.k8s.io/v1 API 不可以创建签署者为 kubernetes.io/legacy-unknown 的请求
      • spec.usages 现在变成必需字段,其中不可以包含重复的字符串值, 并且只能包含已知的用法字符串
    • 对于要批准或者签署证书的 API 客户端而言:
      • status.conditions 中不可以包含重复的类型
      • status.conditions[*].status 字段现在变为必需字段
      • status.certificate 必须是 PEM 编码的,而且其中只能包含 CERTIFICATE 数据块

Lease

coordination.k8s.io/v1beta1 API 版本的 Lease 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 coordination.k8s.io/v1 API 版本,此 API 从 v1.14 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

Ingress

extensions/v1beta1networking.k8s.io/v1beta1 API 版本的 Ingress 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.backend 字段被更名为 spec.defaultBackend
    • 后端的 serviceName 字段被更名为 service.name
    • 数值表示的后端 servicePort 字段被更名为 service.port.number
    • 字符串表示的后端 servicePort 字段被更名为 service.port.name
    • 对所有要指定的路径,pathType 都成为必需字段。 可选项为 PrefixExactImplementationSpecific。 要匹配 v1beta1 版本中未定义路径类型时的行为,可使用 ImplementationSpecific

IngressClass

networking.k8s.io/v1beta1 API 版本的 IngressClass 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.19 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

RBAC 资源

rbac.authorization.k8s.io/v1beta1 API 版本的 ClusterRole、ClusterRoleBinding、 Role 和 RoleBinding 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 rbac.authorization.k8s.io/v1 API 版本,此 API 从 v1.8 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

PriorityClass

scheduling.k8s.io/v1beta1 API 版本的 PriorityClass 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 scheduling.k8s.io/v1 API 版本,此 API 从 v1.14 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

存储资源

storage.k8s.io/v1beta1 API 版本的 CSIDriver、CSINode、StorageClass 和 VolumeAttachment 不在 v1.22 版本中继续提供。

  • 迁移清单和 API 客户端使用 storage.k8s.io/v1 API 版本
    • CSIDriver 从 v1.19 版本开始在 storage.k8s.io/v1 中提供;
    • CSINode 从 v1.17 版本开始在 storage.k8s.io/v1 中提供;
    • StorageClass 从 v1.6 版本开始在 storage.k8s.io/v1 中提供;
    • VolumeAttachment 从 v1.13 版本开始在 storage.k8s.io/v1 中提供;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 没有需要额外注意的变更。

v1.16

v1.16 发行版本停止提供以下已废弃 API 版本:

NetworkPolicy

extensions/v1beta1 API 版本的 NetworkPolicy 不在 v1.16 版本中继续提供。

  • 迁移清单和 API 客户端使用 networking.k8s.io/v1 API 版本,此 API 从 v1.8 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问。

DaemonSet

extensions/v1beta1apps/v1beta2 API 版本的 DaemonSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.templateGeneration 字段被删除
    • spec.selector 现在变成必需字段,并且在对象创建之后不可变更; 可以将现有模板的标签作为选择算符以实现无缝迁移。
    • spec.updateStrategy.type 的默认值变为 RollingUpdateextensions/v1beta1 API 版本中的默认值是 OnDelete)。

Deployment

extensions/v1beta1apps/v1beta1apps/v1beta2 API 版本的 Deployment 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.rollbackTo 字段被删除
    • spec.selector 字段现在变为必需字段,并且在 Deployment 创建之后不可变更; 可以使用现有的模板的标签作为选择算符以实现无缝迁移。
    • spec.progressDeadlineSeconds 的默认值变为 600 秒 (extensions/v1beta1 中的默认值是没有期限)
    • spec.revisionHistoryLimit 的默认值变为 10apps/v1beta1 API 版本中此字段默认值为 2,在extensions/v1beta1 API 版本中的默认行为是保留所有历史记录)。
    • maxSurgemaxUnavailable 的默认值变为 25% (在 extensions/v1beta1 API 版本中,这些字段的默认值是 1)。

StatefulSet

apps/v1beta1apps/v1beta2 API 版本的 StatefulSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.selector 字段现在变为必需字段,并且在 StatefulSet 创建之后不可变更; 可以使用现有的模板的标签作为选择算符以实现无缝迁移。
    • spec.updateStrategy.type 的默认值变为 RollingUpdateapps/v1beta1 API 版本中的默认值是 OnDelete)。

ReplicaSet

extensions/v1beta1apps/v1beta1apps/v1beta2 API 版本的 ReplicaSet 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 apps/v1 API 版本,此 API 从 v1.9 版本开始可用;
  • 所有的已保存的对象都可以通过新的 API 来访问;
  • 值得注意的变更:
    • spec.selector 现在变成必需字段,并且在对象创建之后不可变更; 可以将现有模板的标签作为选择算符以实现无缝迁移。

PodSecurityPolicy

extensions/v1beta1 API 版本的 PodSecurityPolicy 在 v1.16 版本中不再继续提供。

  • 迁移清单和 API 客户端使用 policy/v1beta1 API 版本,此 API 从 v1.10 版本开始可用;
  • 注意 policy/v1beta1 API 版本的 PodSecurityPolicy 会在 v1.25 版本中移除。

需要做什么

在禁用已启用 API 的情况下执行测试

你可以通过在启动 API 服务器时禁用特定的 API 版本来模拟即将发生的 API 移除,从而完成测试。在 API 服务器启动参数中添加如下标志:

--runtime-config=<group>/<version>=false

例如:

--runtime-config=admissionregistration.k8s.io/v1beta1=false,apiextensions.k8s.io/v1beta1,...

定位何处使用了已弃用的 API

使用 1.19 及更高版本中可用的客户端警告、指标和审计信息 来定位在何处使用了已弃用的 API。

迁移到未被弃用的 API

  • 更新自定义的集成组件和控制器,调用未被弃用的 API

  • 更改 YAML 文件引用未被弃用的 API

    你可以用 kubectl-convert 命令自动转换现有对象:

    kubectl convert -f <file> --output-version <group>/<version>
    

    例如,要将较老的 Deployment 版本转换为 apps/v1 版本,你可以运行:

    kubectl convert -f ./my-deployment.yaml --output-version apps/v1
    

    这个转换可能使用了非理想的默认值。要了解更多关于特定资源的信息, 请查阅 Kubernetes API 参考文档

7 - Kubernetes API 健康端点

Kubernetes API 服务器 提供 API 端点以指示 API 服务器的当前状态。 本文描述了这些 API 端点,并说明如何使用。

API 健康端点

Kubernetes API 服务器提供 3 个 API 端点(healthzlivezreadyz)来表明 API 服务器的当前状态。 healthz 端点已被弃用(自 Kubernetes v1.16 起),你应该使用更为明确的 livezreadyz 端点。 livez 端点可与 --livez-grace-period 标志一起使用,来指定启动持续时间。 为了正常关机,你可以使用 /readyz 端点并指定 --shutdown-delay-duration 标志。 检查 API 服务器的 healthz/livez/readyz 端点的机器应依赖于 HTTP 状态代码。 状态码 200 表示 API 服务器是 healthylive 还是 ready,具体取决于所调用的端点。 以下更详细的选项供操作人员使用,用来调试其集群或了解 API 服务器的状态。

以下示例将显示如何与运行状况 API 端点进行交互。

对于所有端点,都可以使用 verbose 参数来打印检查项以及检查状态。 这对于操作人员调试 API 服务器的当前状态很有用,这些不打算给机器使用:

curl -k https://localhost:6443/livez?verbose

或从具有身份验证的远程主机:

kubectl get --raw='/readyz?verbose'

输出将如下所示:

[+]ping ok
[+]log ok
[+]etcd ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
healthz check passed

Kubernetes API 服务器也支持排除特定的检查项。 查询参数也可以像以下示例一样进行组合:

curl -k 'https://localhost:6443/readyz?verbose&exclude=etcd'

输出显示排除了 etcd 检查:

[+]ping ok
[+]log ok
[+]etcd excluded: ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
[+]shutdown ok
healthz check passed

独立健康检查

特性状态: Kubernetes v1.31 [alpha]

每个单独的健康检查都会公开一个 HTTP 端点,并且可以单独检查。 单个运行状况检查的模式为 /livez/<healthcheck-name>/readyz/<healthcheck-name>, 其中 livezreadyz 分别表明你要检查的是 API 服务器是否存活或就绪。 <healthcheck-name> 的路径可以通过上面的 verbose 参数发现 ,并采用 [+]ok 之间的路径。 这些单独的健康检查不应由机器使用,但对于操作人员调试系统而言,是有帮助的:

curl -k https://localhost:6443/livez/etcd