3. 创建第一个CRD:使用kubectl apply创建自定义资源定义,验证CRD注册成功
在理解了CRD的结构与核心字段之后,我们进入实战环节:通过 kubectl apply 将一个自定义资源定义提交到Kubernetes API Server,完成CRD的注册。这是扩展Kubernetes API的第一步,也是后续编写控制器的基石。
3.1 编写第一个CRD YAML清单
我们以一个管理“数据库实例”的场景为例,定义一个名为 Database 的自定义资源。该CRD属于 example.com API组,版本为 v1,作用域为集群级别(Cluster-scoped),包含两个核心字段:engine(数据库引擎类型)和 version(版本号)。
📄 示例:database-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.example.com
spec:
group: example.com
names:
kind: Database
listKind: DatabaseList
plural: databases
singular: database
shortNames:
- db
scope: Cluster
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
engine:
type: string
enum:
- mysql
- postgres
- mongodb
version:
type: string
pattern: '^\d+\.\d+\.\d+$'
required:
- engine
- version
| 字段路径 | 值 | 说明 |
|---|---|---|
metadata.name |
databases.example.com |
CRD名称必须等于 spec.names.plural + "." + spec.group |
spec.group |
example.com |
API组,用于URL路径:/apis/example.com/v1/... |
spec.scope |
Cluster |
集群级别资源,不隶属于任何命名空间 |
spec.versions[0].schema |
OpenAPI v3 Schema | 定义资源字段结构、类型、枚举约束与正则校验 |
spec.versions[0].storage |
true |
该版本是持久化存储版本,etcd中以此版本序列化 |
💡 命名规范: CRD的
metadata.name 必须严格遵循 <plural>.<group> 格式。如果拼写错误,API Server会拒绝注册并返回 422 Unprocessable Entity 错误。
3.2 提交CRD到集群
使用 kubectl apply 将YAML提交到Kubernetes集群。该命令会向API Server发送POST请求,触发CRD的注册流程。
# 提交CRD
kubectl apply -f database-crd.yaml
# 预期输出
customresourcedefinition.apiextensions.k8s.io/databases.example.com created
提交后,API Server会执行以下步骤:
- 验证CRD元数据:检查
metadata.name格式、group与names字段的合法性。 - 编译OpenAPI Schema:将
openAPIV3Schema注册到集群的OpenAPI规范中,用于后续的字段校验。 - 创建REST端点:在API Server内部动态生成
/apis/example.com/v1/databases路由。 - 启动CRD控制器:Kube-Controller-Manager中的CRD控制器开始监听该CRD,并为其创建对应的CRD资源实例的存储结构。
3.3 验证CRD注册成功
CRD提交后,可以通过以下三种方式验证注册状态:
方式一:查询CRD列表
kubectl get crd
# 输出示例
NAME CREATED AT
databases.example.com 2025-01-15T10:30:00Z
方式二:查看CRD详细信息
kubectl describe crd databases.example.com
# 关键输出片段
Name: databases.example.com
Group: example.com
Names:
Kind: Database
List Kind: DatabaseList
Plural: databases
Singular: database
Short Names:
db
Scope: Cluster
Version Names:
v1 SERVED, STORAGE
方式三:直接访问API端点
# 通过kubectl代理访问
kubectl proxy --port=8080 &
curl http://localhost:8080/apis/example.com/v1/databases
# 预期返回(空列表)
{
"apiVersion": "example.com/v1",
"kind": "DatabaseList",
"items": []
}
⚠️ 常见问题: 如果
kubectl get crd 显示 ESTABLISHED 状态为 False,通常是因为OpenAPI Schema中存在语法错误(如字段类型不匹配、缺少 required 字段等)。使用 kubectl describe crd <name> 查看 Conditions 字段中的错误详情。
3.4 CRD注册流程架构图
下图展示了从YAML提交到CRD注册完成的完整流程,以及各组件之间的交互关系:
3.5 创建第一个自定义资源实例
CRD注册成功后,即可创建该类型的自定义资源实例。以下示例创建一个名为 my-production-db 的Database资源:
# database-instance.yaml
apiVersion: example.com/v1
kind: Database
metadata:
name: my-production-db
spec:
engine: postgres
version: "14.5.0"
# 提交资源实例
kubectl apply -f database-instance.yaml
# 预期输出
database.example.com/my-production-db created
# 查询资源实例
kubectl get databases
# 输出示例
NAME ENGINE VERSION AGE
my-production-db postgres 14.5.0 10s
# 使用短名称查询
kubectl get db
# 输出示例
NAME ENGINE VERSION AGE
my-production-db postgres 14.5.0 10s
🔍 验证要点:
kubectl get databases返回了自定义资源实例,说明CRD已成功注册并可用。- 短名称
db生效,简化了日常操作。 - 如果尝试创建
engine: oracle(不在枚举值中),API Server会返回校验错误,拒绝创建。
3.6 注册状态检查清单
| 检查项 | 命令 | 成功标志 |
|---|---|---|
| CRD是否存在 | kubectl get crd databases.example.com |
返回CRD对象,ESTABLISHED 状态为 True |
| API端点是否可用 | kubectl proxy + curl /apis/example.com/v1/databases |
返回 200 OK,响应中包含 DatabaseList |
| 资源实例能否创建 | kubectl apply -f instance.yaml |
返回 created,无校验错误 |
| 字段校验是否生效 | 尝试创建非法字段值(如 engine: oracle) |
API Server返回 422 或 400 错误 |
| 短名称是否注册 | kubectl get db |
返回与 kubectl get databases 相同的结果 |
💡 生产建议: 在CI/CD流水线中,建议将CRD的注册与资源实例的创建分离。先提交CRD并等待
ESTABLISHED 状态变为 True(通常需要几秒钟),再提交资源实例。可以使用 kubectl wait --for=condition=Established crd/databases.example.com --timeout=60s 实现等待逻辑。