在掌握了 Deployment、StatefulSet、Service、Ingress、Helm 等内置对象之后,很多运维工程师会问一个更深层的问题:当 Kubernetes 内置的能力不够用时,我们还能做什么?答案就是 Kubernetes 的核心扩展机制——自定义资源定义(CRD,CustomResourceDefinition)与基于它的 Operator 模式。CRD 让你像在数据库里建表一样,向集群注册全新的资源类型;而 Operator 则是围绕这些自定义资源编写的自动化控制器,把领域知识和运维经验”固化”成可被声明式管理的代码。理解并掌握 CRD 与 Operator,是运维工程师从”会用 K8s”迈向”能扩展 K8s”的关键一跃。

核心概念
什么是 CRD
CRD 本身也是一种 Kubernetes 资源,它通过 Kubernetes 的 API Server 注册,允许你定义一个全新的、结构化的资源类型。一旦 CRD 被创建,你就可以像操作原生对象(比如 Pod)一样,用 kubectl create / get / apply 去管理你的自定义资源。CRD 的核心价值在于:
- 把复杂的应用配置”资源化”,纳入声明式模型;
- 复用 Kubernetes 的所有原生能力:RBAC、Watch 机制、调度、控制器框架;
- 让运维经验沉淀为可复用、可审计、可版本化的资源定义。
CRD 通过 apiextensions.k8s.io/v1 API 组进行管理,是 Kubernetes 官方推荐的扩展方式(相比旧的 Aggregated API Server,CRD 维护成本更低)。
什么是 Operator
Operator 是一种设计模式,由 Red Hat 提出,其核心思想是:把运维知识编码进控制器,使其能自动执行重复的运维任务。一个标准的 Operator 通常包含两部分:CRD(定义资源结构)+ Controller(控制循环,实现期望状态与实际状态的调和,即 Reconcile)。当某个有状态应用(如数据库、消息队列、AI 训练任务)需要备份、扩缩容、故障自愈时,一个 Operator 就能把这些流程自动化,彻底告别手动敲命令。
API 版本与资源命名
自定义资源有一个关键约定:CRD 的 kind 必须使用复数、小写、点分的格式,例如 mysqlclusters.example.com。其中 mysqlclusters 是资源的复数短名,example.com 是所属的 Group。资源名采用”域名反转”格式是为了避免命名冲突,这也是云原生社区的标准实践。
实战步骤
下面我们用 kubebuilder 脚手架,从零创建一个管理”MySQL 集群”的 Operator,演示 CRD 与 Operator 的完整开发流程。
步骤一:安装 kubebuilder
kubebuilder 是 Kubernetes 官方推荐的 Operator 开发框架,它基于 Go 语言,内置了 controller-runtime。先安装它:
# 安装 kubebuilder(以 v3 为例)
go install sigs.k8s.io/kubebuilder/v3@latest
# 验证安装
kubebuilder version
# 下载 kustomize(用于构建部署清单)
go install sigs.k8s.io/kustomize/kustomize/v4@latest
步骤二:初始化项目骨架
接下来初始化项目,并生成第一个 API 与对应的控制器:
# 初始化项目
kubebuilder init --domain example.com --repo github.com/myorg/mysql-operator
# 创建 CRD API 与控制器:GVR(Group Version Kind)
kubebuilder create api --group database --version v1alpha1 --kind MySQLCluster --controller --resource
执行后,项目目录会生成 CRD 的 Go 结构体定义、Reconcile 控制器逻辑、以及 RBAC 权限文件。我们修改生成的 CRD 定义文件 api/v1alpha1/mysqlcluster_types.go,定义期望字段:
// MySQLClusterSpec 定义 MySQL 集群的期望状态
type MySQLClusterSpec struct {
Replicas int32 `json:"replicas,omitempty"` // 期望副本数
Image string `json:"image,omitempty"` // MySQL 镜像
StorageSize string `json:"storageSize,omitempty"` // 数据盘大小
}
步骤三:定义并部署 CRD
kubebuilder 会根据结构体自动生成 CRD 的 YAML。下面是生成的 mysqlclusters.example.com CRD 清单的核心结构(简化版,便于理解):
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: mysqlclusters.example.com
spec:
group: database.example.com
scope: Namespaced
names:
plural: mysqlclusters
singular: mysqlcluster
kind: MySQLCluster
shortNames: [mysql]
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
replicas:
type: integer
minimum: 1
maximum: 8
image:
type: string
注意这里开启了 OpenAPI Schema 校验(openAPIV3Schema),Kubernetes 会自动校验用户提交的资源是否合法。部署 CRD 后,集群中便注册了这个新资源类型:
# 构建并部署 CRD 与 RBAC
kustomize build config/crd | kubectl apply -f -
# 查看 CRD 是否注册成功
kubectl get crd mysqlclusters.example.com
# 查看新资源类型
kubectl get mysqlclusters --all-namespaces
步骤四:提交自定义资源实例
CRD 注册好后,就可以像操作 Pod 一样创建 MySQLCluster 实例:
apiVersion: database.example.com/v1alpha1
kind: MySQLCluster
metadata:
name: web-mysql
namespace: production
spec:
replicas: 3
image: mysql:8.0
storageSize: 100Gi
应用这份清单,Operator 的 Controller 就会收到 Watch 事件,开始执行调和逻辑。
步骤五:实现 Reconcile 控制循环
Operator 的精髓在 Reconcile 方法:它负责把”实际状态”调到”期望状态”。下面是控制器中创建 StatefulSet 的核心伪代码:
func (r *MySQLClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
cluster := &databasev1alpha1.MySQLCluster{}
if err := r.Get(ctx, req.NamespacedName, cluster); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 根据 spec.Replicas 创建/更新 StatefulSet
desired := cluster.Spec.Replicas
sts, err := r.buildStatefulSet(cluster)
if err != nil {
return ctrl.Result{}, err
}
if err := controllerutil.SetControllerReference(cluster, sts, r.Scheme); err != nil {
return ctrl.Result{}, err
}
if err := r.Client.Patch(ctx, sts, client.Apply,
client.FieldOwner("mysql-operator")); err != nil {
return ctrl.Result{}, err
}
_ = desired
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}
Reconcile 返回的 RequeueAfter 让控制器周期性重新检查,从而形成一个持续维护的闭环。
常见问题
Q1:CRD 与 Aggregated API Server 怎么选?
绝大多数场景优先选 CRD,开发部署成本远低于独立的 API Server。只有当你的扩展需要深度定制 API Server 行为(如复杂的存储后端、跨集群路由)时,才考虑 Aggregated API Server。
Q2:为什么自定义资源名要用域名反转?
为了避免不同厂商、不同项目的资源名冲突。mysqlclusters.example.com 里的 example.com 就是命名空间化的 Group,保证全局唯一。
Q3:删除 Operator 后,自定义资源会怎样?
删除 Controller 不影响已经创建的资源实例,它们会保留在 etcd 中但失去自动化能力。注意:不要随意删除 CRD 本身,否则会一并删除所有该类型的资源实例。
Q4:如何调试 Operator?
可用 kubectl describe mysqlcluster web-mysql 查看事件(Events)与状态,结合 Operator 容器的日志、以及 kubectl logs 查看控制器运行时的错误。
总结
CRD 与 Operator 是 Kubernetes 可扩展性的两大基石:CRD 让你向集群注册全新的资源类型,Operator 则把运维经验编码成自动化的控制循环。通过 kubebuilder 这样的脚手架,开发者可以快速构建出管理数据库、消息队列乃至 AI 任务的工作流自动化能力,真正做到”把运维变成代码(Ops as Code)”。掌握这一层,意味着你不仅能使用 Kubernetes,更能按需扩展 Kubernetes,让平台为你所用。
下期预告: 第 24 天我们将进入《K8s 运维系列 | 第 24 天:性能调优——节点与集群性能优化实战》,深入剖析 CPU/内存/Cgroup、节点内核参数、etcd 调优与集群容量规划,让集群性能发挥到极致。敬请期待!


















暂无评论内容