From d611062a0ba49ff33003b4f6da67f6ab3b198b23 Mon Sep 17 00:00:00 2001 From: mouuii <49775493+mouuii@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:31:02 +0800 Subject: [PATCH] sync workflow --- .../5.2-api-conventions.md | 107 +++++++++++------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.2-api-conventions.md b/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.2-api-conventions.md index 84c2d29..fe8ebbb 100644 --- a/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.2-api-conventions.md +++ b/k8s-yuan-ma-ren-cai-fu-hua-xun-lian-ying/di-wu-zhang-apimachinery/5.2-api-conventions.md @@ -1,6 +1,6 @@ # 5.2 api-conventions -k8s API 规范目的是简化客户端开发,对齐颗粒度,每个想要参与 k8s 开源贡献的工程师都应该有几本了解。 +k8s API 规范目的是简化客户端开发,对齐颗粒度,每个想要参与 k8s 开源贡献的工程师都应该有基本了解。 k8s API 是 RESTful 风格 – 通过 HTTP 动词(POST,PUT,DELETE和GET)实现对象的创建,更新,删除或查询 。k8s 也为非标准的动作暴露了额外的接口,允许额外的内容类型(译注:如log,exec)。所有接收和返回的JSON都有一个 schema ,由 "apiVersion" 和 "kind" 这两个字段标识。 @@ -94,29 +94,28 @@ _规范_(spec)是对一个对象目标状态的完整描述,包括用户 _状态_(status)总结描述对象的当前状态,并且通常通过自动化过程与对象一起持久化,但可能会即时(on the fly)生成。作为一般准则,状态字段应该是对实际状态的最新观察,但它们可能包含诸如分配结果或响应对象规范执行的类似操作之类的信息。 请参阅下面的更多细节。 -具有规范和状态的类型可以(并且通常应该)具有不同的授权范围。允许用户被授予对规范的完全写入访问权限和对状态的只读访问权限,而相关控制器被授予对规范的只读访问权限但对状态的完全写入访问权限。 +具有 spec 和 status 的类型可以(并且通常应该)具有不同的授权范围。允许用户被授予对规范的完全写入访问权限和对状态的只读访问权限,而相关控制器被授予对规范的只读访问权限但对状态的完全写入访问权限。 -当对象的新版本被创建(POST)或更新(PUT)时,规范会更新并立即可用。随着时间的推移,系统将努力使状态符合规范。无论先前版本如何,系统都将朝着最新的规范发展。例如,如果一次更新将值从2更改为5,然后在另一个更新中又回到3,则系统不需要在将状态更改为3之前先到达5。换句话说,系统的行为是基于水平的,而不是基于边缘的。这可以在缺少中间状态更改的情况下实现稳健的行为。 +当对象的新版本被创建(POST)或更新(PUT)时,spec 会被更新并立即可用。随着时间的推移,系统将努力使 status 符合 spec 。无论先前版本如何,系统都将朝着最新的 spec 发展。例如,如果一次更新将值从2更改为5,然后在另一个更新中又回到3,则系统不需要在将状态更改为3之前先到达5。换句话说,系统的行为是基于水平的,而不是基于边缘的。这可以在缺少中间状态更改的情况下实现稳健的行为。 Kubernetes API还作为系统声明性配置模式的基础。为了促进声明性配置的基于级别的操作和表达,规范中的字段应该具有声明性而不是命令性的名称和语义——它们代表所需的状态,而不是旨在产生所需状态的操作。 对象上的创建(POST)和更新(PUT)必须忽略状态(status)值,以避免在读-修改-写场景中意外覆盖状态。必须提供`/status`子资源以使系统组件能够更新它们管理的资源的状态。 -此外,更新(PUT)期望是指定整个对象。因此,如果省略了某个字段,则假定客户端想要清楚这个字段的值。PUT不支持部分更新。要想修改资源对象的部分内容,可以先获取(GET)资源,修改资源的`spec`,`lalels`,`annotations`,然后再PUT给服务端。参加下面的并发控制了解读-修改-写模式的一致性。某些对象可能会公开额外资源,以允许更改状态或对对象执行自定义操作。 +此外,更新(PUT)期望是指定整个对象。因此,如果省略了某个字段,则假定客户端想要清除这个字段的值。PUT不支持部分更新。要想修改资源对象的部分内容,可以先获取(GET)资源,修改资源的`spec`,`lalels`,`annotations`,然后再PUT给服务端。参考下面的并发控制了解读-修改-写模式的一致性。某些对象可能会公开额外资源,以允许更改状态或对对象执行自定义操作。 -所有代表物理资源的对象,其状态可能与用户期望的不同,都应该有一个规范(spec)和一个状态(status)。 状态不会与用户期望的不同的对象,可以只有规范(spec),并且可以将“spec”修改为更合适的名称(译注:比如configmap)。 -同时包含_规范_和_状态_的对象不应包含标准元数据字段以外的其他顶级字段。 +同时包含_spec_和_status_的对象不应包含标准元数据字段以外的其他顶级字段。 -一些不会在在系统中持久化的对象 – 例如`SubjectAccessReview`和其他 webhook风格调用 – 可能会选择添加_规范_和_状态_来封装“调用和响应”模式。规范是请求(通常是信息请求),状态是响应。对于这些类似RPC的对象,唯一的操作可能是POST,但是在提交和响应之间具有一致的模式可以降低这些客户端的复杂性。 +一些不会在在系统中持久化的对象 – 例如`SubjectAccessReview`和其他 webhook风格调用 – 可能会选择添加_spec_和_status_来封装“调用和响应”模式。规范是请求(通常是信息请求),状态是响应。对于这些类似RPC的对象,唯一的操作可能是POST,但是在提交和响应之间具有一致的模式可以降低这些客户端的复杂性。 **Typical status properties** -**条件(Conditions)** 为控制器的高层级的状态报告提供了一个标准机制。它们是一种扩展机制,允许工具和其他控制器收集有关资源的摘要信息,而无需了解特定于资源的状态详细信息。控制器要将挂测到的对象的详尽状态补充写入到_条件_中,而不是替换它。例如,部署的“可用”条件可以通过检查部署的就绪副本(readyReplicas)、副本(replicas)和其他属性来确定。“可用”条件避免其他组件重复编写判断Deployment可用性的逻辑。 +**条件(Conditions)** 为控制器的高层级的状态报告提供了一个标准机制。它们是一种扩展机制,允许工具和其他控制器收集有关资源的摘要信息,而无需了解特定于资源的状态详细信息。控制器要将挂测到的对象的详尽状态补充写入到_conditions_中,而不是替换它。例如,deployment 的 "Available" 条件可以通过检查部署的就绪副本(readyReplicas)、副本(replicas)和其他属性来确定。"Available" 条件避免其他组件重复编写判断Deployment可用性的逻辑。 -资源对象的条件报告可以包含多个条件,在未来也可以添加新的条件,也可以由其他第三方控制器添加新条件。因此,条件时使用列表来表示,每个条件有一个类似的结构。该列表应实际上应当认为是一个map,以`type`为字典的key。 +资源对象的 condition 报告可以包含多个,在未来也可以添加新的 conditions ,也可以由其他第三方控制器添加新 conditions 。每个单独的 condition 是一个以type为字典的key。 -当条件遵循一致性的约定时,他们是最有用的: +当条件遵循一致性的约定时,他们能发挥最大的价值: * 应添加条件以明确传达用户和组件关心的属性,而不是要求从其他观察中推断出这些属性。一旦定义,条件的含义就不能随意更改 – 它成为API的一部分,并且与API的任何其他部分具有相同的向后和向前兼容性考虑。 * 控制器应在第一次看到资源对象时将其条件应用于资源对象,即使状态(stauts)为未知(Unknown)。这允许系统中的其他组件知道条件存在,并且控制器正在调和(reconcile)该资源方面取得进展(译注:让别人知道我正在处理)。 @@ -126,8 +125,21 @@ Kubernetes API还作为系统声明性配置模式的基础。为了促进声明 * 条件类型名称应该描述资源的当前观察状态,而不是描述当前状态转换。这通常意味着名称应该是形容词(“Ready”、“OutOfDisk”)或过去时动词(“Succeeded”、“Failed”)而不是现在时动词(“Deploying”)。可以通过将条件的状态(status)设置为未知(Unknown)来指示中间状态。 * 对于需要很长时间(例如超过1分钟)的状态转换,将转换本身视为观察到的状态是合理的。在这些情况下,条件(例如“Resizing”)本身不应是瞬态的,而应使用True/False/Unknown模式发出信号。这允许其他观察者确定来自控制器的最后一次更新,是成功还是失败。在状态转换无法完成且继续协调不可行的情况下,应使用原因和消息来指示转换失败。 * 在为资源设计条件时,有一个通用的顶级条件来概括更详细的条件会很有用。简单的消费者可以简单地查询顶级条件。尽管它们不是一致的标准,但API设计人员可以将`Ready`和`Succeeded`条件类型分别用于长时间运行和有限执行的对象。 -* 对于资源`Foo`,定义了`FooCondition`代表该资源的状态,可以包含下列字段,其中`type`和`status`两个字段是必须有的,其他字段可以没有: + + +Conditions should follow the standard schema included in k8s.io/apimachinery/pkg/apis/meta/v1/types.go. It should be included as a top level element in status, similar to +```go +// +listType=map +// +listMapKey=type +// +patchStrategy=merge +// +patchMergeKey=type +// +optional +Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + +``` + +对于资源`Foo`,定义了`FooCondition`代表该资源的状态,可以包含下列字段,其中`type`和`status`两个字段是必须有的,其他字段可以没有: ```go Type FooConditionType `json:"type" description:"type of Foo condition"` Status ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` @@ -147,7 +159,7 @@ Kubernetes API还作为系统声明性配置模式的基础。为了促进声明 鼓励使用`Reason`字段。 -条件的类型(type)应该以驼峰形式命名,优先使用剪短的名称(如Ready,而不是MyResourceReady)。 +条件的类型(type)应该以驼峰形式命名,优先使用简短的名称(如Ready,而不是MyResourceReady)。 条件的状态(status)值可以是`True`,`False`或`Unknown`。如果条件确实,应该认为其状态为Unknown。控制器如何处理Unknown取决于所讨论的条件。 @@ -198,8 +210,8 @@ ports: * 尽量避免使用浮点值,绝不要在`spec`中使用浮点值。浮点值在传输过程中会被编码和重接解码,可能会发生变化,因此是不可靠的,并且在不同的语言和体系结构中具有不同的精度和表示。 * 所有数字(例如,uint32、int64)会被Javascript和其他一些语言转换为float64,因此字段值的取值范围或精度上超过该值(译注:指float64)的字段(特别是整数值 > 53 位)都应该被序列化并作为字符串使用。 * 不要使用无符号整数,因为跨语言和库的支持不一致。 -* 不要使用枚举值,而是使用string别名(如`NodeConditionType`)。 * 查看API中的类似字段(例如,端口、持续时间)并遵循现有字段的约定(译注:尽量参考现有API)。 +* 不要使用枚举值,而是使用string别名(如`NodeConditionType`)。 * 所有公共整数型字段必须使用Go的`(u)int32`或`(u)int64`类型,而不是`(u)int`(取决于目标平台,这是不明确的)。内部类型可以使用`(u)int`。 * 对于使用布尔类型字段,需要多加考虑。许多想法以布尔值开始,但最终趋向于一小部分互斥选项。通过将策略选项明确描述为字符串类型别名(例如`TerminationMessagePolicy`)来规划未来的扩展。 @@ -209,7 +221,7 @@ ports: Kubernetes中的所有API都必须利用这种风格的常量,包括标志和配置文件。在以前使用不一致常量的情况下,新标志应该只是驼峰形式,并且随着时间的推移,旧标志应该被更新以使用驼峰形式值以及不一致的常量。示例:Kubelet的–topology-manager-policy标志,其值为 none、best-effort、restricted和single-numa-node。这个标志应该接受 None、BestEffort、Restricted和SingleNUMANode。如果向该标志添加新值,则应支持两种形式。 -\ + **Unions** 有时,一组字段中最多可以设置其中一个字段的值。例如,PodSpec的\[volumes]字段有17个不同的卷类型特定字段,例如nfs和iscsi。集合中的所有字段都应该是可选的。 @@ -222,7 +234,7 @@ Kubernetes中的所有API都必须利用这种风格的常量,包括标志和 resourceVersion:一个字符串,标识列表中返回的对象的通用版本。这个值必须被客户端视为不透明的(对于客户端没意义),并且不加修改地传回服务器。资源版本仅在单个命名空间下的单个资源类型有效。服务器返回的每个简单类型,以及发送到服务器的要支持幂等性或乐观并发的简单类型要应该返回这个值。由于简单资源经常被用作修改对象的输入替代动作,所以简单资源的资源版本应该与对象的资源版本相对应。(译注:修改版本为1的资源,返回的版本也是1,以及修改的数据,如果有别人同时进行修改,服务端的版本号增加了,本次修改失败) -\ + ### Differing Representations @@ -240,8 +252,30 @@ API资源应该使用传统的REST模式: * `PATCH //` – 选择性修改资源的指定字段。参考下面更详细的内容。 * `GET /?watch=true` – 监视JSON对象的流,相当于是订阅了资源对象随着时间的变更。 -#### PATCH operations +### 简称和类别 +资源实现者可以选择在为资源类型发布的发现信息中包含“短名称”和类别,客户端可以在解决不明确的用户调用时将其用作提示。 + +对于编译入的资源,这些由 REST 处理程序ShortNames() []string和Categories() []string实现控制。 + +对于自定义资源,这些资源由 CustomResourceDefinition 中的.spec.names.shortNames和.spec.names.categories字段控制。 +### 简称 + +注意:由于短名称冲突(相互冲突或与资源类型冲突)时会发生不可预测的行为,因此请勿向内置资源添加新的短名称,除非 API 审阅者特别允许。请参阅问题#117742和#108573 。 + +客户端可以使用发现中列出的“短名称”作为提示来解决对单个资源的不明确的用户调用。 + +内置短名称的示例包括: + +ds -> apps/v* daemonsets +sts -> apps/v* statefulsets +hpa -> autoscaling/v* horizontalpodautoscalers +hpa -> autoscaling/v* horizontalpodautoscalers + +例如,仅提供内置 API 类型, kubectl get sts相当于 kubectl get statefulsets.v1.apps 。 + + +### PATCH operations kubernetes api支持不同的patch模型,具体使用的模型根据请求的`Content-Type`决定: * JSON Patch:`Content-Type: application/json-patch+json` @@ -257,7 +291,7 @@ kubernetes api支持不同的patch模型,具体使用的模型根据请求的` 可以通过`metadata.generateName`请求系统生成名称(names)。GenerateName表示这个名称应该在持久化之前要使之独唯一。该字段如果非空的话则表示想要一个唯一的名称(返回给客户端的名称会和之前发送给服务端的不同)。如果资源的名称字段未指定,则名称将会以该字段和一个唯一的后缀组成。该字段的值必须符合名称规则。如果指定了该字段,并且没有指定名称的情况下,如果生成的名称在系统中已经存在,将不会返回409,而是返回201 Created,或者504 ServerTimeout(表明在分配的时间内找不到一个唯一的名称),在发生504的情况下客户端应该进行重试(可选的使用Retry-After的头中说明的时间)。 -Optional vs. Required +### Optional vs. Required 字段要么是可选的,要么是必须的。 @@ -269,9 +303,9 @@ Optional vs. Required 在大多数场景下,可选字段同时应该有`omitempy`结构体标签(struct tag)(`emitempty`说明如果该字段拥有一个空值,在进行json编码的时候应该省略)。然而,如果你对可选字段想要区分处理未提供值和提供空值的情况,则不能使用`emitempty`(如\[kubernetes/kubernetes#34641(https://github.com/kubernetes/kubernetes/issues/34641)])。 -注意,考虑到向后兼容性,任何有`emitempty` struct tag的字段会被认为是可选的,但是未来可能会改变这种行为,非常推荐使用`+optionnal`。 +注意,考虑到向后兼容性,任何有`omitempty` struct tag的字段会被认为是可选的,但是未来可能会改变这种行为,非常推荐使用`+optionnal`。 -必须字段拥有相反的属性,即: +required 字段拥有相反的属性,即: * 字段注释中没有`+optional` * 没有`omitempty`结构体标签 @@ -288,38 +322,25 @@ Optional vs. Required 因此,对于没有内置`nil`值的类型,我们要求对于可选字段总是使用指针。 -\ -Defaulting - -默认值是特定于API版本的,并且他们会在特定版本的API的资源定义转换到代表期望状态(`Spec`)的内部对象时进行应用。后续在获取(GETs)资源时将会明确的包含这些默认值。 - -将默认值合并到`Spec`可确保`Spec`描述完整的期望状态,以便系统更容易确定如何实现该状态,并让用户知道预期什么。 - -对于字段的默认值,可以通过`+default=`标签指定。原始类型字段会在反序列化JSON对象的过程中对其赋值。如果字段没有指定`omitempty` json标签,并且没有指定默认值时,字段将被赋予该类型的默认值。 - -参考[该文档](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#defaulting)了解更多信息。 - -API版本特定的默认值由API server设置。 -Late Initialization +### Defaulting -延迟初始化是指资源在创建或更新后,有系统的控制器对其字段进行赋值。 +一般来说,我们希望在 API 中明确表示默认值,而不是断言“未指定的字段获得默认行为”。这很重要,以便: +- 默认值可能会在较新的 API 版本中演变和更改 +- 存储的配置描述了完整的期望状态,使系统更容易确定如何实现该状态,并让用户知道会发生什么 -例如,调度器会pod创建之后设置`pod.spec.nodeName`字段。 +创建或更新(包括修补和应用)资源时,可以通过 3 种不同的方式应用默认值: -延迟初始化只能够做下列类型的修改: -* 为未设置值的字段赋值 -* 向字典中添加键(译注:如label,annotation) -* 向具有合并语义的数组中添加值(类型定义中,具有`patchStrategy:"merge"`属性)(译注:参考vendor/k8s.io/api/core/v1/types.go中Env) +- 静态:根据请求的 API 版本以及资源中可能的其他字段,可以在 API 调用期间为字段分配值 +- 准入控制:根据配置的准入控制器以及集群内外可能的其他状态,可以在 API 调用期间为字段分配值 +- 控制器:API 调用完成后可以对资源进行任意更改(在允许的范围内) -约定: +在决定使用哪种机制和管理语义时需要小心。 -* 允许用户(具有足够权限)通过设置原本会被默认的字段来覆盖任何系统默认行为 -* 使来自用户的更新能够与后期初始化期间所做的更改通过strategic merge patch合并,而不是破坏更改 -* 允许进行后期初始化的组件使用strategic merge patch,有助于此类组件的组合和并发 +### Static Defaults 静态默认值 -尽管apiserver准入控制阶段在对象创建之前起作用,但准入控制插件也应该遵循后期初始化约定,以允许稍后将它们的实现移动到“控制器”或客户端库。 +静态默认值特定于每个 API 版本。使用“v1”API 创建对象时应用的默认字段值可能与使用“v2”API 时应用的值不同。在大多数情况下,这些值由 API 版本定义为文字值(例如“如果未指定此字段,则默认为 0”)。 ### Concurrency Control and Consistency