Kubernetes API 概念
Kubernetes API 是一个基于资源的(RESTful)程序化接口,通过 HTTP 提供。它支持通过标准 HTTP 动词(POST、PUT、PATCH、DELETE、GET)检索、创建、更新和删除主要资源。
对于某些资源,API 包括额外的子资源,这些子资源允许进行细粒度的授权(例如,对 Pod 详细信息和日志检索的单独视图),并且可以接受和提供这些资源的不同表示形式,以方便或提高效率。
Kubernetes 通过监视支持对资源的有效更改通知。Kubernetes 还提供一致的列表操作,以便 API 客户端可以有效地缓存、跟踪和同步资源的状态。
您可以查看API 参考在线,或继续阅读以了解有关 API 的一般信息。
Kubernetes API 术语
Kubernetes 通常利用常见的 RESTful 术语来描述 API 概念
- 资源类型是在 URL 中使用的名称(
pods
、namespaces
、services
) - 所有资源类型都有一个具体的表示形式(它们的对象模式),称为类型
- 资源类型实例列表称为集合
- 资源类型的单个实例称为资源,通常也表示对象
- 对于某些资源类型,API 包含一个或多个子资源,这些子资源表示为资源之下的 URI 路径
大多数 Kubernetes API 资源类型是对象 - 它们代表集群上概念的具体实例,例如 Pod 或命名空间。少数 API 资源类型是虚拟的,因为它们通常代表对对象的操作,而不是对象本身,例如权限检查(使用包含 SubjectAccessReview
的 JSON 编码主体对 subjectaccessreviews
资源进行 POST 请求),或 Pod 的 eviction
子资源(用于触发API 启动的驱逐)。
对象名称
您可以通过 API 创建的所有对象都有一个唯一的对象名称,以允许幂等创建和检索,除了虚拟资源类型可能没有唯一的名称,如果它们不可检索或不依赖于幂等性。在命名空间内,一次只能有一个给定类型的对象具有给定名称。但是,如果您删除了该对象,您可以使用相同的名称创建一个新对象。某些对象不是命名空间的(例如:节点),因此它们的名称在整个集群中必须是唯一的。
API 动词
几乎所有对象资源类型都支持标准 HTTP 动词 - GET、POST、PUT、PATCH 和 DELETE。Kubernetes 还使用它自己的动词,这些动词通常用小写字母编写,以区别于 HTTP 动词。
Kubernetes 使用术语列表来描述返回集合的资源,以区别于检索通常称为获取的单个资源。如果您发送了带有 ?watch
查询参数的 HTTP GET 请求,Kubernetes 将其称为监视而不是获取(有关更多详细信息,请参见有效检测更改)。
对于 PUT 请求,Kubernetes 在内部将它们分类为创建或更新,具体取决于现有对象的状态。更新不同于修补;修补的 HTTP 动词是 PATCH。
资源 URI
所有资源类型要么由集群(/apis/GROUP/VERSION/*
)限定范围,要么由命名空间(/apis/GROUP/VERSION/namespaces/NAMESPACE/*
)限定范围。命名空间范围的资源类型将在其命名空间被删除时被删除,并且对该资源类型的访问由对命名空间范围的授权检查控制。
注意:核心资源使用 /api
而不是 /apis
,并省略 GROUP 路径段。
示例
/api/v1/namespaces
/api/v1/pods
/api/v1/namespaces/my-namespace/pods
/apis/apps/v1/deployments
/apis/apps/v1/namespaces/my-namespace/deployments
/apis/apps/v1/namespaces/my-namespace/deployments/my-deployment
您还可以访问资源集合(例如:列出所有节点)。以下路径用于检索集合和资源
集群范围的资源
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回资源类型资源的集合GET /apis/GROUP/VERSION/RESOURCETYPE/NAME
- 返回资源类型下具有 NAME 的资源
命名空间范围的资源
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回所有命名空间中资源类型的所有实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE
- 返回 NAMESPACE 中资源类型的所有实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME
- 返回 NAMESPACE 中具有 NAME 的资源类型的实例
由于命名空间是集群范围的资源类型,因此您可以使用 GET /api/v1/namespaces
检索所有命名空间的列表(“集合”),并使用 GET /api/v1/namespaces/NAME
检索有关特定命名空间的详细信息。
- 集群范围的子资源:
GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
- 命名空间范围的子资源:
GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE
每个子资源支持的动词将根据对象而有所不同 - 有关更多信息,请参见API 参考。无法跨多个资源访问子资源 - 通常,如果需要,将使用新的虚拟资源类型。
有效检测更改
Kubernetes API 允许客户端对对象或集合发出初始请求,然后跟踪自该初始请求以来的更改:监视。客户端可以发送列表或获取,然后发出后续监视请求。
为了使这种更改跟踪成为可能,每个 Kubernetes 对象都有一个 resourceVersion
字段,表示该资源在底层持久层中的版本。当检索资源集合(命名空间或集群范围)时,来自 API 服务器的响应包含一个 resourceVersion
值。客户端可以使用该 resourceVersion
对 API 服务器启动监视。
当您发送监视请求时,API 服务器将以更改流的形式响应。这些更改详细说明了在您作为监视请求参数指定的 resourceVersion
之后发生的操作(例如创建、删除和更新)的结果。整体监视机制允许客户端获取当前状态,然后订阅后续更改,而不会错过任何事件。
如果客户端监视断开连接,则该客户端可以从最后返回的 resourceVersion
开始新的监视;客户端也可以执行新的获取/列表请求并重新开始。有关更多详细信息,请参见资源版本语义。
例如
列出给定命名空间中的所有 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 操作(例如创建、删除、修补或更新)的通知。每个更改通知都是一个 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 分钟的更改。当请求的监视操作失败,因为该资源的历史版本不可用时,客户端必须通过识别状态代码 410 Gone
、清除其本地缓存、执行新的获取或列表操作并从返回的 resourceVersion
开始监视来处理这种情况。
对于订阅集合,Kubernetes 客户端库通常提供某种形式的标准工具来实现这种列表然后监视的逻辑。(在 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"} }
}
作为客户端,您可以通过将 allowWatchBookmarks=true
查询参数设置为监视请求来请求 BOOKMARK
事件,但您不应该假设书签会在任何特定时间间隔返回,也不应该假设 API 服务器即使在请求时也会发送任何 BOOKMARK
事件。
流式列表
Kubernetes v1.27 [alpha]
在大型集群中,检索某些资源类型的集合可能会导致控制平面上的资源使用量(主要是 RAM)显着增加。为了减轻其影响并简化列表 + 监视模式的用户体验,Kubernetes v1.27 引入了作为 alpha 功能对请求初始状态(以前通过列表请求请求)的支持作为监视请求的一部分。
如果启用了WatchList
特性开关,可以通过在watch请求中指定sendInitialEvents=true
作为查询字符串参数来实现。如果设置了该参数,API服务器将使用合成初始化事件(类型为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-20MB)并消耗大量服务器资源。
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将不会显示,除非您进行单独的list请求,而没有continue
令牌。这使您可以将大型请求分解成较小的块,然后对整个集合执行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资源类型都支持表格响应;例如,CustomResourceDefinitions可能没有定义字段到表格的映射,而扩展核心Kubernetes API的APIService可能根本不提供表格响应。如果您正在实现一个使用表格信息的客户端,并且必须针对所有资源类型(包括扩展)工作,那么您应该发出在Accept
头中指定多个内容类型的请求。例如
Accept: application/json;as=Table;g=meta.k8s.io;v=v1, application/json
资源的替代表示
默认情况下,Kubernetes以JSON格式返回序列化对象,内容类型为application/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;具体来说,对于定义为 CustomResourceDefinitions 或通过 聚合层 提供服务的资源,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
响应的客户端,如果响应不匹配预期的前缀,则应拒绝该响应,因为未来的版本可能需要以不兼容的方式更改序列化格式,并将通过更改前缀来实现。资源删除
当您 **删除** 资源时,这将分两个阶段进行。
- 最终化
- 移除
{
"kind": "ConfigMap",
"apiVersion": "v1",
"metadata": {
"finalizers": ["url.io/neat-finalization", "other-url.io/my-finalizer"],
"deletionTimestamp": nil,
}
}
当客户端首次发送 **删除** 请求以移除资源时,.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 模式中。(对此的一个例外是 CRDs,它们明确选择不通过
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 也被修改为理解 AdmissionReview 中的 DryRun
字段,并且在标记为干运行的任何请求上防止副作用,这种更改就是合适的。以下是一个使用 ?dryRun=All
的干运行请求示例
POST /api/v1/namespaces/test/pods?dryRun=All
Content-Type: application/json
Accept: application/json
响应将与非干运行请求的响应相同,但某些生成字段的值可能会有所不同。
生成的值
对象的某些值通常在对象持久化之前生成。重要的是不要依赖干运行请求设置的这些字段的值,因为这些值在干运行模式下与实际请求时可能不同。其中一些字段是
name
:如果设置了generateName
,则name
将具有唯一的随机名称creationTimestamp
/deletionTimestamp
:记录创建/删除时间UID
:唯一标识 对象,并且是随机生成的(非确定性)resourceVersion
:跟踪对象的持久化版本- 任何由变异准入控制器设置的字段
- 对于
Service
资源:kube-apiserver 分配给 Service 对象的端口或 IP 地址
干运行授权
干运行和非干运行请求的授权相同。因此,要发出干运行请求,您必须有权发出非干运行请求。
例如,要对 Deployment 运行干运行 **patch**,您必须有权执行该 **patch**。以下是一个 Kubernetes RBAC 规则的示例,该规则允许修补 Deployment
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["patch"]
请参阅 授权概述。
对现有资源的更新
Kubernetes 提供了几种更新现有对象的方法。您可以阅读 选择更新机制 以了解哪种方法最适合您的用例。
您可以使用 HTTP PUT 覆盖(**更新**)现有资源,例如 ConfigMap。对于 PUT 请求,客户端负责指定 resourceVersion
(从要更新的对象中获取)。Kubernetes 使用该 resourceVersion
信息,以便 API 服务器可以检测到丢失的更新并拒绝由与集群不同步的客户端发出的请求。如果资源已更改(客户端提供的 resourceVersion
已过期),API 服务器将返回 409 Conflict
错误响应。
客户端可以发送指令到 API 服务器以**修补**现有资源,而不是发送 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 Patch 的 HTTP PATCH
**修补**更新很有用,因为
- 由于您只发送差异,因此您在
PATCH
请求中发送的数据更少。 - 您可以进行依赖于现有值的更改,例如将特定字段的值复制到注释中。
- 与**更新**(HTTP
PUT
)不同,即使对无关字段进行频繁更改,您的更改也可以立即发生:您通常不需要重试。- 如果您想格外小心以避免丢失更新,您可能仍然需要指定
resourceVersion
(以匹配现有对象)。 - 在发生错误的情况下编写一些重试逻辑仍然是最佳实践。
- 如果您想格外小心以避免丢失更新,您可能仍然需要指定
- 您可以使用测试条件来仔细构建特定的更新条件。例如,如果您期望现有值匹配,则可以在不读取的情况下递增计数器。即使对象自您上次写入它以来以其他方式更改,您也可以在没有丢失更新风险的情况下执行此操作。(如果测试条件失败,您可以回退到读取当前值,然后写回更改后的数字)。
但是
- 您需要更多本地(客户端)逻辑来构建修补程序;如果您有 JSON Patch 的库实现,甚至专门针对 Kubernetes 创建 JSON Patch,这将非常有帮助。
- 作为客户端软件的作者,您在构建修补程序(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 Bad Request |
| limit 未设置 | 无效 | 无效 | 精确 |
| limit=<n> ,continue 未设置 | 无效 | 无效 | 精确 |
| limit 未设置 | 无效 | 任何 | 不早于 |
| limit=<n> ,continue 未设置 | 无效 | 任何 | 不早于 |
注意
如果您的集群的 API 服务器不遵守resourceVersionMatch
参数,则行为与您未设置它时相同。**获取**和**列出**语义的含义是
- 任何
- 返回任何资源版本的 data。首选最新的可用资源版本,但不需要强一致性;可以提供任何资源版本的 data。由于分区或陈旧的缓存,请求可能会返回客户端之前观察到的旧得多的资源版本的 data,尤其是在高可用性配置中。无法容忍这种情况的客户端不应使用这种语义。
- 最新
- 返回最新资源版本的 data。返回的 data 必须一致(详细说明:通过仲裁读取从 etcd 提供)。
- 不早于
- 返回至少与提供的
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
且设置了限制时,客户端必须处理 HTTP 410 “Gone” 响应。例如,客户端可能会使用较新的resourceVersion
重试,或回退到resourceVersion=""
。
当使用resourceVersionMatch=Exact
且未设置limit
时,客户端必须验证集合的.metadata.resourceVersion
是否与请求的resourceVersion
匹配,并处理不匹配的情况。例如,客户端可能会回退到设置了limit
的请求。
观察的语义
对于观察,资源版本的语义是
观察
resourceVersion 未设置 | resourceVersion="0" | resourceVersion="{value other than 0}" |
---|---|---|
获取状态并从最新开始 | 获取状态并从任意开始 | 从精确开始 |
这些观察语义的含义是
- 获取状态并从任意开始
- 从任何资源版本开始观察;首选最可用的最新资源版本,但不是必需的。允许任何起始资源版本。由于分区或陈旧缓存,观察可能会从客户端之前观察到的旧得多的资源版本开始,尤其是在高可用性配置中。无法容忍这种明显回退的客户端不应使用此语义启动观察。为了建立初始状态,观察从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续观察事件都是针对起始资源版本之后发生的更改。
注意
以这种方式初始化的观察可能会返回任意陈旧的数据。请在使用此语义之前查看此语义,并尽可能使用其他语义。 - 获取状态并从最新开始
- 从最新资源版本开始观察,该版本必须是一致的(详细说明:通过一致性读取从 etcd 提供服务)。为了建立初始状态,观察从所有在起始资源版本存在的资源实例的合成“Added”事件开始。所有后续观察事件都是针对起始资源版本之后发生的更改。
- 从精确开始
- 从精确的资源版本开始观察。观察事件是针对提供的资源版本之后的所有更改。与“获取状态并从最新开始”和“获取状态并从任意开始”不同,观察不会从提供的资源版本的合成“Added”事件开始。假设客户端已经拥有起始资源版本的初始状态,因为客户端提供了资源版本。
“410 Gone” 响应
服务器不需要提供所有较旧的资源版本,如果客户端请求的resourceVersion
比服务器保留的版本旧,则可能会返回 HTTP 410 (Gone)
状态代码。客户端必须能够容忍410 (Gone)
响应。有关如何在观察资源时处理410 (Gone)
响应的详细信息,请参阅有效检测更改。
如果您请求的resourceVersion
超出适用限制,那么 API 服务器可能会根据请求是从缓存提供还是不从缓存提供,以410 Gone
HTTP 响应进行回复。
不可用的资源版本
服务器不需要提供无法识别的资源版本。如果您请求列出或获取 API 服务器无法识别的资源版本,那么 API 服务器可能会
- 短暂等待资源版本变为可用,然后在提供的资源版本在合理的时间内没有变为可用时,以
504 (Gateway Timeout)
超时; - 以
Retry-After
响应头进行回复,指示客户端应等待多少秒才能重试请求。
如果您请求 API 服务器无法识别的资源版本,kube-apiserver 还会使用“资源版本过大”消息标识其错误响应。
如果您对无法识别的资源版本发出观察请求,API 服务器可能会无限期等待(直到请求超时)资源版本变为可用。