
Kubernetes作为云原生时代的操作系统,其强大的扩展能力很大程度上得益于Custom Resource Definitions(CRD)机制。CRD允许用户在Kubernetes中定义和管理自己的资源类型,从而扩展原生API的功能。本文将全面剖析CRD的工作原理、设计模式、典型应用场景以及最佳实践,帮助开发者深入理解并有效利用这一核心机制。
CRD(Custom Resource Definition)是Kubernetes提供的一种无需修改核心代码即可扩展API的机制,它允许用户定义自己的资源类型(Custom Resource)。从本质上说,CRD本身就是一种特殊的Kubernetes内置资源类型,用于描述用户自定义资源的结构和行为。
一个典型的CRD定义包含以下核心元素:
stable.example.com;v1、v1beta1;CronTab;Namespaced(命名空间级)和Cluster(集群级);apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec: {type: string}
image: {type: string}
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames: [ct]单纯定义CRD只能实现资源的声明和存储,要使自定义资源真正发挥作用,通常需要配合自定义控制器(CustomController),这就是所谓的Operator模式。Operator的核心思想是将领域知识编码到软件中,通过以下组件协同工作:
这种“CRD+Controller”的组合使Kubernetes能够管理任何类型的应用,从数据库中间件到AI训练任务。
相比传统的API扩展方式,CRD具有以下显著优势:
当用户创建CRD时,Kubernetes API Server会执行以下关键操作:
/apis/路径下创建新的API端点;/apis列表中暴露新资源的信息;# 查看已注册的API组:kubectl get --raw /apis | jq '.groups[].name'
# 查看特定CRD的API端点:kubectl get --raw /apis/stable.example.com/v1/crontabsKubernetes中的所有资源最终都以层次化结构存储在etcd中,其路径构成为:
/<group>/<version>/namespaces/<namespace>/<resource>/<name>例如:
/core/v1/namespaces/default/pods/my-pod/stable.example.com/v1/namespaces/default/crontabs/my-cron这种统一的存储结构使得自定义资源与内置资源在持久化层面享有相同的特性和保障。
当用户提交自定义资源YAML时,API Server会经过以下处理流程:
自定义控制器通常采用以下架构实现:
graph TD
A[API Server] -->|Watch| B(Informer)
B -->|Add/Update/Delete| C[WorkQueue]
C --> D[Reconcile Loop]
D -->|Get| E[API Server]
D -->|Create/Update| F[Kubernetes Resources]关键组件说明:
这种设计模式确保了控制器能够高效、可靠地处理资源变更,即使在发生中断的情况下也能恢复状态。
CRD支持定义多个API版本,每个版本可以有不同的Schema。Kubernetes通过存储版本和转换机制实现版本兼容:
versions:
- name: v1alpha1
served: true
storage: false
schema: {...}
- name: v1beta1
served: true
storage: false
schema: {...}
- name: v1
served: true
storage: true
schema: {...}通过OpenAPI v3 Schema,CRD可以定义丰富的字段校验规则,包括:
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [image, replicas]
properties:
image:
type: string
pattern: "^[a-zA-Z0-9.-]+(:[a-zA-Z0-9.-]+)?$"
replicas:
type: integer
minimum: 1
maximum: 10CRD支持定义status和scale子资源,这符合Kubernetes的声明式API设计原则:
subresources:
status: {}
scale:
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicasFinalizer机制允许控制器在资源删除前执行清理逻辑,确保资源被安全删除:
// 在控制器中添加Finalizer
if !controllerutil.ContainsFinalizer(obj, myFinalizer) {
controllerutil.AddFinalizer(obj, myFinalizer)
return ctrl.Result{Requeue: true}, nil
}
// 处理删除逻辑
if !obj.DeletionTimestamp.IsZero() {
// 执行清理工作
controllerutil.RemoveFinalizer(obj, myFinalizer)
return ctrl.Result{}, nil
}Operator模式最常见的应用场景是管理有状态分布式系统,如:
例如Etcd Operator可以自动处理节点故障恢复、备份还原、版本升级等复杂操作,将运维知识编码到控制器逻辑中。
CRD可以将领域概念直接映射为Kubernetes资源,如:
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
name: mnist-train
spec:
tfReplicaSpecs:
Worker:
replicas: 3
template:
spec:
containers:
- name: tensorflow
image: tensorflow/mnist:latest
command: ["python", "/mnist.py"]在多云和边缘计算场景中,CRD可以统一抽象不同环境的配置策略:
apiVersion: edge.k8s.io/v1
kind: DeviceProfile
metadata:
name: camera-device
spec:
protocol: MQTT
properties:
resolution: 1080p
framerate: 30通过CRD可以扩展Kubernetes的运维能力,实现:
apiVersion: autoscaling.internal.k8s.io/v1
kind: CustomMetricHPA
metadata:
name: order-queue-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-processor
metrics:
- type: External
external:
metricName: queue_length
targetValue: 100在 Kubernetes 集群里开发、部署并使用自定义 CRD 的完整实战流程,包含示例 YAML、常用命令、控制器(Operator)骨架代码完成第一个可落地的 CRD 扩展。
1、需求假设我们要在集群里管理「游戏房间」这一业务对象,期望能像操作 Deployment 一样用 kubectl 命令完成增删改查,同时由控制器自动在后台拉起对应的 Pod、Service。
2、定义 CRD(YAML 方式,无需编程)
文件 game-room-crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: gamerooms.example.com # <plural>.<group>
spec:
group: example.com
scope: Namespaced # 或 Cluster
names:
plural: gamerooms
singular: gameroom
kind: GameRoom
shortNames:
- gr
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image:
type: string
port:
type: integer
minimum: 1024
replicas:
type: integer
minimum: 1
status:
type: object
properties:
readyReplicas:
type: integer一键注册
kubectl apply -f game-room-crd.yaml
kubectl wait --for condition=established crd/gamerooms.example.com --timeout=60s验证
kubectl api-resources | grep gamerooms
kubectl get crd gamerooms.example.com -o yaml3、创建自定义资源实例
文件 my-room.yaml
apiVersion: example.com/v1
kind: GameRoom
metadata:
name: room-demo
namespace: default
spec:
image: nginx:1.25
port: 8080
replicas: 2kubectl apply -f my-room.yaml
kubectl get gr # 简写
kubectl describe gr room-demo此时 API Server 已能正常存储该对象,但还没有任何业务逻辑——接下来写控制器。
4、用 Kubebuilder 3.x 生成控制器骨架(Go)
# 安装 kubebuilder
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/
# 初始化项目
mkdir game-room-operator && cd game-room-operator
kubebuilder init --domain example.com --repo github.com/example/game-room-operator
kubebuilder create api --group example --version v1 --kind GameRoom生成的目录结构
api/v1/gameroom_types.go # 对应 CRD 的 Spec/Status 结构体
controllers/gameroom_controller.go
main.go5、补全控制器逻辑(核心片段)
controllers/gameroom_controller.go
func (r *GameRoomReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
var room examplev1.GameRoom
if err := r.Get(ctx, req.NamespacedName, &room); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 1. 计算期望 Deployment
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: room.Name,
Namespace: room.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &room.Spec.Replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": room.Name},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": room.Name}},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: "game",
Image: room.Spec.Image,
Ports: []corev1.ContainerPort{{ContainerPort: int32(room.Spec.Port)}},
}},
},
},
},
}
// 2. 设置 ownerRef,实现级联删除
if err := controllerutil.SetControllerReference(&room, deploy, r.Scheme); err != nil {
return ctrl.Result{}, err
}
// 3. 创建或更新
var found appsv1.Deployment
if err := r.Get(ctx, client.ObjectKeyFromObject(deploy), &found); err != nil {
if errors.IsNotFound(err) {
log.Info("Creating Deployment", "Deploy.Namespace", deploy.Namespace, "Deploy.Name", deploy.Name)
return ctrl.Result{}, r.Create(ctx, deploy)
}
return ctrl.Result{}, err
}
// 4. 更新 status(略)
return ctrl.Result{}, nil
}6、本地一键部署调试
# 生成 CRD YAML 并直接装进集群
make manifests
kubectl apply -f config/crd/bases
# 本地运行控制器,连接 ~/.kube/config
make run此时再创建 GameRoom 实例,控制器会立即创建同名 Deployment,你可以 kubectl get deploy,po,svc 观察。
7、打包发布 Operator(可选)
make docker-build docker-push IMG=your-registry/game-room-operator:v0.1.0
make deploy IMG=your-registry/game-room-operator:v0.1.08、常见问题
spec.versions[*].schema 一旦发布后只能做「向后兼容」的修改;如需破坏式变更,必须新增版本并做存储版本迁移。subresources:
status: {}控制器才能通过 .status 单独更新而避免触发 Reconcile 风暴。
metadata.finalizers,并在控制器里监听 DeletionTimestamp。kubebuilder 生成的 config/rbac/role.yaml 已经包含所需权限;生产环境务必做最小化裁剪。served: true 留给 v2,storage: true 留在 v1,逐步迁移。一个可运行、可发布、可迭代的 Kubernetes CRD 扩展已开发完成。步骤归纳如下:
步骤 | 是否必须 | 工具/命令 | 备注 |
|---|---|---|---|
定义 CRD | 必须 | YAML + kubectl apply | 无代码即可扩展 API |
创建实例 | 必须 | kubectl apply | 与普通资源体验一致 |
编写控制器 | 推荐 | Kubebuilder/Operator-SDK | 把「声明」翻译成「实际资源」 |
打包发布 | 可选 | make docker-build/deploy | 适合团队或对外发布 |
Kubernetes CRD机制通过声明式API和控制器模式,为用户提供了强大的扩展能力。对于开发者和架构师而言,掌握CRD的设计和实现原理,将有助于构建更灵活、更强大的云原生应用。在实践中,逐步构建符合业务特性的扩展模型,同时充分利用Kubebuilder等工具提高开发效率。
如果你觉得这篇文章有用,欢迎点赞、转发、收藏、留言、推荐❤!
本文分享自 Nicholas与Pypi 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!