K8s CRD(CustomResourceDefinition,自定义资源定义)是 Kubernetes 核心的扩展能力之一,它允许用户在不修改 K8s 源代码、不编译自定义 API Server 的前提下,基于原生 K8s API 体系新增自定义资源类型,从而将 K8s 的管理能力从 "容器与服务" 延伸到业务领域(如数据库实例、AI 任务、IoT 设备等),实现 "业务资源 K8s 化管理"。
一、CRD 的核心本质与价值
1. 本质:扩展 K8s API 的 "元资源"
K8s 原生提供的资源(Pod、Deployment、Service 等)是为通用容器化场景设计的 "基础资源",而 CRD 是一种 "元资源"------ 它不直接管理业务对象,而是定义新资源类型的 "模板" 。当 CRD 被创建后,K8s API Server 会自动识别该自定义资源类型,并支持通过 kubectl、client-go 等工具进行 create/get/update/delete(CRUD)操作,与原生资源完全一致。
2. 核心价值
- 解耦业务与 K8s 核心:无需修改 K8s 源码,即可让 K8s 管理业务专属资源(如 "数据库实例""大数据任务"),降低扩展门槛。
- 统一管理体验 :自定义资源与原生资源使用相同的声明式配置(YAML)、相同的 API 接口、相同的命令行工具(
kubectl),运维人员无需学习新工具。 - 支撑 Operator 模式:CRD 是 Operator 框架的核心基础(Operator = CRD + 自定义控制器),通过 CRD 定义业务资源的 "期望状态",再通过控制器实现 "实际状态向期望状态的自动调和"(如自动创建数据库、备份数据、扩容节点)。
二、CRD 的关键概念与核心配置
1. 关键术语
| 术语 | 含义 |
|---|---|
| CRD(自定义资源定义) | 定义新资源类型的 YAML 配置,相当于 "资源模板",创建后 K8s 会识别该资源类型。 |
| CR(Custom Resource,自定义资源) | 基于 CRD 创建的具体实例,相当于 "资源对象"(如基于 Inventory CRD 创建的 laptop-inventory)。 |
| Group(组) | 自定义资源的 API 组,通常以业务域名命名(如 db.example.com、ai.company.com),用于区分不同业务领域的资源。 |
| Version(版本) | 自定义资源的版本(如 v1、v1beta1),支持资源结构的迭代升级(如从 v1 新增字段到 v2)。 |
| Scope(作用域) | 资源的生效范围: - Namespaced:命名空间级(如 Pod),仅在所属命名空间内可见; - Cluster:集群级(如 Node),全集群可见,无需指定命名空间。 |
2. CRD 核心配置详解(以 "数据库实例" 为例)
下面是一个定义 "MySQL 数据库实例" 的 CRD 配置(mysql-instances-crd.yaml),包含完整的核心字段说明:
yaml
apiVersion: apiextensions.k8s.io/v1 # CRD 对应的 API 版本(必选,v1 是稳定版)
kind: CustomResourceDefinition # 资源类型为 CRD(必选)
metadata:
name: mysqlinstances.db.example.com # CRD 的名称,格式为"复数资源名.API组"(必选)
spec:
group: db.example.com # 自定义资源的 API 组(必选,与 metadata.name 中的组一致)
names: # 自定义资源的名称配置(必选,影响 kubectl 命令使用)
plural: mysqlinstances # 资源的复数名(如 `kubectl get mysqlinstances`)
singular: mysqlinstance # 资源的单数名(如 `kubectl get mysqlinstance`)
kind: MySQLInstance # 资源的类型名(YAML 中 `kind` 字段的值,首字母大写)
shortNames: [mysql] # 资源的简称(如 `kubectl get mysql`,可选)
listKind: MySQLInstanceList # 列表类型名(默认是"Kind+List",可选)
scope: Namespaced # 作用域:命名空间级(可选,默认 Namespaced)
versions: # 资源的版本配置(必选,支持多版本)
- name: v1 # 版本名(如 v1)
served: true # 是否在 API Server 中提供该版本的服务(true=可访问)
storage: true # 是否将该版本作为存储版本(true=数据存储用该版本,仅一个版本可设为 true)
schema: # 资源的结构校验(可选,基于 OpenAPI v3,确保 CR 字段合法)
openAPIV3Schema:
type: object # 资源整体为对象类型
properties: # 定义 CR 的 spec 字段结构
spec:
type: object
required: [image, resources, rootPassword] # 必选字段(缺失会报错)
properties:
image: # 数据库镜像(如 mysql:8.0)
type: string
resources: # 资源限制(CPU/内存)
type: object
properties:
cpu:
type: string
memory:
type: string
rootPassword: # root 密码(敏感字段,实际建议用 Secret 挂载)
type: string
replicas: # 副本数(可选,默认 1)
type: integer
minimum: 1
storage: # 存储配置(可选)
type: object
properties:
size:
type: string
storageClass:
type: string
additionalPrinterColumns: # 自定义 `kubectl get` 时的显示列(可选,提升易用性)
- name: Replicas
type: integer
jsonPath: .spec.replicas # 从 CR 的 spec.replicas 取值
- name: Image
type: string
jsonPath: .spec.image # 从 CR 的 spec.image 取值
- name: Age
type: date
jsonPath: .metadata.creationTimestamp # 资源创建时间(原生字段)
关键配置说明:
schema.openAPIV3Schema:用于校验 CR 的字段合法性(如replicas必须是 ≥1 的整数),避免无效配置。如果不配置,K8s 会允许 CR 提交任意字段,可能导致业务异常。additionalPrinterColumns:自定义kubectl get mysql时的显示列,默认仅显示NAME和AGE,配置后可直接看到Replicas、Image等核心信息,无需kubectl describe。
三、CRD 与 CR 的实战流程(以 MySQL 实例为例)
1. 步骤 1:创建 CRD(定义资源类型)
将上述 mysql-instances-crd.yaml 应用到 K8s 集群,完成 "MySQL 实例" 资源类型的定义:
bash
# 应用 CRD
kubectl apply -f mysql-instances-crd.yaml
# 验证 CRD 是否创建成功(状态为 Established 表示生效)
kubectl get crd mysqlinstances.db.example.com
# 输出示例:
# NAME CREATED AT
# mysqlinstances.db.example.com 2024-05-20T10:00:00Z
2. 步骤 2:创建 CR(基于 CRD 的具体实例)
创建一个名为 my-mysql 的 MySQL 实例(my-mysql-cr.yaml),即 "基于 CRD 模板生成的资源对象":
yaml
apiVersion: db.example.com/v1 # API 组+版本(与 CRD 中的 group 和 versions.name 一致)
kind: MySQLInstance # 资源类型(与 CRD 中的 names.kind 一致)
metadata:
name: my-mysql # CR 的名称(命名空间级,需在 default 命名空间内唯一)
namespace: default
spec:
image: mysql:8.0 # 数据库镜像(必选字段,符合 CRD 中的 schema 校验)
replicas: 2 # 副本数(可选,默认 1)
resources: # 资源限制(必选字段)
cpu: "1"
memory: "1Gi"
rootPassword: "MyPass123!" # root 密码(必选字段,实际场景建议用 Secret 挂载)
storage: # 存储配置(可选)
size: "10Gi"
storageClass: "standard"
应用 CR 并验证:
bash
# 应用 CR
kubectl apply -f my-mysql-cr.yaml
# 查看 CR(用简称 mysql,更简洁)
kubectl get mysql
# 输出示例(包含自定义的 Replicas、Image 列):
# NAME REPLICAS IMAGE AGE
# my-mysql 2 mysql:8.0 30s
# 查看 CR 的详细信息
kubectl describe mysql my-mysql
3. 步骤 3:管理 CR(与原生资源操作一致)
CR 支持与原生资源完全相同的 kubectl 操作:
bash
# 更新 CR(修改副本数为 3)
kubectl edit mysql my-mysql # 编辑 YAML,将 spec.replicas 改为 3
# 删除 CR
kubectl delete mysql my-mysql
# 查看 CR 列表(全命名空间)
kubectl get mysql --all-namespaces
四、CRD 的进阶场景:结合自定义控制器(Operator)
CRD 本身仅负责 "定义资源类型" 和 "存储资源数据",但无法实现 "业务逻辑自动化"(如根据 CR 的 spec.replicas=2 自动创建 2 个 MySQL Pod)。要实现这一点,需要搭配自定义控制器(Controller) ,构成完整的 Operator 模式。
示例:MySQL Operator 的工作逻辑
- CRD 定义 "MySQLInstance" 资源:描述 MySQL 实例的期望状态(副本数、镜像、存储等)。
- 自定义控制器监听 CR 变化 :控制器通过 K8s API 监听
MySQLInstance类型的 CR,当 CR 被创建 / 更新 / 删除时,触发调和逻辑。 - 控制器实现 "期望→实际" 的调和 :
- 当 CR 中
spec.replicas=2时,控制器自动创建 2 个 MySQL Pod,并关联 Service、PersistentVolumeClaim(PVC)。 - 当某个 MySQL Pod 故障时,控制器检测到 "实际副本数 < 期望副本数",自动重启或重建 Pod。
- 当 CR 中
spec.image从mysql:8.0改为mysql:8.1时,控制器自动滚动更新所有 Pod 的镜像。
- 当 CR 中
常见 Operator 案例
- Prometheus Operator :通过 CRD(如
ServiceMonitor、Prometheus)管理 Prometheus 监控实例,自动创建监控配置、关联目标服务。 - PostgreSQL Operator(Crunchy Data) :通过 CRD(
PostgresCluster)管理 PostgreSQL 集群,自动实现主从复制、备份、故障转移。 - Elasticsearch Operator :通过 CRD(
Elasticsearch、Kibana)管理 Elasticsearch 集群,自动扩容、升级、维护索引。
五、CRD 的注意事项
- 版本兼容性 :CRD 支持多版本(如
v1、v2),但需注意版本迁移时的字段兼容性(如删除字段需谨慎,避免旧 CR 报错)。 - 资源命名规范 :CRD 的名称必须符合 "复数资源名.API 组" 格式(如
mysqlinstances.db.example.com),否则创建失败。 - 敏感数据处理 :CR 的
spec中不建议直接存储敏感信息(如密码、密钥),应通过secretKeyRef挂载 Secret(如rootPassword: { secretKeyRef: { name: mysql-secret, key: root-password } })。 - 性能考量:如果集群中存在大量 CR(如数万级),需优化自定义控制器的监听逻辑,避免频繁查询 API Server 导致性能瓶颈。
六、总结
CRD 是 K8s 实现 "业务定制化管理" 的核心手段,它通过扩展 API 体系,让 K8s 从 "容器编排平台" 升级为 "通用资源管理平台"。其核心流程是:用 CRD 定义资源类型→用 CR 创建具体实例→用自定义控制器实现自动化调和。无论是企业内部的中间件管理(MySQL、Redis),还是特定领域的业务场景(AI 训练任务、IoT 设备管理),CRD 都能帮助用户将业务逻辑与 K8s 原生能力深度融合,实现更高效的运维自动化。