Kubernetes Operator 介绍与使用
介绍
什么是 CRD?
首先我们需要知道第一个概念就是 CRD,CRD 全称 Custom Resource Definition,是 Kubernetes 提供的一种 API 扩展机制,允许用户定义新的资源类型。
通过 CRD,你可以像使用原生资源(如 Pod、Service)一样操作自定义资源。
什么是 Kubernetes Operator?
Kubernetes Operator 是一种特定于应用的控制器,用于将人工运维操作自动化。它基于 CRD 定义的自定义资源,并根据资源的状态进行相应的操作,例如部署、升级、备份等。
我们可以简单的理解为 Kubernetes Operator = CRD + Controller 也就是说自定义资源加自定义控制器就是 Kubernetes Operator,使用它我们不仅可以自定义我们想要的资源,还可以通过我们想要的逻辑和方式对它进行操作。
核心原理
:利用 CRD 定义领域特定资源(Domain Specific Resources),控制器监听这些资源的变化并作出反应(Reconciliation Loop),实现“期望状态”与“实际状态”的自动同步
Kubernetes Operator 架构

自定义Operator开发
1. 准备开发环境
确认主机上已经安装 Docker 和 kubectl 并配置好 kubeconfig
2. 安装 Go
浏览器访问 https://go.dev/dl/ 获取最新版 Go 链接。使用 wget 下载
1
| wget https://go.dev/dl/go1.24.4.linux-amd64.tar.gz
|
解压并安装到 /usr/local
1
| sudo tar -C /usr/local -xzf go1.24.4.linux-amd64.tar.gz
|
编辑 ~/.bashrc 文件,添加 Go 到环境变量
1 2 3
| export PATH=$PATH:/usr/local/go/bin # 设置 Go 模块代理 export GOPROXY=https://goproxy.cn,direct
|
激活环境变量使生效
验证 Go 版本
3. 安装 kubebuilder
浏览器访问 https://github.com/kubernetes-sigs/kubebuilder/releases 获取最新版 kubebuilder 链接。使用 wget 下载
1
| wget https://github.com/kubernetes-sigs/kubebuilder/releases/download/v4.6.0/kubebuilder_linux_amd64
|
移动到 /usr/local/bin
1
| sudo mv kubebuilder_linux_amd64 /usr/local/bin/kubebuilder
|
验证 kubebuilder 版本
4. 初始化项目
1 2 3
| mkdir my-operator cd my-operator kubebuilder init --repo github.com/my-org/my-operator --domain mydomain.com
|
参数解释:
--repo
:指定 Go module 的根路径(一般为你的仓库路径,如 github.com/my-org/my-operator
)。Kubebuilder 用它生成 go.mod
和导入路径。
--domain
:指定 CRD API Group 后缀(如 apps.mydomain.com
)。
5. 定义一个新的 API(CRD + Controller)
1
| kubebuilder create api --group my-operator --version v1alpha1 --kind MyKind --resource --controller
|
参数解释:
--group
:指定 CRD 的 API Group。
--version
:指定 CRD 的 API Version。
--kind
:指定 CRD 的 Kind(资源类型)。
这会:
- 在 api/ 目录中创建 CRD 的 Go 类型。
- 在 controllers/ 中创建控制器逻辑。
- 更新 config/ 目录以包含部署文件模板。
6. 修改 CRD 的定义
修改 api/v1/examplea_types.go 文件。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package v1alpha1
import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" )
type MyKindSpec struct { GroupName string `json:"groupName,omitempty"` }
type MyKindStatus struct { UnderControl bool `json:"underControl,omitempty"` }
type MyKind struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MyKindSpec `json:"spec,omitempty"` Status MyKindStatus `json:"status,omitempty"` }
type MyKindList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` Items []MyKind `json:"items"` }
func init() { SchemeBuilder.Register(&MyKind{}, &MyKindList{}) }
|
7. 修改 Controller 的定义
修改 internal/controller/mykind_controller.go 文件。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| package controller
import ( "context"
corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile"
myoperatorv1alpha1 "github.com/my-org/my-operator/api/v1alpha1" )
type MyKindReconciler struct { client.Client Scheme *runtime.Scheme }
func (r *MyKindReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) logger.Info("开始调用Reconcile方法")
var exp myoperatorv1alpha1.MyKind if err := r.Get(ctx, req.NamespacedName, &exp); err != nil { logger.Error(err, "未找到对应的CRD资源") return ctrl.Result{}, client.IgnoreNotFound(err) }
exp.Status.UnderControl = false
var podList corev1.PodList if err := r.List(ctx, &podList); err != nil { logger.Error(err, "无法获取pod列表") } else { for _, item := range podList.Items { if item.GetLabels()["group"] == exp.Spec.GroupName { logger.Info("找到对应的pod资源", "name", item.GetName()) exp.Status.UnderControl = true } } }
if err := r.Status().Update(ctx, &exp); err != nil { logger.Error(err, "无法更新CRD资源状态") return ctrl.Result{}, err } logger.Info("已更新CRD资源状态", "status", exp.Status.UnderControl)
return ctrl.Result{}, nil }
func (r *MyKindReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&myoperatorv1alpha1.MyKind{}). Watches( &corev1.Pod{}, handler.EnqueueRequestsFromMapFunc(r.podChangeHandler), ). Complete(r) }
func (r *MyKindReconciler) podChangeHandler(ctx context.Context, obj client.Object) []reconcile.Request { logger := log.FromContext(ctx)
var req []reconcile.Request var list myoperatorv1alpha1.MyKindList if err := r.Client.List(ctx, &list); err != nil { logger.Error(err, "无法获取到资源") } else { for _, item := range list.Items { if item.Spec.GroupName == obj.GetLabels()["group"] { req = append(req, reconcile.Request{ NamespacedName: types.NamespacedName{Name: item.Name, Namespace: item.Namespace}, }) } } } return req }
|
8. 运行 Operator
执行下面的命令生成并将 CRD 安装到 k8s 集群中
1 2
| make manifests make install
|
安装成功后,查看一下
1
| kubectl get crd | grep mykind
|
新开一个终端窗口启动
创建一个 MyKind 资源
1 2 3 4 5 6 7 8 9
| apiVersion: my-operator.mydomain.com/v1alpha1 kind: MyKind metadata: labels: app.kubernetes.io/name: my-operator app.kubernetes.io/managed-by: kustomize name: mykind-sample spec: groupName: business
|
创建成功后,我们查看一下当前的 CRD 状态,可以看到现在状态应该是空的
1
| kubectl describe MyKind mykind-sample
|
然后新建一个文件 example_v1_examplea 1.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13
| apiVersion: v1 kind: Pod metadata: name: busybox labels: group: "business" spec: containers: - name: busybox image: busybox:latest command: - sleep - "3600"
|
然后再次查看 CRD 状态,可以看到控制状态已经变成了 true 了,同时你也可以在控制台的日志中看到资源状态变更的日志。
之后你就可以摸索更加高级的各种操作了,根据具体的实际业务场景需求来满足不同的需要。
如果需要回收删除对应的资源先使用 kubectl delete -f 删除所有创建的测试。然后直接执行 make uninstall 就可以了。