Kubernetes CRD(自定义资源,CustomResourceDefinition)详解

文章目录
- [Kubernetes CRD(自定义资源,CustomResourceDefinition)详解](#Kubernetes CRD(自定义资源,CustomResourceDefinition)详解)
-
- [1. 引言](#1. 引言)
- [2. 基本概念](#2. 基本概念)
- [3. CRD 的工作原理](#3. CRD 的工作原理)
- [4. 如何创建 CRD](#4. 如何创建 CRD)
- [5. 使用 CRD](#5. 使用 CRD)
- [6. 控制器模式](#6. 控制器模式)
- [7. 高级特性](#7. 高级特性)
-
- [7.1 版本管理](#7.1 版本管理)
- [7.2 子资源](#7.2 子资源)
- [7.3 验证和默认值](#7.3 验证和默认值)
- [7.4 附加打印列](#7.4 附加打印列)
- [7.5 最终确定器 (Finalizers)](#7.5 最终确定器 (Finalizers))
- [8. 实际应用场景](#8. 实际应用场景)
- [9. 最佳实践和注意事项](#9. 最佳实践和注意事项)
- [10. 总结](#10. 总结)
1. 引言
Kubernetes 是一个可扩展的平台,其核心 API 提供了诸如 Pod、Service、Deployment 等丰富的内置资源,用于管理容器化应用。然而,在实际业务中,我们往往需要表达一些 Kubernetes 原生不支持的领域概念,例如数据库实例、消息队列主题、应用配置策略等。CustomResourceDefinition (CRD) 正是 Kubernetes 提供的一种机制,允许用户在不修改核心代码的情况下,向 Kubernetes API 中添加自定义的资源类型,从而将 Kubernetes 打造成一个通用的、可编程的应用平台。
CRD 是 Kubernetes API 扩展的事实标准,与 Operator 模式紧密结合,成为构建云原生基础设施和应用的关键技术。
2. 基本概念
CRD 本身是 Kubernetes API 中的一个内置资源类型(位于 apiextensions.k8s.io/v1 API 组)。通过创建一个 CRD 对象,你向 Kubernetes 声明了一种新的、用户自定义的资源类型。此后,你就可以像操作 Pod 一样,通过 kubectl 或直接调用 API 来创建、查看、更新和删除这种新资源的实例。
- CustomResourceDefinition (CRD):定义一种新的资源类型的元数据,包括资源名称、API 组、版本、作用域(Namespaced 或 Cluster)以及结构验证规则等。
- Custom Resource (CR):根据 CRD 定义创建出来的具体资源对象,代表某个领域实体的实例。
例如,你可以定义一个名为 Database 的 CRD,然后创建 database-sample 这样的 CR 来代表一个具体的数据库实例。
3. CRD 的工作原理
CRD 的实现基于 Kubernetes API 服务器的扩展能力。当你创建一个 CRD 对象时,API Server 会自动注册一个新的 RESTful 路径,用于处理该自定义资源的请求。其工作流程大致如下:
- 用户创建 CRD:用户提交一个 CRD YAML 到 Kubernetes API。
- API Server 动态注册 :API Server 解析 CRD,验证其合法性,然后在内部存储中注册新的资源类型。这相当于在 API Server 的路由表中增加了一条规则,将
/<api-group>/<version>/namespaces/<namespace>/<resource-plural>路径映射到新的资源处理逻辑。 - 存储支持:自定义资源的数据同样存储在 etcd 中,与内置资源一样享受高可用和一致性保证。API Server 负责对 CR 进行请求认证、鉴权、验证(根据 CRD 中定义的 OpenAPI schema)以及持久化。
- 用户操作 CR :此后,用户可以通过标准的 Kubernetes API 或
kubectl来操作自定义资源。例如kubectl get databases或kubectl describe database database-sample。 - 控制器协调:通常,CRD 会配合一个控制器(Controller)一起工作。控制器监听自定义资源的变化,并根据 CR 的期望状态(Spec)执行相应的业务逻辑,最终更新资源的状态(Status)。这一模式正是 Kubernetes 声明式 API 的精髓。
4. 如何创建 CRD
创建一个 CRD 需要编写 YAML 清单,并通过 kubectl apply 提交。以下是一个简化的 CRD 示例,定义了一个名为 Database 的自定义资源:
yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.example.com 必须格式:<plural>.<group>
spec:
group: example.com API 组
versions:
- name: v1alpha1 版本
served: true 是否在该版本上提供服务
storage: true 是否将数据存储为该版本
schema: OpenAPI v3 验证 schema
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
engine:
type: string
enum: ["mysql", "postgres"]
version:
type: string
storageGB:
type: integer
minimum: 1
status:
type: object
properties:
phase:
type: string
connectionString:
type: string
subresources:
status: {} 启用 status 子资源
scale: 启用 scale 子资源(可选)
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
additionalPrinterColumns: 定义 `kubectl get` 显示的额外列
- name: Engine
type: string
jsonPath: .spec.engine
- name: Version
type: string
jsonPath: .spec.version
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
scope: Namespaced 作用域:Namespaced 或 Cluster
names:
plural: databases 复数名称,用于 URL
singular: database 单数名称,用于 CLI 别名
kind: Database 资源类型名称,用于 YAML/CLI
shortNames: 短名称
- db
关键字段说明:
group:自定义资源所属的 API 组,通常采用域名形式以避免冲突。versions:支持一个或多个版本,每个版本可以独立定义验证 schema、子资源等。必须指定一个存储版本。schema:OpenAPI v3 schema,用于验证自定义资源的字段类型和值,确保数据的合法性。强烈建议提供,避免无效数据进入 etcd。subresources:启用/status和/scale子路径,方便控制器更新状态或水平伸缩。additionalPrinterColumns:定义kubectl get输出时显示的额外列,提升可读性。scope:资源是命名空间级别(Namespaced)还是集群级别(Cluster)。names:资源的各种名称形式。
5. 使用 CRD
一旦 CRD 创建成功,你就可以像操作内置资源一样操作自定义资源。
-
创建自定义资源实例(CR):
yamlapiVersion: example.com/v1alpha1 kind: Database metadata: name: mydb namespace: default spec: engine: postgres version: "13" storageGB: 100使用
kubectl apply -f database.yaml创建。 -
查看自定义资源:
bashkubectl get databases kubectl get db 使用短名称 kubectl describe database mydb -
直接访问 API :
也可以通过 API Server 的代理访问,例如:
bashkubectl proxy --port=8080 & curl http://localhost:8080/apis/example.com/v1alpha1/namespaces/default/databases/mydb
6. 控制器模式
CRD 本身只负责存储和提供 API,而实际的业务逻辑需要通过控制器来实现。控制器监听自定义资源的事件(创建、更新、删除),然后采取相应的动作使实际状态趋近于期望状态。这种模式被称为 Operator 模式。
例如,一个 Database 控制器可能会:
- 当发现一个新的
DatabaseCR 时,根据spec中的引擎和版本信息,在底层云平台或数据库服务中创建一个真实的数据库实例。 - 更新 CR 的
status字段,将数据库的连接地址、状态等信息写回。 - 当 CR 被删除时,清理对应的真实数据库资源。
开发控制器常用的工具包括:
- kubebuilder:官方脚手架,基于 controller-runtime 库。
- Operator SDK:由 Red Hat 主导,封装了 kubebuilder 并提供额外功能。
- controller-runtime:直接使用 Go 库编写控制器。
7. 高级特性
7.1 版本管理
CRD 支持多个 API 版本共存,方便资源演进。你可以定义多个版本,并通过 storage 字段指定哪个版本负责存储。对于不同版本之间的转换,可以通过 Conversion Webhook 实现自定义的转换逻辑,确保旧版本对象可以无损地转换为新版本。
7.2 子资源
- status 子资源 :启用后,对 CR 的
/status子路径的更新不会触发metadata.generation的增加,便于控制器与用户更改分离。 - scale 子资源 :使自定义资源可以接入
kubectl scale命令,并与 HorizontalPodAutoscaler 集成。
7.3 验证和默认值
除了 OpenAPI schema 验证,CRD 还支持通过 ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 实现更复杂的验证和默认值设置。Webhook 可以在资源持久化之前拦截请求,进行自定义逻辑。
7.4 附加打印列
通过 additionalPrinterColumns,你可以让 kubectl get 的输出包含用户关心的字段,如状态、版本等,提升 CLI 的友好度。
7.5 最终确定器 (Finalizers)
与内置资源一样,CR 可以包含 finalizers,用于在删除资源前执行异步清理任务。控制器在完成清理后移除 finalizer,资源才会被真正删除。
8. 实际应用场景
CRD 广泛应用于云原生生态中的各种 Operator 和扩展:
- Istio :通过
VirtualService、DestinationRule等 CRD 管理服务网格流量规则。 - Cert-Manager :提供
Certificate、Issuer等 CRD 自动化 TLS 证书的申请和续期。 - Prometheus Operator :使用
ServiceMonitor、PrometheusRule等 CRD 动态配置监控和告警。 - Knative :定义
Service、Revision等 CRD 实现 Serverless 平台。 - 数据库 Operator :如
MySQL Operator、Postgres Operator通过 CRD 管理数据库实例的生命周期。
9. 最佳实践和注意事项
- 设计良好的 API :遵循 Kubernetes API 设计规范,使用
spec和status分离期望状态和实际状态。 - 版本规划:从一开始考虑 API 的演进,合理设计多版本策略,尽早定义转换 webhook。
- 提供完整的验证 schema:避免无效数据进入存储,减少控制器处理异常情况的复杂度。
- 使用 status 子资源:让控制器通过 status 更新状态,避免与用户对 spec 的更新冲突。
- 合理设置 finalizers:确保资源删除时能正确清理外部依赖,防止残留。
- 注意性能:CRD 本身不会带来明显的性能开销,但大量 CR 或频繁的更新可能会增加 API Server 和 etcd 的压力。使用 label 选择器和 informer 机制减少不必要的监听。
- 避免名称冲突 :选择特定的 API 组(如
company.com)以避免与其他 CRD 冲突。 - 权限管理:使用 RBAC 为不同的用户或控制器授予对自定义资源的适当权限。
10. 总结
CRD 是 Kubernetes 可扩展性的基石,它允许你将 Kubernetes 的声明式 API 模型应用到任何领域。通过 CRD,用户可以定义自己的资源类型,并配合控制器实现复杂的自动化运维逻辑,即 Operator 模式。掌握 CRD 的设计与开发,能够帮助你充分利用 Kubernetes 平台的能力,构建高度自动化和可扩展的云原生应用。
无论是平台工程师还是应用开发者,理解 CRD 的工作原理和使用方法,都是在 Kubernetes 生态中进行深度实践的关键一步。