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

返回本页常规视图.

API 概述

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

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

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

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

API 版本控制

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

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

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

下面是每个级别的摘要:

  • Alpha:
    • 版本名称包含 alpha(例如,v1alpha1)。
    • 软件可能会有 Bug。启用某个特性可能会暴露出 Bug。 某些特性可能默认禁用。
    • 对某个特性的支持可能会随时被删除,恕不另行通知。
    • API 可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
    • 由于缺陷风险增加和缺乏长期支持,建议该软件仅用于短期测试集群。
  • Beta:
    • 版本名称包含 beta (例如, v2beta3)。
    • 软件被很好的测试过。启用某个特性被认为是安全的。 特性默认开启。
    • 尽管一些特性会发生细节上的变化,但它们将会被长期支持。
  • 在随后的 Beta 版或稳定版中,对象的模式和(或)语义可能以不兼容的方式改变。 当这种情况发生时,将提供迁移说明。 模式更改可能需要删除、编辑和重建 API 对象。 编辑过程可能并不简单。 对于依赖此功能的应用程序,可能需要停机迁移。
  • 该版本的软件不建议生产使用。 后续发布版本可能会有不兼容的变动。 如果你有多个集群可以独立升级,可以放宽这一限制。
说明: 请试用测试版特性时并提供反馈。特性完成 Beta 阶段测试后, 就可能不会有太多的变更了。
  • Stable:
    • 版本名称如 vX,其中 X 为整数。
    • 特性的稳定版本会出现在后续很多版本的发布软件中。

API 组

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

以下是 Kubernetes 中的几个组:

  • 核心(也叫 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 服务器和控制器管理器来使 --runtime-config 生效。

持久化

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

接下来

1 - Kubernetes API 概念

本页描述 Kubernetes API 的通用概念。

Kubernetes API 是基于资源的(RESTful)、通过 HTTP 提供的编程接口。 API 支持通过标准的 HTTP 动词(POST、PUT、PATCH、DELETE 和 GET) 检视、创建、更新和删除主要资源,为很多允许细粒度权限控制的对象提供子资源 (如将 Pod 绑定到节点上),并且出于便利性或效率考虑,支持并提供这些资源的 不同表示形式。 Kubernetes API 还通过 "watch" 和一致性的列表支持高效的资源变更通知, 从而允许其他组件对资源的状态进行高效的缓存和同步。

标准 API 术语

大多数 Kubernetes API 资源类型都是 对象: 它们代表的是集群中某一概念的具体实例,例如一个 Pod 或名字空间。 为数不多的几个 API 资源类型是“虚拟的” - 它们通常代表的是操作而非对象本身, 例如访问权限检查(使用 POST 请求发送一个 JSON 编码的 SubjectAccessReview 负载到 subjectaccessreviews 资源)。 所有对象都有一个唯一的名字,以便支持幂等的创建和检视操作,不过如果虚拟资源类型 不可检视或者不要求幂等,可以不具有唯一的名字。

Kubernetes 一般会利用标准的 RESTful 术语来描述 API 概念:

  • 资源类型(Resource Type) 是在 URL 中使用的名称(podsnamespacesservices
  • 所有资源类型都有具有一个 JSON 形式(其对象的模式定义)的具体表示,称作类别(Kind)
  • 某资源类型的实例的列表称作 集合(Collection)
  • 资源类型的单个实例被称作 资源(Resource)

所有资源类型要么是集群作用域的(/apis/GROUP/VERSION/*),要么是名字空间 作用域的(/apis/GROUP/VERSION/namespaces/NAMESPACE/*)。 名字空间作用域的资源类型会在其名字空间被删除时也被删除,并且对该资源类型的 访问是由定义在名字空间域中的授权检查来控制的。 下列路径用来检视集合和资源:

  • 集群作用域的资源:
    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回指定资源类型的资源的集合
    • GET /apis/GROUP/VERSION/RESOURCETYPE/NAME - 返回指定资源类型下名称为 NAME 的资源
  • 名字空间作用域的资源:
    • GET /apis/GROUP/VERSION/RESOURCETYPE - 返回所有名字空间中指定资源类型的全部实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE - 返回名字空间 NAMESPACE 内给定资源类型的全部实例的集合
    • GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME - 返回名字空间 NAMESPACE 中给定资源类型的名称为 NAME 的实例

由于名字空间本身是一个集群作用域的资源类型,你可以通过 GET /api/v1/namespaces/ 检视所有名字空间的列表,使用 GET /api/v1/namespaces/NAME 查看特定名字空间的 详细信息。

几乎所有对象资源类型都支持标准的 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。 Kubernetes 使用术语 list 来描述返回资源集合的操作,以便与返回单个资源的、 通常称作 get 的操作相区分。

某些资源类型有一个或多个子资源(Sub-resource),表现为对应资源下面的子路径:

  • 集群作用域的子资源:GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
  • 名字空间作用域的子资源:GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE

取决于对象是什么,每个子资源所支持的动词有所不同 - 参见 API 文档以了解更多信息。 跨多个资源来访问其子资源是不可能的 - 如果需要这一能力,则通常意味着需要一种 新的虚拟资源类型了。

高效检测变更

为了使客户端能够构造一个模型来表达集群的当前状态,所有 Kubernetes 对象资源类型 都需要支持一致的列表和一个称作 watch 的增量变更通知信源(feed)。 每个 Kubernetes 对象都有一个 resourceVersion 字段,代表该资源在下层数据库中 存储的版本。检视资源集合(名字空间作用域或集群作用域)时,服务器返回的响应 中会包含 resourceVersion 值,可用来向服务器发起 watch 请求。 服务器会返回所提供的 resourceVersion 之后发生的所有变更(创建、删除和更新)。 这使得客户端能够取回当前的状态并监视其变更,且不会错过任何变更事件。 客户端的监视连接被断开时,可以从最后返回的 resourceVersion 重启新的监视连接, 或者执行一个新的集合请求之后从头开始监视操作。 参阅资源版本语义以了解更多细节。

例如:

  1. 列举给定名字空间中的所有 Pods:

    GET /api/v1/namespaces/test/pods
    ---
    200 OK
    Content-Type: application/json
    {
      "kind": "PodList",
      "apiVersion": "v1",
      "metadata": {"resourceVersion":"10245"},
      "items": [...]
    }
    
  1. 从资源版本 10245 开始,以 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,清空其本地的缓存,重新执行 list 操作, 并基于新的 list 操作所返回的 resourceVersion 来开始新的 watch 操作。 大多数客户端库都能够提供某种形式的、包含此逻辑的工具。 (在 Go 语言客户端库中,这一设施称作 Reflector,位于 k8s.io/client-go/cache 包中。)

监视书签

为了处理历史窗口过短的问题,我们引入了 bookmark(书签) 监视事件的概念。 该事件是一种特殊事件,用来标示客户端所请求的、指定的 resourceVersion 之前 的所有变更都以被发送。该事件中返回的对象是所请求的资源类型,但其中仅包含 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 事件,但是客户端不能假定服务器端会按某特定时间间隔返回书签事件,甚至也不能 假定服务器一定会发送 bookmark 事件。

分块检视大体量结果

FEATURE STATE: Kubernetes v1.9 [beta]

在较大规模的集群中,检视某些资源类型的集合时可能会返回较大体量的响应数据,对 服务器和客户端都会造成影响。例如,某集群可能包含数万个 Pod,每个 Pod 的 JSON 编码都有 1-2 KB 的大小。返回所有名字空间的全部 Pod 时,其结果可能体量很大 (10-20 MB)且耗用大量的服务器资源。 从 Kubernetes 1.9 开始,服务器支持将单一的大体量集合请求分解成多个小数据块 同时还保证整个请求的一致性的能力。 各个数据块可以按顺序返回,进而降低请求的尺寸,允许面向用户的客户端以增量形式 呈现返回结果,改进系统响应效果。

为了用分块的形式返回一个列表,集合请求上可以设置两个新的参数 limitcontinue,并且所有 list 操作的返回结果列表的 metadata 字段中会包含一个 新的 continue 字段。 客户端应该将 limit 设置为希望在每个数据块中收到的结果个数上限,而服务器则 会在结果中至多返回 limit 个资源并在集合中还有更多资源的时候包含一个 continue 值。客户端在下次请求时则可以将此 continue 值传递给服务器, 告知后者要从何处开始返回结果的下一个数据块。 通过重复这一操作直到服务器端返回空的 continue 值,客户端可以受到结果的 全集。

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

例如,如果集群上有 1253 个 Pods,客户端希望每次收到包含至多 500 个 Pod 的 数据块,它应按下面的步骤来请求数据块:

  1. 列举集群中所有 Pod,每次接收至多 500 个 Pods:
GET /api/v1/pods?limit=500
---
200 OK
Content-Type: application/json

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion":"10245",
    "continue": "ENCODED_CONTINUE_TOKEN",
    ...
  },
  "items": [...] // returns pods 1-500
}
  1. 继续前面的调用,返回下一组 500 个 Pods:
GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN
---
200 OK
Content-Type: application/json

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion":"10245",
    "continue": "ENCODED_CONTINUE_TOKEN_2",
    ...
  },
  "items": [...] // returns pods 501-1000
}
  1. 继续前面的调用,返回最后 253 个 Pods:
GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN_2
---
200 OK
Content-Type: application/json

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion":"10245",
    "continue": "", // continue token is empty because we have reached the end of the list
    ...
  },
  "items": [...] // returns pods 1001-1253
}

注意 list 操作的 resourceVersion 在每个请求中都设置的是同一个数值, 这表明服务器要向我们展示一个一致的 Pods 快照视图。 在版本 10245 之后创建、更新或删除的 Pods 都不会显示出来,除非用户发出 list 请求时不指定 continue 令牌。 这一设计使得客户端能够将较大的响应切分为较小的数据块,且能够对较大的集合 执行监视动作而不会错失任何更新事件。

以表格形式接收资源

kubectl get 命令的输出是一个包含一个或多个资源的简单表格形式。 过去,客户端需要重复 kubectl 中所实现的表格输出和描述输出逻辑,以执行 简单的对象列表操作。 这一方法在处理某些对象时,需要引入不容忽视的逻辑。 此外,API 聚合定制资源 所提供的资源类型都是编译时不可预知的。这意味着,客户端必须针对无法 识别的类型提供通用的实现逻辑。

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

例如,以 Table 格式列举集群中所有 Pods:

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

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

对于在服务器上不存在定制的 Table 定义的 API 资源类型而言,服务器会返回 一个默认的 Table 响应,其中包含资源的 namecreationTimestamp 字段。

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

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

kube-apiserver 从 1.10 版本开始提供 Table 响应。 因此,并非所有 API 资源类型都支持 Table 响应,尤其是使用客户端访问较老的集群时。 如果客户端需要能够处理所有资源类型,或者有可能需要与较老的集群交互, 则需要在其 Accept 头部设定多个内容类型值,以便可以回退到非表格形式的 JSON 表示。

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

资源的其他表示形式

默认情况下,Kubernetes 返回 JSON 序列化的的对象并设定内容类型为 application/json。这是 API 的默认序列化格式。 不过,客户端也可出于大规模环境中更佳性能的需求而请求对象的更为高效的 Protobuf 表现形式。 Kubernetes API 实现了标准的 HTTP 内容类型协商:为 GET 调用传递一个 Accept 头部来请求服务器以所指定的内容类型返回对象,同时在通过 PUTPOST 调用 向服务器发送 Protobuf 格式的对象时提供 Content-Type 头部。 服务器会能够支持所请求的格式时返回 Content-Type 头部,并在所提供的内容类型 不合法时返回 406 Not acceptable(无法接受) 错误。

请参阅 API 文档了解每个 API 所支持的内容类型。

例如:

  1. 以 Protobuf 格式列举集群上的所有 Pods:
GET /api/v1/pods
Accept: application/vnd.kubernetes.protobuf
---
200 OK
Content-Type: application/vnd.kubernetes.protobuf

... binary encoded PodList object
  1. 通过向服务器发送 Protobuf 编码的数据创建 Pod,但请求以 JSON 形式接收响应:
POST /api/v1/namespaces/test/pods
Content-Type: application/vnd.kubernetes.protobuf
Accept: application/json
... binary encoded Pod object
---
200 OK
Content-Type: application/json

{
  "kind": "Pod",
  "apiVersion": "v1",
  ...
}

并非所有 API 资源类型都支持 Protobuf,尤其是那些通过定制资源定义(CRD)或通过 API 扩展而加入的资源。如果客户端必须能够处理所有资源类型,则应在其 Accept 头部指定多种内容类型以便可以回退到 JSON 格式:

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

Protobuf encoding

Kubernetes 使用封套形式来对 Protobuf 响应进行编码。 封套外层由 4 个字节的特殊数字开头,便于从磁盘文件或 etcd 中辩识 Protobuf 格式的(而不是 JSON)数据。 接下来存放的是 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;
  }

收到 application/vnd.kubernetes.protobuf 格式响应的客户端在响应与预期的前缀 不匹配时应该拒绝响应,因为将来的版本可能需要以某种不兼容的方式更改序列化格式, 并且这种更改是通过变更前缀完成的。

资源删除

资源删除要经过两个阶段:1) 终止(finalization),和 2)去除。

{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    "finalizers": {"url.io/neat-finalization", "other-url.io/my-finalizer"},
    "deletionTimestamp": nil,
  }
}

当客户端首先删除某资源时,其 .metadata.deletionTimestamp 会被设置为当前时间。 一旦 .metadata.deletionTimestamp 被设置,则对终结器(finalizers)执行动作 的外部控制器就可以在任何时候、以任何顺序执行其清理工作。 这里不强调顺序是因为很可能带来 .metadata.finalizers 被锁定的风险。 .metadata.finalizers 是一个共享的字段,任何具有相关权限的主体都可以对其 执行重排序的操作。如果终结器列表要按顺序处理,则很可能导致负责列表中第一个 终结器的组件要等待负责列表中排序靠后的终结器的组件的信号(可能是字段值变更、 外部系统或者其他形式),从而导致死锁行为。 在不对终结器顺序作强制要求的情况下,终结器可以自行排序,且不会因为其在列表 中的顺序而引入任何不稳定因素。

当最后一个终结器也被移除时,资源才真正从 etcd 中移除。

单个资源 API

API 动词 GET、CREATE、UPDATE、PATCH、DELETE 和 PROXY 仅支持单个资源。 这些支持单一资源的动词不支持以有序或无序列表甚或事务的形式同时提交给 多个资源。 包括 kubectl 在内的客户端将解析资源的列表,并执行单一资源的 API 请求。

API 动词 LIST 和 WATCH 支持获取多个资源,而 DELETECOLLECTION 支持删除多个 资源。

试运行

FEATURE STATE: Kubernetes v1.18 [stable]

修改性质的动词(POSTPUTPATCHDELETE)可以支持 试运行(dry run) 模式的请求。试运行模式可帮助通过典型的请求阶段(准入控制链、合法性 检查、合并冲突)来评估请求,只是最终的对象不会写入存储。请求的响应主体与 非试运行模式下的响应尽可能接近。系统会保证试运行模式的请求不会被写入到存储 中,也不会产生其他副作用。

发起试运行请求

通过设置 dryRun 查询参数可以触发试运行模式。此参数是一个字符串,以枚举值 的形式工作且可接受的值只有:

  • All:每个阶段被会正常运行,除了最后的存储阶段。准入控制器会被运行来检查请求 是否合法,变更性(Mutating)控制器会变更请求,PATCH 请求也会触发合并操作, 对象字段的默认值也会被设置,且基于模式定义的合法性检查也会被执行。 所生成的变更不会被写入到下层的持久性存储中,但本来会写入到数据库中的最终对象 会和正常的状态代码一起被返回给用户。如果请求会触发准入控制器而该准入控制器 带有一定的副作用,则请求会失败而不是冒险产生不希望的副作用。 所有的内置准入控制器插件都支持试运行模式。此外,准入控制 Webhook 也可在其 配置对象 中通过将 sideEffects 字段设置为 "None" 来声明自身不会产生副作用。 如果某 Webhook 确实会产生副作用,那么 sideEffects 字段应该设置为 "NoneOnDryRun", 并且 Webhook 应该被更改以支持 AdmissionReview 中的 dryRun 字段,从而避免 在试运行时产生副作用。

  • 空字符串(也即默认值):保留默认的修改行为。

例如:

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

响应会与非试运行模式请求的响应看起来相同,只是某些生成字段的值可能会不同。

试运行的授权

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

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

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

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

生成的值

对象的某些值通常是在对象被写入数据库之前生成的。很重要的一点是不要依赖试运行 请求为这些字段所设置的值,因为试运行模式下所得到的这些值与真实请求所获得的 值很可能不同。这类字段有:

  • name:如果设置了 generateName 字段,则 name 会获得一个唯一的随机名称
  • creationTimestamp/deletionTimestamp:记录对象的创建/删除时间
  • UID:唯一性标识对象,取值随机生成(非确定性)
  • resourceVersion: 跟踪对象的持久化(存储)版本
  • 变更性准入控制器所设置的字段
  • 对于 Service 资源:kube-apiserverv1.Service 对象分配的端口和 IP

服务器端应用

FEATURE STATE: Kubernetes v1.16 [beta]

从 Kubernetes v1.18 开始,可以启用服务器端应用功能 特性,启用该特性后,控制面会跟踪所有新创建的对象的托管字段。服务器端应用提供了一种简洁的模式来管理字段冲突,提供服务器端的 ApplyUpdate 操作,并取代了 kubectl apply 的客户端功能。有关该特性的详细描述,请参见服务器端应用章节

资源版本

资源版本采用字符串来表达,用来标示对象的服务器端内部版本。 客户端可以使用资源版本来判定对象是否被更改,或者在读取、列举或监视资源时 用来表达数据一致性需求。 客户端必需将资源版本视为不透明的对象,将其原封不动地传递回服务器端。 例如,客户端一定不能假定资源版本是某种数值标识,也不可以对两个资源版本值 进行比较看其是否相同(也就是不可以比较两个版本值以判断其中一个比另一个 大或小)。

metadata 中的 resourceVersion

客户端可以在资源中看到资源版本信息,这里的资源包括从服务器返回的 Watch 事件 以及 list 操作响应:

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

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

resourceVersion 参数

GET、LIST 和 WATCH 操作都支持 resourceVersion 参数。

参数的具体含义取决于所执行的操作和所给的 resourceVersion 值:

对于 GET 和 LIST 而言,资源版本的语义为:

GET:

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

LIST:

v1.19 及以上版本的 API 服务器支持 resourceVersionMatch 参数,用以确定如何对 LIST 调用应用 resourceVersion 值。 强烈建议在为 LIST 调用设置了 resourceVersion 时也设置 resourceVersionMatch。 如果 resourceVersion 未设置,则 resourceVersionMatch 是不允许设置的。 为了向后兼容,客户端必须能够容忍服务器在某些场景下忽略 resourceVersionMatch 的行为:

  • 当设置 resourceVersionMatch=NotOlderThan 且指定了 limit 时,客户端必须能够 处理 HTTP 410 "Gone" 响应。例如,客户端可以使用更新一点的 resourceVersion 来重试,或者回退到 resourceVersion="" (即允许返回任何版本)。

  • 当设置了 resourceVersionMatch=Exact 且未指定 limit 时,客户端必须验证 响应数据中 ListMetaresourceVersion 与所请求的 resourceVersion 匹配, 并处理二者可能不匹配的情况。例如,客户端可以重试设置了 limit 的请求。

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

list 操作的 resourceVersionMatch 与分页参数
resourceVersionMatch 参数分页参数resourceVersion 未设置resourceVersion="0"resourceVersion="<非零值>"
resourceVersionMatch 未设置limit 未设置最新版本任意版本不老于指定版本
resourceVersionMatch 未设置limit=<n>, continue 未设置最新版本任意版本精确匹配
resourceVersionMatch 未设置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 未设置非法请求任意版本不老于指定版本

脚注:

[1] 如果服务器无法正确处理 resourceVersionMatch 参数,其行为与未设置该参数相同。

GET 和 LIST 操作的语义含义如下:

  • 最新版本: 返回资源版本为最新的数据。所返回的数据必须一致 (通过票选读操作从 etcd 中取出)。
  • 任意版本: 返回任意资源版本的数据。优选最新可用的资源版本,不过不能保证 强一致性;返回的数据可能是任何资源版本的。请求返回的数据有可能是客户端以前 看到过的很老的资源版本。尤其在某些高可用配置环境中,网络分区或者高速缓存 未被更新等状态都可能导致这种状况。不能容忍这种不一致性的客户端不应采用此 语义。
  • 不老于指定版本: 返回至少比所提供的 resourceVersion 还要新的数据。 优选最新的可用数据,不过最终提供的可能是不老于所给 resourceVersion 的任何版本。 对于发给能够正确处理 resourceVersionMatch 参数的服务器的 LIST 请求,此语义 保证 ListMeta 中的 resourceVersion 不老于请求的 resourceVersion,不过 不对列表条目之 ObjectMetaresourceVersion 提供任何保证。 这是因为 ObjectMeta.resourceVersion 所跟踪的是列表条目对象上次更新的时间, 而不是对象被返回时是否是最新。

  • 确定版本: 返回精确匹配所给资源版本的数据。如果所指定的 resourceVersion 的数据不可用,服务器会响应 HTTP 410 "Gone"。 对于发送给能够正确处理 resourceVersionMatch 参数的服务器的 LIST 请求而言, 此语义会保证 ListMeta 中的 resourceVersion 与所请求的 resourceVersion 匹配, 不过不对列表条目之 ObjectMetaresourceVersion 提供任何保证。 这是因为 ObjectMeta.resourceVersion 所跟踪的是列表条目对象上次更新的时间, 而不是对象被返回时是否是最新。

  • Continue 令牌、精确匹配: 返回原先带分页参数的 LIST 调用中指定的资源版本的数据。 在最初的带分页参数的 LIST 调用之后,所有分页式的 LIST 调用都使用所返回的 Continue 令牌来跟踪最初提供的资源版本,

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

WATCH:

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

WATCH 操作语义的含义如下:

  • 读取状态并从最新版本开始: 从最新的资源版本开始 WATCH 操作。这里的 最新版本必须是一致的(即通过票选读操作从 etcd 中取出)。为了建立初始状态, WATCH 首先会处理一组合成的 "Added" 事件,这些事件涵盖在初始资源版本中存在 的所有资源实例。 所有后续的 WATCH 事件都是关于 WATCH 开始时所处资源版本之后发生的变更。
  • 读取状态并从任意版本开始: 警告:通过这种方式初始化的 WATCH 操作可能会 返回任何状态的停滞数据。请在使用此语义之前执行复核,并在可能的情况下采用其他 语义。此语义会从任意资源版本开始执行 WATCH 操作,优选最新的可用的资源版本, 不过不是必须的;采用任何资源版本作为起始版本都是被允许的。 WATCH 操作有可能起始于客户端已经观测到的很老的版本。在高可用配置环境中,因为 网络分裂或者高速缓存未及时更新的原因都会造成此现象。 如果客户端不能容忍这种不一致性,就不要使用此语义来启动 WATCH 操作。 为了建立初始状态,WATCH 首先会处理一组合成的 "Added" 事件,这些事件涵盖在 初始资源版本中存在的所有资源实例。 所有后续的 WATCH 事件都是关于 WATCH 开始时所处资源版本之后发生的变更。
  • 从指定版本开始: 从某确切资源版本开始执行 WATCH 操作。WATCH 事件都是 关于 WATCH 开始时所处资源版本之后发生的变更。与前面两种语义不同,WATCH 操作 开始的时候不会生成或处理为所提供资源版本合成的 "Added" 事件。 我们假定客户端既然能够提供确切资源版本,就应该已经拥有了起始资源版本对应的初始状态。

"410 Gone" 响应

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

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

不可用的资源版本

服务器不必未无法识别的资源版本提供服务。针对无法识别的资源版本的 LIST 和 GET 请求 可能会短暂等待,以期资源版本可用。如果所给的资源版本在一定的时间段内仍未变得 可用,服务器应该超时并返回 504 (Gateway Timeout),且可在响应中添加 Retry-After 响应头部字段,标明客户端在再次尝试之前应该等待多少秒钟。 目前,kube-apiserver 也能使用 Too large resource version(资源版本过高) 消息来标识这类响应。针对某无法识别的资源版本的 WATCH 操作可能会无限期 (直到请求超时)地等待下去,直到资源版本可用。

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

FEATURE STATE: Kubernetes v1.16 [beta]

简介

服务器端应用协助用户、控制器通过声明式配置的方式管理他们的资源。 它发送完整描述的目标(A fully specified intent), 声明式地创建和/或修改 对象

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

系统支持多个应用者(appliers)在同一个对象上开展协作。

字段管理(field management)”机制追踪对象字段的变化。 当一个字段值改变时,其所有权从当前管理器(manager)转移到施加变更的管理器。 当尝试将新配置应用到一个对象时,如果字段有不同的值,且由其他管理器管理, 将会引发冲突。 冲突引发警告信号:此操作可能抹掉其他协作者的修改。 冲突可以被刻意忽略,这种情况下,值将会被改写,所有权也会发生转移。

当你从配置文件中删除一个字段,然后应用这个配置文件, 这将触发服务端应用检查此字段是否还被其他字段管理器拥有。 如果没有,那就从活动对象中删除该字段;如果有,那就重置为默认值。 该规则同样适用于 list 或 map 项目。

服务器端应用既是原有 kubectl apply 的替代品, 也是控制器发布自身变化的一个简化机制。

如果你启用了服务器端应用,控制平面就会跟踪被所有新创建对象管理的字段。

字段管理

相对于通过 kubectl 管理的注解 last-applied, 服务器端应用使用了一种更具声明式特点的方法: 它持续的跟踪用户的字段管理,而不仅仅是最后一次的执行状态。 这就意味着,作为服务器端应用的一个副作用, 关于用哪一个字段管理器负责管理对象中的哪个字段的这类信息,都要对外界开放了。

用户管理字段这件事,在服务器端应用的场景中,意味着用户依赖并期望字段的值不要改变。 最后一次对字段值做出断言的用户将被记录到当前字段管理器。 这可以通过发送 POSTPUT、 或非应用(non-apply)方式的 PATCH 等命令来修改字段值的方式实现, 或通过把字段放在配置文件中,然后发送到服务器端应用的服务端点的方式实现。 当使用服务器端应用,尝试着去改变一个被其他人管理的字段, 会导致请求被拒绝(在没有设置强制执行时,参见冲突)。

如果两个或以上的应用者均把同一个字段设置为相同值,他们将共享此字段的所有权。 后续任何改变共享字段值的尝试,不管由那个应用者发起,都会导致冲突。 共享字段的所有者可以放弃字段的所有权,这只需从配置文件中删除该字段即可。

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

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

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply
    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

上述对象在 metadata.managedFields 中包含了唯一的管理器。 管理器由管理实体自身的基本信息组成,比如操作类型、API 版本、以及它管理的字段。

说明: 该字段由 API 服务器管理,用户不应该改动它。

不过,执行 Update 操作修改 metadata.managedFields 也是可实现的。 强烈不鼓励这么做,但当发生如下情况时, 比如 managedFields 进入不一致的状态(显然不应该发生这种情况), 这么做也是一个合理的尝试。

managedFields 的格式在 API 文档中描述。

冲突

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

  • 覆盖前值,成为唯一的管理器: 如果打算覆盖该值(或应用者是一个自动化部件,比如控制器), 应用者应该设置查询参数 force 为 true,然后再发送一次请求。 这将强制操作成功,改变字段的值,从所有其他管理器的 managedFields 条目中删除指定字段。

  • 不覆盖前值,放弃管理权: 如果应用者不再关注该字段的值, 可以从配置文件中删掉它,再重新发送请求。 这就保持了原值不变,并从 managedFields 的应用者条目中删除该字段。

  • 不覆盖前值,成为共享的管理器: 如果应用者仍然关注字段值,并不想覆盖它, 他们可以在配置文件中把字段的值改为和服务器对象一样,再重新发送请求。 这样在不改变字段值的前提下, 就实现了字段管理被应用者和所有声明了管理权的其他的字段管理器共享。

管理器

管理器识别出正在修改对象的工作流程(在冲突时尤其有用), 管理器可以通过修改请求的参数 fieldManager 指定。 虽然 kubectl 默认发往 kubectl 服务端点,但它则请求到应用的服务端点(apply endpoint)。 对于其他的更新,它默认的是从用户代理计算得来。

应用和更新

此特性涉及两类操作,分别是 Apply (内容类型为 application/apply-patch+yamlPATCH 请求) 和 Update (所有修改对象的其他操作)。 这两类操作都会更新字段 managedFields,但行为表现有一点不同。

说明:

不管你提交的是 JSON 数据还是 YAML 数据, 都要使用 application/apply-patch+yaml 作为 Content-Type 的值。

所有的 JSON 文档 都是合法的 YAML。

例如,在冲突发生的时候,只有 apply 操作失败,而 update 则不会。 此外,apply 操作必须通过提供一个 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 的方式运行。 此 update 更改 data 字段的值, 并使得字段管理器被改为 kube-controller-manager

如果把 update 操作改为 Apply,那就会因为所有权冲突的原因,导致操作失败。

合并策略

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

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

Kubernetes 1.16 和 1.17 中添加了一些标记, 允许 API 开发人员描述由 list、map、和 structs 支持的合并策略。 这些标记可应用到相应类型的对象,在 Go 文件或在 CRD 的 OpenAPI 的模式中定义:

Golang 标记OpenAPI extension可接受的值描述引入版本
//+listTypex-kubernetes-list-typeatomic/set/map适用于 list。 atomicset 适用于只包含标量元素的 list。 map 适用于只包含嵌套类型的 list。 如果配置为 atomic, 合并时整个列表会被替换掉; 任何时候,唯一的管理器都把列表作为一个整体来管理。如果是 setmap ,不同的管理器也可以分开管理条目。1.16
//+listMapKeyx-kubernetes-list-map-keys用来唯一标识条目的 map keys 切片,例如 ["port", "protocol"]仅当 +listType=map 时适用。组合值的字符串切片必须唯一标识列表中的条目。尽管有多个 key,listMapKey 是单数的,这是因为 key 需要在 Go 类型中单独的指定。1.16
//+mapTypex-kubernetes-map-typeatomic/granular适用于 map。 atomic 指 map 只能被单个的管理器整个的替换。 granular 指 map 支持多个管理器各自更新自己的字段。1.17
//+structTypex-kubernetes-map-typeatomic/granular适用于 structs;否则就像 //+mapType 有相同的用法和 openapi 注释.1.17

自定义资源

默认情况下,服务器端应用把自定义资源看做非结构化数据。 所有的键值(keys)就像 struct 的字段一样被处理, 所有的 list 被认为是原子性的。

如果自定义资源定义(Custom Resource Definition,CRD)定义了一个 模式, 它包含类似以前“合并策略”章节中定义过的注解, 这些注解将在合并此类型的对象时使用。

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

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

  • 应用的对象必须包含控制器关注的所有字段。
  • 对于在控制器没有执行过应用操作之前就已经存在的字段,不能删除。 (控制器在这种用例环境下,依然可以发送一个 PATCH/UPDATE)
  • 对象不必事先读取,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 启用 HPA,例如:

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

现在,用户希望从他们的配置中删除 replicas,所以他们总是和 HPA 控制器冲突。 然而,这里存在一个竟态: 在 HPA 需要调整 replicas 之前会有一个时间窗口, 如果在 HPA 写入字段成为所有者之前,用户删除了replicas, 那 API 服务器就会把 replicas 的值设为1, 也就是默认值。 这不是用户希望发生的事情,即使是暂时的。

这里有两个解决方案:

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

首先,用户新定义一个只包含 replicas 字段的配置文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3

用户使用名为 handover-to-hpa 的字段管理器,应用此配置文件。

kubectl apply -f https://k8s.io/examples/application/ssa/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 设置了一个新值, 该临时字段管理器将不再拥有任何字段,会被自动删除。 这里不需要执行清理工作。

在用户之间转移所有权

通过在配置文件中把一个字段设置为相同的值,用户可以在他们之间转移字段的所有权, 从而共享了字段的所有权。 当用户共享了字段的所有权,任何一个用户可以从他的配置文件中删除该字段, 并应用该变更,从而放弃所有权,并实现了所有权向其他用户的转移。

与客户端应用的对比

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

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

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

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

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

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

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

注意:

保持注解 last-applied-configuration 是最新的。 从注解能推断出字段是由客户端应用管理的。 任何没有被客户端应用管理的字段将引发冲突。

举例说明,比如你在客户端应用之后, 使用 kubectl scale 去更新 replicas 字段, 可是该字段并没有被客户端应用所拥有, 在执行 kubectl apply --server-side 时就会产生冲突。

此操作以 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 格式的 部分定义对象(partially specified objects)发送到此端点。 当一个配置文件被应用时,它应该包含所有体现你意图的字段。

清除 ManagedFields

可以从对象中剥离所有 managedField, 实现方法是通过使用 MergePatchStrategicMergePatchJSONPatchUpdate、以及所有的非应用方式的操作来覆盖它。 这可以通过用空条目覆盖 managedFields 字段的方式实现。

PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/merge-patch+json
Accept: application/json
Data: {"metadata":{"managedFields": [{}]}}
PATCH /api/v1/namespaces/default/configmaps/example-cm
Content-Type: application/json-patch+json
Accept: application/json
Data: [{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]

这一操作将用只包含一个空条目的 list 覆写 managedFields, 来实现从对象中整个的去除 managedFields。 注意,只把 managedFields 设置为空 list 并不会重置字段。 这么做是有目的的,所以 managedFields 将永远不会被与该字段无关的客户删除。

在重置操作结合 managedFields 以外其他字段更改的场景中, 将导致 managedFields 首先被重置,其他改变被押后处理。 其结果是,应用者取得了同一个请求中所有字段的所有权。

注意: 对于不接受资源对象类型的子资源(sub-resources), 服务器端应用不能正确地跟踪其所有权。 如果你对这样的子资源使用服务器端应用,变更的字段将不会被跟踪。

禁用此功能

服务器端应用是一个 beta 版特性,默认启用。 要关闭此特性门控, 你需要在启动 kube-apiserver 时包含参数 --feature-gates ServerSideApply=false。 如果你有多个 kube-apiserver 副本,他们都应该有相同的标记设置。

3 - 客户端库

本页面包含基于各种编程语言使用 Kubernetes API 的客户端库概述。

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

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

官方支持的 Kubernetes 客户端库

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

语言客户端库样例程序
Gogithub.com/kubernetes/client-go/浏览
Pythongithub.com/kubernetes-client/python/浏览
Javagithub.com/kubernetes-client/java浏览
dotnetgithub.com/kubernetes-client/csharp浏览
JavaScriptgithub.com/kubernetes-client/javascript浏览
Haskellgithub.com/kubernetes-client/haskell浏览

社区维护的客户端库

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

语言客户端库
Clojuregithub.com/yanatan16/clj-kubernetes-api
Gogithub.com/ericchiang/k8s
Java (OSGi)bitbucket.org/amdatulabs/amdatu-kubernetes
Java (Fabric8, OSGi)github.com/fabric8io/kubernetes-client
Javagithub.com/manusa/yakc
Lispgithub.com/brendandburns/cl-k8s
Lispgithub.com/xh4/cube
Node.js (TypeScript)github.com/Goyoo/node-k8s-client
Node.jsgithub.com/ajpauwels/easy-k8s
Node.jsgithub.com/godaddy/kubernetes-client
Node.jsgithub.com/tenxcloud/node-kubernetes-client
Perlmetacpan.org/pod/Net::Kubernetes
PHPgithub.com/allansun/kubernetes-php-client
PHPgithub.com/maclof/kubernetes-client
PHPgithub.com/travisghansen/kubernetes-client-php
PHPgithub.com/renoki-co/php-k8s
Pythongithub.com/eldarion-gondor/pykube
Pythongithub.com/fiaas/k8s
Pythongithub.com/mnubo/kubernetes-py
Pythongithub.com/tomplus/kubernetes_asyncio
Pythongithub.com/Frankkkkk/pykorm
Rubygithub.com/abonas/kubeclient
Rubygithub.com/Ch00k/kuber
Rubygithub.com/kontena/k8s-client
Rustgithub.com/clux/kube-rs
Rustgithub.com/ynqa/kubernetes-rust
Scalagithub.com/hagay3/skuber
Scalagithub.com/joan38/kubernetes-client
Swiftgithub.com/swiftkube/client
DotNetgithub.com/tonnyeremin/kubernetes_gen
DotNet (RestSharp)github.com/masroorhasan/Kubernetes.DotNet
Elixirgithub.com/obmarg/kazan
Elixirgithub.com/coryodaniel/k8s

4 - Kubernetes 弃用策略

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

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

弃用 API 的一部分

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

示例分类
v1正式发布(Generally available,GA,稳定版本)
v1beta1Beta (预发布)
v1alpha1Alpha (试验性)

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

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

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

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

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

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

说明: 由于历史原因,Kubernetes 中存在两个“单体式(Monolithic)”API 组 - “core”(无组名)和“extensions”。这两个遗留 API 组中的资源会被逐渐迁移到 更为特定领域的 API 组中。

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

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

规则 #3:给定类别的 API 版本在新的、稳定性未降低的 API 版本发布之前不可被废弃。

一个正式发布的(GA)API 版本可替换现有的正式 API 版本或 alpha、beta API 版本。 Beta API 版本 不可以 替代正式的 API 版本。

规则 #4a:除了每类 API 版本中的最新版本,旧的 API 版本在其被宣布被废弃之后 至少以下时长内仍需被支持:

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

这里也包含了关于最大支持 2 个发布版本的版本偏差的约定。

说明:#52185被解决之前, 已经被保存到持久性存储中的 API 版本都不可以被去除。 你可以禁止这些版本所对应的 REST 末端(在符合本文中弃用时间线的前提下), 但是 API 服务器必须仍能解析和转换存储中以前写入的数据。

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

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

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

发布版本API 版本优选/存储版本注释
Xv1alpha1v1alpha1
X+1v1alpha2v1alpha2
  • v1alpha1 被去除,发布说明中会包含 "action required(采取行动)" 说明
X+2v1beta1v1beta1
  • v1alpha2 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+3v1beta2、v1beta1(已弃用)v1beta1
  • v1beta1 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+4v1beta2、v1beta1(已弃用)v1beta2
X+5v1、v1beta1(已弃用)、v1beta2(已弃用)v1beta2
  • v1beta2 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+6v1、v1beta2(已弃用)v1
  • v1beta1 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+7v1、v1beta2(已弃用)v1
X+8v2alpha1、v1v1
  • v1beta2 被去除,发布说明中包含对应的 "action required(采取行动)" 说明
X+9v2alpha2、v1v1
  • v2alpha1 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+10v2beta1、v1v1
  • v2alpha2 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+11v2beta2、v2beta1(已弃用)、v1v1
  • v2beta1 被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+12v2、v2beta2(已弃用)、v2beta1(已弃用)、v1(已弃用)v1
  • v2beta2 已被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
  • v1 已被弃用,发布说明中包含对应的 "action required(采取行动)" 说明
X+13v2、v2beta1(已弃用)、v2beta2(已弃用)、v1(已弃用)v2
X+14v2、v2beta2(已弃用)、v1(已弃用)v2
  • v2beta1 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+15v2、v1(已弃用)v2
  • v2beta2 被删除,发布说明中包含对应的 "action required(采取行动)" 说明
X+16v2、v1(已弃用)v2
X+17v2v2
  • v1 被删除,发布说明中包含对应的 "action required(采取行动)" 说明

REST 资源(也即 API 对象)

考虑一个假想的名为 Widget 的 REST 资源,在上述时间线中位于 API v1, 而现在打算将其弃用。 我们会在文档和 声明 中与 X+1 版本的发布同步记述此弃用决定。 Wdiget 资源仍会在 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 个发布版本

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

弃用某功能特性或行为

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

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

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

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

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

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

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

弃用

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

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

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

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

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

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

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

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

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

弃用度量值 {#Deprecating a metric}

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

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

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

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

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

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

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

在随后的版本中(当度量值 deprecatedVersion 等于_当前 Kubernetes 版本 - 3_), 被弃用的度量值将变成 _隐藏(Hidden)_ metric 度量值。 与被弃用的度量值不同,隐藏的度量值将不再被自动注册到 metrics 端点(因此被隐藏)。 但是,它们可以通过可执行文件的命令行标志显式启用(--show-hidden-metrics-for-version=)。

如果集群管理员不能对早期的弃用警告作出反应,这一设计就为他们提供了抓紧迁移弃用度量值的途径。 隐藏的度量值应该在再过一个发行版本后被删除。

例外

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

5 - 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 服务器的 health/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

独立健康检查

FEATURE STATE: Kubernetes v1.21 [alpha]

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

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