自定义资源
自定义资源是 Kubernetes API 的扩展。本页面讨论何时将自定义资源添加到 Kubernetes 集群以及何时使用独立服务。它介绍了添加自定义资源的两种方法以及如何选择它们。
自定义资源
资源是 Kubernetes API 中的一个端点,用于存储特定类型的API 对象集合;例如,内置的 pods 资源包含 Pod 对象的集合。
自定义资源是 Kubernetes API 的扩展,不一定在默认的 Kubernetes 安装中可用。它代表了特定 Kubernetes 安装的定制。然而,许多核心 Kubernetes 功能现在使用自定义资源构建,这使得 Kubernetes 更加模块化。
自定义资源可以通过动态注册出现在运行中的集群中或从中消失,集群管理员可以独立于集群本身更新自定义资源。安装自定义资源后,用户可以使用kubectl创建和访问其对象,就像他们对待 Pods 等内置资源一样。
自定义控制器
仅凭自身,自定义资源允许你存储和检索结构化数据。当你将自定义资源与自定义控制器结合使用时,自定义资源提供真正的声明式 API。
Kubernetes 的声明式 API 强制执行职责分离。你声明了资源的期望状态。Kubernetes 控制器使 Kubernetes 对象的当前状态与你声明的期望状态保持同步。这与命令式 API 不同,在命令式 API 中,你指示服务器执行什么操作。
你可以在运行中的集群上部署和更新自定义控制器,独立于集群的生命周期。自定义控制器可以处理任何类型的资源,但在与自定义资源结合使用时特别有效。Operator 模式结合了自定义资源和自定义控制器。你可以使用自定义控制器将特定应用的领域知识编码到 Kubernetes API 的扩展中。
我是否应该将自定义资源添加到我的 Kubernetes 集群?
创建新的 API 时,考虑是将你的 API 与 Kubernetes 集群 API 进行聚合,还是让你的 API 独立存在。
如果满足以下条件,考虑 API 聚合 | 如果满足以下条件,首选独立 API |
---|---|
你的 API 是声明式的。 | 你的 API 不符合声明式模型。 |
你希望你的新类型可以使用 kubectl 进行读写。 | 不需要 kubectl 支持 |
你希望在 Kubernetes UI(例如 dashboard)中与内置类型一起查看你的新类型。 | 不需要 Kubernetes UI 支持。 |
你正在开发新的 API。 | 你已经有一个程序可以服务你的 API 并运行良好。 |
你愿意接受 Kubernetes 对 REST 资源路径的格式限制,例如 API Groups 和 Namespaces。(参阅API 概述。) | 你需要特定的 REST 路径以兼容已定义的 REST API。 |
你的资源自然地限定在集群或集群的命名空间内。 | 集群或命名空间范围的资源不太适合;你需要控制资源路径的细节。 |
你希望重用Kubernetes API 支持功能。 | 你不需要那些功能。 |
声明式 API
在声明式 API 中,通常
- 你的 API 由相对少量的相对小的对象(资源)组成。
- 这些对象定义应用或基础设施的配置。
- 这些对象更新频率相对较低。
- 人类通常需要读写这些对象。
- 对对象的主要操作是 CRUD 式的(创建、读取、更新和删除)。
- 不需要跨对象的事务:API 代表的是期望状态,而不是精确状态。
命令式 API 不是声明式的。你的 API 可能不是声明式的迹象包括
- 客户端说“执行此操作”,然后在操作完成后立即获得同步响应。
- 客户端说“执行此操作”,然后获得操作 ID,必须检查单独的 Operation 对象才能确定请求是否完成。
- 你谈论远程过程调用 (RPCs)。
- 直接存储大量数据;例如,每个对象大于几 KB,或对象数量大于 1000 个。
- 需要高带宽访问(持续每秒 10 个以上请求)。
- 存储最终用户数据(例如图片、PII 等)或应用处理的其他大规模数据。
- 对这些对象的自然操作不是 CRUD 式的。
- API 不容易建模为对象。
- 你选择用操作 ID 或操作对象来表示挂起的操作。
我应该使用 ConfigMap 还是自定义资源?
如果符合以下任何条件,请使用 ConfigMap
- 存在已有的、文档完善的配置文件格式,例如
mysql.cnf
或pom.xml
。 - 你希望将整个配置放入 ConfigMap 的一个键中。
- 配置文件的主要用途是供运行在集群 Pod 中的程序读取该文件来配置自身。
- 文件的使用者倾向于通过 Pod 中的文件或 Pod 中的环境变量来使用,而不是通过 Kubernetes API。
- 你希望在文件更新时通过 Deployment 等方式执行滚动更新。
注意
对于敏感数据,请使用Secret;Secret 类似于 ConfigMap 但更安全。如果符合以下大多数条件,请使用自定义资源(CRD 或聚合 API)
- 你希望使用 Kubernetes 客户端库和 CLI 来创建和更新新资源。
- 你希望
kubectl
提供顶层支持;例如,kubectl get my-object object-name
。 - 你希望构建新的自动化,它监视新对象的更新,然后对其他对象执行 CRUD 操作,反之亦然。
- 你希望编写处理对象更新的自动化程序。
- 你希望使用 Kubernetes API 约定,例如
.spec
、.status
和.metadata
。 - 你希望对象是对一组受控资源的抽象,或者是其他资源的汇总。
添加自定义资源
Kubernetes 提供了两种向集群添加自定义资源的方式
- CRD 简单,无需任何编程即可创建。
- API 聚合需要编程,但可以更好地控制 API 行为,例如数据存储方式和 API 版本之间的转换。
Kubernetes 提供这两个选项来满足不同用户的需求,这样既不牺牲易用性,也不牺牲灵活性。
聚合 API 是位于主 API 服务器后面的从属 API 服务器,主 API 服务器充当代理。这种安排称为API 聚合 (AA)。对于用户来说,Kubernetes API 看起来是扩展的。
CRD 允许用户在不添加另一个 API 服务器的情况下创建新类型的资源。你不需要理解 API 聚合即可使用 CRD。
无论如何安装,新资源都称为自定义资源,以便与内置的 Kubernetes 资源(如 pods)区分开来。
注意
避免将自定义资源用作应用程序、最终用户或监控数据的数据存储:将应用程序数据存储在 Kubernetes API 内的架构设计通常表示一种耦合过于紧密的设计。
从架构上看,云原生应用架构倾向于组件之间的松耦合。如果你的工作负载的某一部分在日常操作中需要后端服务,请将该后端服务作为组件运行或将其作为外部服务使用。这样,你的工作负载在正常操作时就不依赖于 Kubernetes API。
CustomResourceDefinition
CustomResourceDefinition API 资源允许你定义自定义资源。定义一个 CRD 对象会创建一个新的自定义资源,具有你指定的名称和 Schema。Kubernetes API 服务并处理自定义资源的存储。CRD 对象本身的名称必须是有效的DNS 子域名称,派生自已定义的资源名称及其 API 组;有关更多详细信息,请参阅如何创建 CRD。此外,由 CRD 定义的 kind/resource 的对象的名称也必须是有效的 DNS 子域名称。
这让你无需编写自己的 API 服务器来处理自定义资源,但这种实现的通用性意味着你不如API 服务器聚合灵活。
请参阅自定义控制器示例,了解如何注册新的自定义资源、如何使用新资源类型的实例以及如何使用控制器处理事件。
API 服务器聚合
通常,Kubernetes API 中的每个资源都需要处理 REST 请求和管理对象持久存储的代码。主 Kubernetes API 服务器处理内置资源(如 pods 和 services),也可以通过 CRD 通用地处理自定义资源。
聚合层允许你通过编写和部署自己的 API 服务器,为自定义资源提供专门的实现。主 API 服务器将请求委托给你的 API 服务器来处理你负责的自定义资源,从而使它们对所有客户端可用。
选择添加自定义资源的方法
CRD 更易于使用。聚合 API 更灵活。选择最符合你需求的方法。
通常,如果满足以下条件,CRD 是个不错的选择
- 你有少量字段
- 你正在公司内部使用该资源,或者作为小型开源项目的一部分(与商业产品相对)
比较易用性
CRD 比聚合 API 更易于创建。
CRD | 聚合 API |
---|---|
不需要编程。用户可以为 CRD 控制器选择任何语言。 | 需要编程并构建二进制文件和镜像。 |
无需运行额外的服务;CRD 由 API 服务器处理。 | 需要创建并可能失败的额外服务。 |
CRD 创建后无需持续支持。任何 Bug 修复都会作为正常的 Kubernetes Master 升级的一部分进行。 | 可能需要定期从上游获取 Bug 修复,然后重建并更新聚合 API 服务器。 |
无需处理 API 的多个版本;例如,当你控制此资源的客户端时,可以与 API 同步升级。 | 你需要处理 API 的多个版本;例如,在开发要与世界分享的扩展时。 |
高级功能和灵活性
聚合 API 提供更多高级 API 功能和对其他功能的定制;例如,存储层。
功能 | 描述 | CRD | 聚合 API |
---|---|---|---|
验证 | 帮助用户防止错误,并允许你独立于客户端演进 API。当有许多客户端无法同时更新时,这些功能最有用。 | 是的。大多数验证可以在 CRD 中使用OpenAPI v3.0 验证指定。CRDValidationRatcheting 功能门控允许忽略使用 OpenAPI 指定的失败验证(如果资源的失败部分未更改)。添加 Validating Webhook 可支持任何其他验证。 | 是的,任意验证检查 |
设置默认值 | 同上 | 是的,可以通过OpenAPI v3.0 验证的 default 关键字(1.17 GA)实现,或通过 Mutating Webhook 实现(但读取 etcd 中的旧对象时不会运行此 Webhook)。 | 是的 |
多版本支持 | 允许通过两个 API 版本提供同一对象。有助于简化 API 变更(如字段重命名)。如果你控制客户端版本,则这一点不太重要。 | 是的 | 是的 |
自定义存储 | 如果你需要具有不同性能模式(例如,时序数据库而非键值存储)的存储,或出于安全的隔离(例如,敏感信息的加密等) | 否 | 是的 |
自定义业务逻辑 | 在创建、读取、更新或删除对象时执行任意检查或操作。 | 是的,使用Webhook。 | 是的 |
Scale 子资源 | 允许 HorizontalPodAutoscaler 和 PodDisruptionBudget 等系统与你的新资源交互 | 是的 | 是的 |
Status 子资源 | 允许细粒度访问控制,其中用户写入 spec 部分,控制器写入 status 部分。允许在自定义资源数据变异时增加对象 Generation(资源中需要单独的 spec 和 status 部分)。 | 是的 | 是的 |
其他子资源 | 添加 CRUD 之外的操作,例如“logs”或“exec”。 | 否 | 是的 |
strategic-merge-patch | 新的端点支持使用 Content-Type: application/strategic-merge-patch+json 进行 PATCH。对于可能同时在本地和由服务器修改的对象进行更新很有用。有关更多信息,请参阅“使用 kubectl patch 就地更新 API 对象” | 否 | 是的 |
Protocol Buffers | 新资源支持希望使用 Protocol Buffers 的客户端 | 否 | 是的 |
OpenAPI Schema | 是否存在可从服务器动态获取的类型的 OpenAPI (swagger) Schema?通过确保只设置允许的字段,是否保护用户免受字段名称拼写错误的影响?是否强制执行类型(换句话说,不要将 int 放入 string 字段)? | 是的,基于 OpenAPI v3.0 验证 Schema(1.16 GA)。 | 是的 |
实例名称 | 此扩展机制是否对由此方式定义的 kind/resource 的对象的名称施加任何限制? | 是的,此类对象的名称必须是有效的 DNS 子域名称。 | 否 |
共同特性
与在 Kubernetes 平台之外实现相比,通过 CRD 或 AA 创建自定义资源时,你的 API 会获得许多特性
功能 | 功能描述 |
---|---|
CRUD | 新端点支持通过 HTTP 和 kubectl 执行基本的 CRUD 操作 |
Watch | 新端点支持通过 HTTP 执行 Kubernetes Watch 操作 |
发现 | kubectl 和 dashboard 等客户端会自动提供对你的资源的列出、显示和字段编辑操作 |
json-patch | 新端点支持使用 Content-Type: application/json-patch+json 进行 PATCH |
merge-patch | 新端点支持使用 Content-Type: application/merge-patch+json 进行 PATCH |
HTTPS | 新端点使用 HTTPS |
内置认证 | 对扩展的访问使用核心 API 服务器(聚合层)进行认证 |
内置授权 | 对扩展的访问可以重用核心 API 服务器使用的授权;例如,RBAC。 |
终结器 | 阻止删除扩展资源,直到外部清理完成。 |
准入 Webhook | 在任何创建/更新/删除操作期间设置默认值并验证扩展资源。 |
UI/CLI 显示 | Kubectl 和 dashboard 可以显示扩展资源。 |
未设置 vs 空值 | 客户端可以区分未设置的字段和零值字段。 |
客户端库生成 | Kubernetes 提供通用客户端库以及生成特定类型客户端库的工具。 |
标签和注解 | 跨对象的共同元数据,工具知道如何为核心和自定义资源进行编辑。 |
准备安装自定义资源
在将自定义资源添加到集群之前,有几个注意事项需要了解。
第三方代码和新的故障点
创建 CRD 本身并不会自动添加任何新的故障点(例如,导致第三方代码在你的 API 服务器上运行),但软件包(例如 Charts)或其他安装包通常包含 CRD 以及实现新自定义资源业务逻辑的第三方代码的 Deployment。
安装聚合 API 服务器总是涉及运行新的 Deployment。
存储
自定义资源消耗存储空间的方式与 ConfigMap 相同。创建过多的自定义资源可能会使你的 API 服务器的存储空间过载。
聚合 API 服务器可能使用与主 API 服务器相同的存储,在这种情况下,同样的警告适用。
认证、授权和审计
CRD 总是使用与你的 API 服务器内置资源相同的认证、授权和审计日志。
如果你使用 RBAC 进行授权,大多数 RBAC 角色不会授予对新资源的访问权限(cluster-admin 角色或使用通配符规则创建的任何角色除外)。你需要显式地授予对新资源的访问权限。CRD 和聚合 API 通常会捆绑包含它们添加的类型的新角色定义。
聚合 API 服务器可能使用,也可能不使用与主 API 服务器相同的认证、授权和审计。
访问自定义资源
可以使用 Kubernetes 客户端库来访问自定义资源。并非所有客户端库都支持自定义资源。Go 和 Python 客户端库支持。
添加自定义资源后,你可以使用以下方式访问它
kubectl
- Kubernetes 动态客户端。
- 你编写的 REST 客户端。
- 使用Kubernetes 客户端生成工具生成的客户端(生成客户端是一项高级任务,但有些项目可能会随 CRD 或 AA 一起提供客户端)。
自定义资源字段选择器
字段选择器允许客户端根据一个或多个资源字段的值选择自定义资源。
所有自定义资源都支持 metadata.name
和 metadata.namespace
字段选择器。
CustomResourceDefinition 中声明的字段如果包含在 CustomResourceDefinition 的 spec.versions[*].selectableFields
字段中,也可以与字段选择器一起使用。
自定义资源的字段选择器
Kubernetes v1.32 [stable]
(默认启用: true)CustomResourceDefinition 的 spec.versions[*].selectableFields
字段可用于声明自定义资源中的哪些其他字段可以在字段选择器中使用。
以下示例将 .spec.color
和 .spec.size
字段添加为可选择字段。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: shirts.stable.example.com
spec:
group: stable.example.com
scope: Namespaced
names:
plural: shirts
singular: shirt
kind: Shirt
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
color:
type: string
size:
type: string
selectableFields:
- jsonPath: .spec.color
- jsonPath: .spec.size
additionalPrinterColumns:
- jsonPath: .spec.color
name: Color
type: string
- jsonPath: .spec.size
name: Size
type: string
然后可以使用字段选择器获取仅具有 color
为 blue
的资源
kubectl get shirts.stable.example.com --field-selector spec.color=blue
输出应为
NAME COLOR SIZE
example1 blue S
example2 blue M