Kubernetes API 概念
Kubernetes API 是通过 HTTP 提供的基于资源(RESTful)的编程接口。它支持通过标准 HTTP 动词(POST、PUT、PATCH、DELETE、GET)检索、创建、更新和删除主要资源。
对于某些资源,API 包含额外的子资源,这些子资源允许进行细粒度授权(例如,对 Pod 详细信息和日志检索的不同视图),并且可以接受和提供这些资源的不同表示形式以方便或提高效率。
Kubernetes 通过 *watches* 支持对资源进行高效的更改通知。Kubernetes 还提供一致的列表操作,以便 API 客户端可以有效地缓存、跟踪和同步资源状态。
您可以在线查看 API 参考,或继续阅读以了解有关 API 的一般信息。
Kubernetes API 术语
Kubernetes 通常利用常见的 RESTful 术语来描述 API 概念
- *资源类型* 是 URL 中使用的名称(
pods
、namespaces
、services
) - 所有资源类型都有一个具体的表示形式(它们的 对象模式),称为 *类型*
- 资源类型实例列表称为 *集合*
- 资源类型的一个实例称为 *资源*,通常也表示一个 *对象*
- 对于某些资源类型,API 包含一个或多个 *子资源*,这些子资源表示为资源下的 URI 路径
大多数 Kubernetes API 资源类型是 对象——它们代表集群上概念的具体实例,例如 Pod 或命名空间。较少的 API 资源类型是 *虚拟* 的,因为它们通常表示对对象的运算,而不是对象本身,例如权限检查(使用包含 SubjectAccessReview
的 JSON 编码体的 POST 请求到 subjectaccessreviews
资源),或 Pod 的 eviction
子资源(用于触发 API 触发的驱逐)。
对象名称
您可以通过 API 创建的所有对象都具有唯一的对象 名称 以允许幂等创建和检索,除了虚拟资源类型可能没有唯一的名称,如果它们不可检索或不依赖于幂等性。在 命名空间 中,一次只能有一个给定类型的对象具有给定名称。但是,如果您删除了对象,则可以创建一个具有相同名称的新对象。某些对象没有命名空间(例如:节点),因此它们的名称在整个集群中必须是唯一的。
API 动词
几乎所有对象资源类型都支持标准 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。Kubernetes 也使用自己的动词,这些动词通常写成小写以区分它们与 HTTP 动词。
Kubernetes 使用术语 **list** 来描述返回 资源集合 以区分检索单个资源,单个资源通常称为 **get**。如果您发送了带有 ?watch
查询参数的 HTTP GET 请求,Kubernetes 将其称为 **watch** 而不是 **get**(有关更多详细信息,请参阅 高效检测更改)。
对于 PUT 请求,Kubernetes 在内部将它们分类为 **create** 或 **update**,具体取决于现有对象的状态。**update** 与 **patch** 不同;**patch** 的 HTTP 动词是 PATCH。
资源 URI
所有资源类型都由集群(/apis/GROUP/VERSION/*
)或命名空间(/apis/GROUP/VERSION/namespaces/NAMESPACE/*
)限定。命名空间限定的资源类型将在其命名空间删除时被删除,对该资源类型的访问由命名空间范围的授权检查控制。
注意:核心资源使用 /api
而不是 /apis
并省略 GROUP 路径段。
示例
/api/v1/namespaces
/api/v1/pods
/api/v1/namespaces/my-namespace/pods
/apis/apps/v1/deployments
/apis/apps/v1/namespaces/my-namespace/deployments
/apis/apps/v1/namespaces/my-namespace/deployments/my-deployment
您还可以访问资源集合(例如:列出所有节点)。以下路径用于检索集合和资源
集群范围资源
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回资源类型资源的集合GET /apis/GROUP/VERSION/RESOURCETYPE/NAME
- 返回资源类型下具有 NAME 的资源
命名空间范围资源
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回所有命名空间中资源类型的所有实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE
- 返回命名空间中资源类型的所有实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME
- 返回命名空间中具有 NAME 的资源类型的实例
由于命名空间是集群范围的资源类型,您可以使用 GET /api/v1/namespaces
检索所有命名空间的列表(“集合”),并使用 GET /api/v1/namespaces/NAME
检索特定命名空间的详细信息。
- 集群范围子资源:
GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
- 命名空间范围子资源:
GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE
每个子资源支持的动词将根据对象有所不同 - 请参阅 API 参考 以了解更多信息。无法跨多个资源访问子资源 - 通常,如果需要,将使用新的虚拟资源类型。
高效检测更改
Kubernetes API 允许客户端对对象或集合进行初始请求,然后跟踪自该初始请求以来的更改:**watch**。客户端可以发送 **list** 或 **get**,然后发出后续 **watch** 请求。
为了使此更改跟踪成为可能,每个 Kubernetes 对象都有一个 resourceVersion
字段,表示该资源存储在底层持久性层中的版本。检索资源集合(命名空间或集群范围)时,API 服务器的响应包含一个 resourceVersion
值。客户端可以使用该 resourceVersion
来针对 API 服务器启动 **watch**。
当您发送 **watch** 请求时,API 服务器将以更改流的形式进行响应。这些更改详细说明了在您作为 **watch** 请求参数指定的 resourceVersion
之后发生的运算(例如 **create**、**delete** 和 **update**)的结果。总体的 **watch** 机制允许客户端获取当前状态,然后订阅后续更改,而不会错过任何事件。
如果客户端 **watch** 断开连接,则该客户端可以从上次返回的 resourceVersion
启动新的 **watch**;客户端也可以执行新的 **get** / **list** 请求并重新开始。有关更多详细信息,请参阅 资源版本语义。
例如
列出给定命名空间中的所有 Pod。
GET /api/v1/namespaces/test/pods --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": {"resourceVersion":"10245"}, "items": [...] }
从资源版本 10245 开始,接收影响 *test* 命名空间中 Pod 的任何 API 操作(例如 **create**、**delete**、**patch** 或 **update**)的通知。每个更改通知都是一个 JSON 文档。HTTP 响应主体(以
application/json
提供)包含一系列 JSON 文档。GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245 --- 200 OK Transfer-Encoding: chunked Content-Type: application/json { "type": "ADDED", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...} } { "type": "MODIFIED", "object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "11020", ...}, ...} } ...
给定的 Kubernetes 服务器只会保留一定时间内的更改历史记录。使用 etcd 3 的集群默认情况下会保留过去 5 分钟内的更改。当请求的 **watch** 操作由于该资源的历史版本不可用而失败时,客户端必须通过识别状态码 410 Gone
、清除本地缓存、执行新的 **get** 或 **list** 操作并从返回的 resourceVersion
开始 **watch** 来处理这种情况。
对于订阅集合,Kubernetes 客户端库通常提供某种形式的标准工具来实现此 **list** 然后 **watch** 逻辑。(在 Go 客户端库中,这称为 Reflector
,位于 k8s.io/client-go/tools/cache
包中。)
Watch 书签
为了减轻短历史窗口的影响,Kubernetes API 提供了一个名为 BOOKMARK
的 watch 事件。它是一种特殊类型的事件,用于标记客户端请求的所有更改已发送到给定的 resourceVersion
。表示 BOOKMARK
事件的文档与请求类型相同,但只包含 .metadata.resourceVersion
字段。例如
GET /api/v1/namespaces/test/pods?watch=1&resourceVersion=10245&allowWatchBookmarks=true
---
200 OK
Transfer-Encoding: chunked
Content-Type: application/json
{
"type": "ADDED",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10596", ...}, ...}
}
...
{
"type": "BOOKMARK",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "12746"} }
}
作为客户端,您可以通过将 allowWatchBookmarks=true
查询参数设置为 **watch** 请求来请求 BOOKMARK
事件,但您不应该假设书签会在任何特定间隔返回,也不应该假设 API 服务器即使在请求时也会发送任何 BOOKMARK
事件。
流式列表
Kubernetes v1.27 [alpha]
在大型集群中,检索某些资源类型的集合可能会导致控制平面上的资源使用量(主要是 RAM)显着增加。为了减轻其影响并简化 **list** + **watch** 模式的用户体验,Kubernetes v1.27 引入了对请求初始状态(以前通过 **list** 请求请求)作为 **watch** 请求的一部分的支持,作为 alpha 功能。
只要启用了 WatchList
功能开关,就可以通过在 **watch** 请求中指定 sendInitialEvents=true
作为查询字符串参数来实现。如果设置,API 服务器将使用合成 init 事件(类型为 ADDED
)启动 watch 流,以构建所有现有对象的完整状态,然后是一个 BOOKMARK
事件(如果通过 allowWatchBookmarks=true
选项请求)。书签事件包含已同步到的资源版本。发送书签事件后,API 服务器将继续进行其他任何 **watch** 请求。
当您在查询字符串中设置 sendInitialEvents=true
时,Kubernetes 还要求您将 resourceVersionMatch
设置为 NotOlderThan
值。如果您在查询字符串中提供 resourceVersion
而不提供值或根本不提供,则这被解释为请求 *一致读取*;书签事件在状态同步到请求开始处理时的时刻至少一致读取时发送。如果您指定了 resourceVersion
(在查询字符串中),则当状态同步到提供的资源版本至少时,书签事件就会发送。
示例
例如,您想要查看一组 Pod。对于该集合,当前资源版本为 10245,并且有两个 Pod:foo
和 bar
。然后发送以下请求(通过使用 resourceVersion=
设置空资源版本来明确请求一致读取)可能会导致以下事件序列
GET /api/v1/namespaces/test/pods?watch=1&sendInitialEvents=true&allowWatchBookmarks=true&resourceVersion=&resourceVersionMatch=NotOlderThan
---
200 OK
Transfer-Encoding: chunked
Content-Type: application/json
{
"type": "ADDED",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "8467", "name": "foo"}, ...}
}
{
"type": "ADDED",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "5726", "name": "bar"}, ...}
}
{
"type": "BOOKMARK",
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "10245"} }
}
...
<followed by regular watch stream starting from resourceVersion="10245">
响应压缩
Kubernetes v1.16 [beta]
APIResponseCompression
是一种允许 API 服务器压缩 get 和 list 请求的响应的选项,从而减少网络带宽并提高大型集群的性能。它从 Kubernetes 1.16 开始默认启用,可以通过在 API 服务器的 --feature-gates
标志中包含 APIResponseCompression=false
来禁用。
API 响应压缩可以显着减小响应的大小,特别是对于大型资源或 集合。例如,针对 Pod 的 list 请求可以返回数百 KB 甚至 MB 的数据,具体取决于 Pod 的数量及其属性。通过压缩响应,可以节省网络带宽并减少延迟。
要验证 APIResponseCompression
是否正常工作,您可以向 API 服务器发送带有 Accept-Encoding
标头的 get 或 list 请求,并检查响应大小和标头。例如
GET /api/v1/pods
Accept-Encoding: gzip
---
200 OK
Content-Type: application/json
content-encoding: gzip
...
content-encoding
标头表明响应已使用 gzip
压缩。
分块检索大型结果集
Kubernetes v1.29 [stable]
在大型集群中,检索某些资源类型的集合可能会导致非常大的响应,这会影响服务器和客户端。例如,集群可能拥有数万个 Pod,每个 Pod 约等于 2 KiB 的编码 JSON。检索所有命名空间中的所有 Pod 可能会导致非常大的响应(10-20 MB)并消耗大量的服务器资源。
Kubernetes API 服务器支持将单个大型集合请求分解为许多较小的块,同时保持整个请求的一致性。每个块都可以按顺序返回,这既减少了请求的总大小,又允许面向用户的客户端逐步显示结果以提高响应速度。
您可以请求 API 服务器通过使用页面(Kubernetes 称为块)来处理单个集合的 list。要分块检索单个集合,支持针对集合的请求的两个查询参数 limit
和 continue
,并且所有 list 操作在集合的 metadata
字段中返回一个响应字段 continue
。客户端应使用 limit
指定希望在每个块中接收的最大结果数,服务器将返回结果中的最多 limit
个资源,如果集合中还有更多资源,则包含一个 continue
值。
作为 API 客户端,您可以在下一个请求中将此 continue
值传递给 API 服务器,以指示服务器返回结果的下一页(块)。通过一直继续,直到服务器返回一个空的 continue
值,您就可以检索整个集合。
类似于 watch 操作,continue
令牌将在短时间后过期(默认情况下为 5 分钟),如果无法返回更多结果,则返回 410 Gone
。在这种情况下,客户端需要从头开始或省略 limit
参数。
例如,如果集群上有 1,253 个 Pod,并且您想一次接收 500 个 Pod 的块,请按如下方式请求这些块
列出集群上的所有 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 }
继续上一次调用,检索下一组 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 }
继续上一次调用,检索最后 253 个 Pod。
GET /api/v1/pods?limit=500&continue=ENCODED_CONTINUE_TOKEN_2 --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": { "resourceVersion":"10245", "continue": "", // continue token is empty because we have reached the end of the list ... }, "items": [...] // returns pods 1001-1253 }
请注意,集合的 resourceVersion
在每个请求中保持不变,表明服务器向您显示 Pod 的一致快照。在版本 10245
之后创建、更新或删除的 Pod 不会显示,除非您发出不带 continue
令牌的单独 list 请求。这使您能够将大型请求分解成较小的块,然后对整个集合执行 watch 操作,而不会错过任何更新。
remainingItemCount
是集合中未包含在此响应中的后续项目的数量。如果 list 请求包含标签或字段 选择器,则剩余项目数未知,API 服务器不会在其响应中包含 remainingItemCount
字段。如果 list 完成(因为不是分块,或者因为这是最后一个块),则没有更多剩余项目,API 服务器不会在其响应中包含 remainingItemCount
字段。remainingItemCount
的预期用途是估计集合的大小。
集合
在 Kubernetes 术语中,从 list 获取的响应称为集合。但是,Kubernetes 为不同类型资源的集合定义了具体的种类。集合的种类以资源种类命名,并在其后附加 List
。
当您查询 API 以获取特定类型时,该查询返回的所有项目都属于该类型。例如,当您 list Services 时,集合响应的 kind
设置为 ServiceList
;该集合中的每个项目都代表一个单独的 Service。例如
GET /api/v1/services
{
"kind": "ServiceList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "2947301"
},
"items": [
{
"metadata": {
"name": "kubernetes",
"namespace": "default",
...
"metadata": {
"name": "kube-dns",
"namespace": "kube-system",
...
Kubernetes API 中定义了数十种集合类型(例如 PodList
、ServiceList
和 NodeList
)。您可以从 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
注意
请记住,Kubernetes API 没有名为 List
的 kind
。
kind: List
是用于处理可能属于不同类型的对象的集合的客户端内部实现细节。避免在自动化或其他代码中依赖 kind: List
。
以表格形式接收资源
当您运行 kubectl get
时,默认输出格式是特定资源类型的一个或多个实例的简单表格表示形式。过去,客户端需要重新生成 kubectl
中实现的表格和描述输出以执行对象的简单列表。该方法有一些局限性,包括在处理某些对象时涉及的非平凡逻辑。此外,由 API 聚合或第三方资源提供的类型在编译时是未知的。这意味着必须为客户端无法识别的类型提供通用实现。
为了避免上述潜在的局限性,客户端可以请求对象的表格表示形式,将打印的具体细节委托给服务器。Kubernetes API 实现标准 HTTP 内容类型协商:传递包含 application/json;as=Table;g=meta.k8s.io;v=v1
值的 Accept
标头以及 GET
调用将请求服务器以表格内容类型返回对象。
例如,以表格格式列出集群上的所有 Pod。
GET /api/v1/pods
Accept: application/json;as=Table;g=meta.k8s.io;v=v1
---
200 OK
Content-Type: application/json
{
"kind": "Table",
"apiVersion": "meta.k8s.io/v1",
...
"columnDefinitions": [
...
]
}
对于控制平面不知道自定义表格定义的 API 资源类型,API 服务器返回一个默认表格响应,其中包含资源的 name
和 creationTimestamp
字段。
GET /apis/crd.example.com/v1alpha1/namespaces/default/resources
---
200 OK
Content-Type: application/json
...
{
"kind": "Table",
"apiVersion": "meta.k8s.io/v1",
...
"columnDefinitions": [
{
"name": "Name",
"type": "string",
...
},
{
"name": "Created At",
"type": "date",
...
}
]
}
并非所有 API 资源类型都支持表格响应;例如,自定义资源定义 可能不会定义字段到表格的映射,并且 扩展核心 Kubernetes API 的 APIService 可能根本不提供表格响应。如果您正在实现使用表格信息的客户端并且必须针对所有资源类型(包括扩展)工作,则应发出在 Accept
标头中指定多个内容类型的请求。例如
Accept: application/json;as=Table;g=meta.k8s.io;v=v1, application/json
资源的替代表示形式
默认情况下,Kubernetes 以内容类型 application/json
返回序列化为 JSON 的对象。这是 API 的默认序列化格式。但是,客户端可以请求这些对象的更高效的 Protobuf 表示形式,以便在规模上实现更好的性能。Kubernetes API 实现标准 HTTP 内容类型协商:使用 GET
调用传递包含 Accept
标头的请求将要求服务器尝试以您首选的媒体类型返回响应,而将对象以 Protobuf 格式发送到服务器以进行 PUT
或 POST
调用意味着您必须适当地设置 Content-Type
标头。
如果支持请求的格式,服务器将返回带有 Content-Type
标头的响应,如果未支持您请求的任何媒体类型,则返回 406 Not acceptable
错误。所有内置资源类型都支持 application/json
媒体类型。
有关每个 API 支持的内容类型的列表,请参阅 Kubernetes API 参考。
例如
以 Protobuf 格式列出集群上的所有 Pod。
GET /api/v1/pods Accept: application/vnd.kubernetes.protobuf --- 200 OK Content-Type: application/vnd.kubernetes.protobuf ... binary encoded PodList object
通过将 Protobuf 编码数据发送到服务器来创建 Pod,但请求以 JSON 格式返回响应。
POST /api/v1/namespaces/test/pods Content-Type: application/vnd.kubernetes.protobuf Accept: application/json ... binary encoded Pod object --- 200 OK Content-Type: application/json { "kind": "Pod", "apiVersion": "v1", ... }
并非所有 API 资源类型都支持 Protobuf;具体来说,Protobuf 不适用于定义为 自定义资源定义 或通过 聚合层 提供的资源。作为客户端,如果您可能需要使用扩展类型,则应在请求的 Accept
标头中指定多个内容类型以支持回退到 JSON。例如
Accept: application/vnd.kubernetes.protobuf, application/json
Kubernetes Protobuf 编码
Kubernetes 使用一个信封包装器来对 Protobuf 响应进行编码。该包装器以一个 4 字节的魔数开头,以帮助识别磁盘或 etcd 中的内容为 Protobuf(而不是 JSON),然后是 Protobuf 编码的包装器消息,该消息描述了基础对象的编码和类型,然后包含该对象。
包装器格式为
A four byte magic number prefix:
Bytes 0-3: "k8s\x00" [0x6b, 0x38, 0x73, 0x00]
An encoded Protobuf message with the following IDL:
message Unknown {
// typeMeta should have the string values for "kind" and "apiVersion" as set on the JSON object
optional TypeMeta typeMeta = 1;
// raw will hold the complete serialized object in protobuf. See the protobuf definitions in the client libraries for a given kind.
optional bytes raw = 2;
// contentEncoding is encoding used for the raw data. Unspecified means no encoding.
optional string contentEncoding = 3;
// contentType is the serialization method used to serialize 'raw'. Unspecified means application/vnd.kubernetes.protobuf and is usually
// omitted.
optional string contentType = 4;
}
message TypeMeta {
// apiVersion is the group/version for this type
optional string apiVersion = 1;
// kind is the name of the object schema. A protobuf definition should exist for this object.
optional string kind = 2;
}
注意
接收application/vnd.kubernetes.protobuf
响应的客户端如果该响应不匹配预期的前缀,则应拒绝该响应,因为未来的版本可能需要以不兼容的方式更改序列化格式,并且将通过更改前缀来做到这一点。资源删除
当您 delete 资源时,此过程分为两个阶段。
- 最终化
- 移除
{
"kind": "ConfigMap",
"apiVersion": "v1",
"metadata": {
"finalizers": ["url.io/neat-finalization", "other-url.io/my-finalizer"],
"deletionTimestamp": nil,
}
}
当客户端首次发送 delete 来请求删除资源时,.metadata.deletionTimestamp
将设置为当前时间。一旦设置了 .metadata.deletionTimestamp
,在最终化器上操作的外部控制器就可以随时以任何顺序开始执行其清理工作。
最终化器之间不强制执行顺序,因为这会导致 .metadata.finalizers
卡住的风险显著增加。
.metadata.finalizers
字段是共享的:任何具有权限的参与者都可以重新排序它。如果最终器列表按顺序处理,那么这可能会导致一种情况,即负责列表中第一个最终器的组件正在等待由负责列表中后面最终器的组件产生的某些信号(字段值、外部系统或其他),从而导致死锁。
在没有强制排序的情况下,最终器可以自由地在彼此之间排序,并且不会受到列表中排序更改的影响。
一旦最后一个最终器被移除,资源就会从 etcd 中真正移除。
单个资源 API
Kubernetes API 动词 **get**、**create**、**update**、**patch**、**delete** 和 **proxy** 仅支持单个资源。这些支持单个资源的动词不支持将多个资源一起以有序或无序列表或事务的形式提交。
当客户端(包括 kubectl)对一组资源执行操作时,客户端会发出一系列针对单个资源的 API 请求,然后根据需要聚合响应。
相比之下,Kubernetes API 动词 **list** 和 **watch** 允许获取多个资源,而 **deletecollection** 允许删除多个资源。
字段验证
Kubernetes 始终验证字段的类型。例如,如果 API 中的字段定义为数字,则不能将该字段设置为文本值。如果字段定义为字符串数组,则只能提供数组。某些字段允许你省略它们,其他字段是必需的。从 API 请求中省略必需字段将导致错误。
如果你使用额外的字段发出请求,而集群控制平面无法识别该字段,那么 API 服务器的行为会更加复杂。
默认情况下,API 服务器会从其接收的输入(例如,PUT
请求的 JSON 主体)中删除它无法识别的字段。
有两种情况下,API 服务器会删除你在 HTTP 请求中提供的字段。
这些情况是
- 该字段无法识别,因为它不在资源的 OpenAPI 架构中。(对此的一个例外是 CRD,它们通过
x-kubernetes-preserve-unknown-fields
明确选择不修剪未知字段)。 - 该字段在对象中重复。
对无法识别或重复字段的验证
Kubernetes v1.27 [稳定]
从 1.25 开始,当使用可以提交数据的 HTTP 动词(POST
、PUT
和 PATCH
)时,对象中无法识别或重复的字段会在服务器上通过验证进行检测。可能的验证级别有 Ignore
、Warn
(默认)和 Strict
。
忽略
- API 服务器成功处理请求,就像没有设置错误字段一样,删除所有未知和重复字段,并且不提供任何它已执行此操作的指示。
警告
- (默认)API 服务器成功处理请求,并向客户端报告警告。警告使用
Warning:
响应标头发送,为每个未知或重复字段添加一个警告项。有关警告和 Kubernetes API 的更多信息,请参阅博客文章 警告:有用的警告。 严格
- 当 API 服务器检测到任何未知或重复字段时,它会拒绝该请求,并返回 400 错误请求错误。API 服务器的响应消息会指定 API 服务器检测到的所有未知或重复字段。
字段验证级别由 fieldValidation
查询参数设置。
注意
如果你提交的请求指定了无法识别的字段,并且该请求也因其他原因无效(例如,请求提供了字符串值,而 API 对于已知字段需要整数),那么 API 服务器将返回 400 错误请求错误,但不会提供有关未知或重复字段的任何信息(仅它遇到的第一个致命错误)。
在这种情况下,无论你请求什么字段验证级别,你始终会收到错误响应。
向服务器提交请求的工具(如 kubectl
)可能会设置自己的默认值,这些默认值与 API 服务器默认使用的 Warn
验证级别不同。
kubectl
工具使用 --validate
标志来设置字段验证级别。它接受 ignore
、warn
和 strict
值,同时还接受 true
(等效于 strict
)和 false
(等效于 ignore
)值。kubectl 的默认验证设置是 --validate=true
,这意味着严格的服务器端字段验证。
当 kubectl 无法连接到具有字段验证的 API 服务器(Kubernetes 1.27 之前的 API 服务器)时,它将回退到使用客户端验证。客户端验证将在 kubectl 的未来版本中完全删除。
注意
在 Kubernetes 1.25 之前,kubectl --validate
用于作为布尔标志来切换客户端验证的开启或关闭。试运行
Kubernetes v1.19 [稳定]
当你使用可以修改资源的 HTTP 动词(POST
、PUT
、PATCH
和 DELETE
)时,你可以以试运行模式提交你的请求。试运行模式有助于通过典型的请求阶段(准入链、验证、合并冲突)评估请求,直到将对象持久化到存储。请求的响应主体尽可能接近非试运行响应。Kubernetes 保证试运行请求不会持久化到存储中,也不会产生任何其他副作用。
发出试运行请求
试运行是通过设置 dryRun
查询参数触发的。此参数是一个字符串,充当枚举,唯一接受的值是
- [未设置值]
- 允许副作用。你可以使用诸如
?dryRun
或?dryRun&pretty=true
之类的查询字符串请求此操作。响应是最终将被持久化的对象,或者如果请求无法满足,则返回错误。 全部
- 每个阶段都按正常运行,除了最终的存储阶段,该阶段会阻止副作用。
当你设置 ?dryRun=All
时,任何相关的 准入控制器 都将运行,验证准入控制器会检查请求的变异后的状态,在 PATCH
上执行合并,默认化字段,并进行模式验证。更改不会持久化到底层存储,但最终将被持久化的对象仍然会返回给用户,以及正常的状态代码。
如果请求的非试运行版本会触发具有副作用的准入控制器,则请求将失败,而不是冒着意外副作用的风险。所有内置的准入控制插件都支持试运行。此外,准入 Webhook 可以在其 配置对象 中声明它们没有副作用,方法是将其 sideEffects
字段设置为 None
。
注意
如果 Webhook 实际上确实有副作用,那么sideEffects
字段应设置为 "NoneOnDryRun"。只要 Webhook 也被修改为理解 DryRun
字段在 AdmissionReview 中,以及阻止对标记为试运行的任何请求产生副作用,这种更改就是合适的。以下是一个使用 ?dryRun=All
的试运行请求示例
POST /api/v1/namespaces/test/pods?dryRun=All
Content-Type: application/json
Accept: application/json
响应将与非试运行请求的响应相同,但一些生成字段的值可能会有所不同。
生成的值
对象的一些值通常在对象持久化之前生成。重要的是不要依赖试运行请求设置的这些字段的值,因为这些值在试运行模式下与发出真实请求时可能会有所不同。其中一些字段是
name
:如果设置了generateName
,则name
将具有唯一的随机名称creationTimestamp
/deletionTimestamp
:记录创建/删除时间UID
:唯一标识 对象,并且是随机生成的(非确定性的)resourceVersion
:跟踪对象的持久化版本- 任何由变异准入控制器设置的字段
- 对于
Service
资源:kube-apiserver 分配给 Service 对象的端口或 IP 地址
试运行授权
试运行和非试运行请求的授权相同。因此,要发出试运行请求,你必须有权发出非试运行请求。
例如,要对部署执行试运行 **patch**,你必须有权执行该 **patch**。以下是一个 Kubernetes RBAC 规则示例,它允许修补部署
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["patch"]
请参阅 授权概述。
更新现有资源
Kubernetes 提供了几种更新现有对象的方法。你可以阅读 选择更新机制 以了解哪种方法最适合你的用例。
你可以使用 HTTP PUT 覆盖(**更新**)现有资源(例如,ConfigMap)。对于 PUT 请求,客户端有责任指定 resourceVersion
(从要更新的对象中获取)。Kubernetes 使用该 resourceVersion
信息,以便 API 服务器可以检测到丢失的更新,并拒绝由与集群不一致的客户端发出的请求。如果资源已发生更改(客户端提供的 resourceVersion
已过期),则 API 服务器会返回 409 Conflict
错误响应。
客户端可以发送一条指令到 API 服务器,要求**修补**现有资源,而不是发送 PUT 请求。如果客户端想要进行的更改不依赖于现有数据,则通常应使用**修补**。需要有效检测丢失更新的客户端应该考虑根据现有 resourceVersion
(HTTP PUT 或 HTTP PATCH)使他们的请求有条件,然后处理在发生冲突时需要的任何重试。
Kubernetes API 支持四种不同的 PATCH 操作,这些操作由其相应的 HTTP Content-Type
标头决定
application/apply-patch+yaml
- 服务器端应用 YAML(一个基于 YAML 的 Kubernetes 特定扩展)。所有 JSON 文档都是有效的 YAML,因此你也可以使用此媒体类型提交 JSON。有关更多详细信息,请参阅 服务器端应用序列化。
对于 Kubernetes,如果对象不存在,则此操作为**创建**操作,如果对象已存在,则此操作为**修补**操作。 application/json-patch+json
- JSON Patch,如 RFC6902 中所定义。JSON Patch 是一系列在资源上执行的操作;例如
{"op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ]}
。
对于 Kubernetes,此操作为**修补**操作。使用
application/json-patch+json
的**修补**可以包含条件以验证一致性,允许操作在不满足这些条件时失败(例如,为了避免丢失更新)。 application/merge-patch+json
- JSON 合并补丁,如 RFC7386 中所定义。JSON 合并补丁本质上是资源的局部表示。提交的 JSON 与当前资源结合以创建一个新的资源,然后保存新资源。
对于 Kubernetes,此操作为**修补**操作。 application/strategic-merge-patch+json
- 策略合并补丁(基于 JSON 的 Kubernetes 特定扩展)。策略合并补丁是 JSON 合并补丁的自定义实现。你只能将策略合并补丁用于内置 API,或用于具有对其特殊支持的聚合 API 服务器。你不能将
application/strategic-merge-patch+json
用于使用 CustomResourceDefinition 定义的任何 API。注意
Kubernetes 的服务器端应用机制已经取代了策略合并补丁。
Kubernetes 的 服务器端应用 功能允许控制平面跟踪新创建对象的管理字段。服务器端应用为管理字段冲突提供了一个清晰的模式,提供服务器端应用和更新操作,并取代了 kubectl apply
的客户端功能。
对于服务器端应用,Kubernetes 将请求视为创建(如果对象尚不存在),否则视为补丁。对于在 HTTP 级别使用 PATCH 的其他请求,逻辑 Kubernetes 操作始终为补丁。
有关详细信息,请参阅 服务器端应用。
选择更新机制
HTTP PUT 以替换现有资源
更新(HTTP PUT
)操作易于实现且灵活,但存在缺点
- 你需要处理对象
resourceVersion
在客户端读取它并尝试将其写回时发生变化的冲突。Kubernetes 始终检测到冲突,但你作为客户端作者需要实现重试。 - 如果你在本地解码对象(例如,使用 client-go,你可能会接收到客户端不知道如何处理的字段 - 然后在更新过程中将其删除),则可能会意外删除字段。
- 如果对象(即使在你不尝试编辑的字段或字段集上)存在大量竞争,你可能会在发送更新时遇到问题。对于较大的对象和具有许多字段的对象,这个问题更严重。
使用 JSON 补丁的 HTTP PATCH
补丁更新很有用,因为
- 由于你只发送差异,因此
PATCH
请求中需要发送的数据更少。 - 你可以进行依赖于现有值的更改,例如将特定字段的值复制到注释中。
- 与更新(HTTP
PUT
)不同,即使无关字段频繁更改,也可以立即进行更改:通常不需要重试。- 你可能仍然需要指定
resourceVersion
(以匹配现有对象),如果你想格外小心以避免更新丢失 - 在发生错误时编写一些重试逻辑仍然是最佳实践。
- 你可能仍然需要指定
- 你可以使用测试条件来仔细构建特定的更新条件。例如,如果现有值与你的预期匹配,则可以递增计数器而无需读取它。你可以这样做,而不会有更新丢失的风险,即使对象自上次写入后在其他方面发生了更改。(如果测试条件失败,你可以回退到读取当前值,然后写回更改后的数字)。
然而
- 你需要更多本地(客户端)逻辑来构建补丁;如果你有 JSON 补丁的库实现,甚至用于专门针对 Kubernetes 创建 JSON 补丁,这将非常有帮助
- 作为客户端软件的作者,在构建补丁(HTTP 请求正文)时,你需要小心不要删除字段(操作顺序很重要)
使用服务器端应用的 HTTP PATCH
服务器端应用有一些明显的优势
- 单次往返:它很少需要首先进行
GET
请求。- 并且你仍然可以检测意外更改的冲突
- 你可以在适当的情况下选择强制覆盖冲突
- 客户端实现易于制作
- 你无需额外努力即可获得原子创建或更新操作(类似于某些 SQL 方言中的
UPSERT
)
然而
- 服务器端应用根本不适用于依赖对象当前值的字段更改
- 你只能将更新应用于对象。Kubernetes HTTP API 中的一些资源不是对象(它们没有
.metadata
字段),服务器端应用仅与 Kubernetes 对象相关。
资源版本
资源版本是标识服务器对象内部版本的字符串。客户端可以使用资源版本来确定对象何时更改,或在获取、列出和监视资源时表达数据一致性要求。客户端必须将资源版本视为不透明,并将其未修改地传递回服务器。
你不应假设资源版本是数字或可排序的。API 客户端只能比较两个资源版本以确定是否相等(这意味着你不应比较资源版本以确定它们是否大于或小于)。
元数据中的 resourceVersion
字段
客户端在资源中找到资源版本,包括来自监视响应流的资源,或在使用列表枚举资源时。
v1.meta/ObjectMeta - 资源实例的 metadata.resourceVersion
标识实例最后修改时的资源版本。
v1.meta/ListMeta - 资源集合(对列表的响应)的 metadata.resourceVersion
标识构建集合时的资源版本。
查询字符串中的 resourceVersion
参数
获取、列出和监视操作支持 resourceVersion
参数。从 v1.19 版本开始,Kubernetes API 服务器还支持列表请求上的 resourceVersionMatch
参数。
API 服务器根据你请求的操作和 resourceVersion
的值对 resourceVersion
参数进行不同的解释。如果你设置了 resourceVersionMatch
,这也将影响匹配的方式。
获取和列出的语义
对于获取和列出,resourceVersion
的语义如下
获取
resourceVersion 未设置 | resourceVersion="0" | resourceVersion="{value other than 0}" |
---|---|---|
最新 | 任何 | 不早于 |
列表
从 v1.19 版本开始,Kubernetes API 服务器支持列表请求上的 resourceVersionMatch
参数。如果你同时设置了 resourceVersion
和 resourceVersionMatch
,则 resourceVersionMatch
参数决定 API 服务器如何解释 resourceVersion
。
在列表请求上设置 resourceVersion
时,你应始终设置 resourceVersionMatch
参数。但是,要准备好处理响应的 API 服务器不知道 resourceVersionMatch
并忽略它的情况。
除非你有强一致性要求,否则使用 resourceVersionMatch=NotOlderThan
和已知的 resourceVersion
更好,因为它可以实现比不设置 resourceVersion
和 resourceVersionMatch
(需要仲裁读取才能提供服务)更好的集群性能和可扩展性。
在不设置 resourceVersion
的情况下设置 resourceVersionMatch
参数是无效的。
此表解释了具有 resourceVersion
和 resourceVersionMatch
的各种组合的列表请求的行为
resourceVersionMatch 参数 | 分页参数 | resourceVersion 未设置 | resourceVersion="0" | resourceVersion="{value other than 0}" |
---|---|---|---|---|
未设置 | limit 未设置 | 最新 | 任何 | 不早于 |
未设置 | limit=<n>,continue 未设置 | 最新 | 任何 | 精确 |
未设置 | limit=<n>,continue=<token> | 继续令牌,精确 | 无效,视为继续令牌,精确 | 无效,HTTP 400 错误请求 |
resourceVersionMatch=精确 | limit 未设置 | 无效 | 无效 | 精确 |
resourceVersionMatch=精确 | limit=<n>,continue 未设置 | 无效 | 无效 | 精确 |
resourceVersionMatch=不早于 | limit 未设置 | 无效 | 任何 | 不早于 |
resourceVersionMatch=不早于 | limit=<n>,continue 未设置 | 无效 | 任何 | 不早于 |
注意
如果你的集群 API 服务器不尊重resourceVersionMatch
参数,则行为与你没有设置它时相同。获取和列出语义的含义是
- 任何
- 返回任何资源版本的 data。首选最新的可用资源版本,但不要求强一致性;可以提供任何资源版本的 data。请求可能会返回比客户端先前观察到的要旧得多的资源版本的 data,特别是在高可用性配置中,因为存在分区或陈旧的缓存。无法容忍这种情况的客户端不应使用这种语义。
- 最新
- 返回最新资源版本的 data。返回的 data 必须一致(详细地说:通过仲裁读取从 etcd 提供服务)。对于 etcd v3.4.31+ 和 v3.5.13+ Kubernetes 1.31,从监视缓存提供“最新”读取:API 服务器内部的内存存储,它缓存并镜像持久存储到 etcd 的数据状态。Kubernetes 请求进度通知以维护与 etcd 持久层的一致性。Kubernetes 版本 v1.28 到 v1.30 也支持此功能,尽管作为 Alpha,它不推荐用于生产环境,并且在 v1.31 版本之前默认情况下不会启用。
- 不早于
- 返回不早于提供的
resourceVersion
的 data。首选最新的可用 data,但可以提供任何不早于提供的resourceVersion
的 data。对于服务器尊重resourceVersionMatch
参数的列表请求,这保证集合的.metadata.resourceVersion
不早于请求的resourceVersion
,但不对该集合中任何项目的.metadata.resourceVersion
做出任何保证。 - 精确
- 返回提供的精确资源版本的 data。如果提供的
resourceVersion
不可用,则服务器将响应 HTTP 410“Gone”。对于服务器尊重resourceVersionMatch
参数的列表请求,这保证集合的.metadata.resourceVersion
与你在查询字符串中请求的resourceVersion
相同。该保证不适用于该集合中任何项目的.metadata.resourceVersion
。 - 继续令牌,精确
- 返回初始分页列表调用的资源版本。返回的继续令牌负责跟踪初始分页列表调用之后所有分页列表调用的初始提供的资源版本。
注意
当你列出资源并接收集合响应时,响应将包含集合的 列表元数据 以及该集合中每个项目的 对象元数据。对于在集合响应中找到的单个对象,.metadata.resourceVersion
跟踪该对象上次更新的时间,而不是跟踪该对象在提供服务时的更新程度。在使用 resourceVersionMatch=NotOlderThan
并且设置了 limit 时,客户端必须处理 HTTP 410“Gone”响应。例如,客户端可能会使用较新的 resourceVersion
重试,或者回退到 resourceVersion=""
。
当使用resourceVersionMatch=Exact
且limit
未设置时,客户端必须验证集合的.metadata.resourceVersion
是否与请求的resourceVersion
匹配,并处理不匹配的情况。例如,客户端可能回退到设置了limit
的请求。
**watch**语义
对于**watch**,资源版本的语义是
watch
resourceVersion 未设置 | resourceVersion="0" | resourceVersion="{value other than 0}" |
---|---|---|
获取状态并从最新版本开始 | 获取状态并从任意版本开始 | 从精确版本开始 |
这些**watch**语义的含义是
- 获取状态并从任意版本开始
- 从任何资源版本开始 **watch**;首选最 新的可用资源版本,但不是必需的。允许任何起始资源版本。由于分区或陈旧缓存,**watch** 可能会从客户端先前观察到的旧 的资源版本开始,特别是在高可用性配置中。无法容忍这种明显回溯的客户端不应使用此语义启动 **watch**。为了建立初始状态,**watch** 从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续 watch 事件都是针对起始资源版本之后发生的更改。
注意
以这种方式初始化的 watch 可能会返回任意过时的 数据。请在使用之前审查此语义,并尽可能优先使用其他语义。 - 获取状态并从最新版本开始
- 从最新的资源版本开始 **watch**,该版本必须一致(详细 地说:通过 etcd 的 quorum 读取提供服务)。为了建立初始状态,**watch** 从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续 watch 事件都是针对起始资源版本之后发生的更改。
- 从精确版本开始
- 从精确的资源版本开始 **watch**。 watch 事件针对提供的资源版本之后的所有更改。与“获取状态并从最新版本开始”和“获取状态并从任意版本开始”不同,**watch** 不会从提供的资源版本的合成“Added”事件开始。假设客户端已经拥有起始资源版本的初始状态,因为客户端提供了资源版本。
“410 Gone” 响应
服务器不需要提供所有旧的资源版本,如果客户端请求的 resourceVersion
早于服务器保留的版本,则可能会返回 HTTP 410 (Gone)
状态代码。客户端必须能够容忍 410 (Gone)
响应。有关在监视资源时如何处理 410 (Gone)
响应的详细信息,请参阅有效检测更改。
如果您请求的 resourceVersion
超出了适用限制,则 API 服务器可能会根据请求是从缓存中提供还是否,回复 410 Gone
HTTP 响应。
不可用的资源版本
服务器不需要提供无法识别的资源版本。如果您请求资源版本的 **list** 或 **get**,而 API 服务器无法识别该资源版本,则 API 服务器可能会
- 等待短暂时间使资源版本可用,然后在提供 的资源版本在合理的时间内没有变为可用时,超时并返回
504 (Gateway Timeout)
; - 以
Retry-After
响应头响应,指示客户端应等待多 少秒后重试请求。
如果您请求 API 服务器无法识别的资源版本,kube-apiserver 还会在 其错误响应中 添加“资源版本太大”的消息。
如果您对无法识别的资源版本发出 **watch** 请求,API 服务器可能会无限期地等待(直到请求超时)使资源版本可用。