Kubernetes Operator是一个进阶的云原生工具,它能将复杂应用的运维知识编码成可重复使用的自动化程序。虽然概念听起来有些复杂,但别担心,下面我会为你拆解它的核心思想,并提供一个从使用到编写的详细指南。

一、🔍 理解Kubernetes Operator

简单来说,你可以把Operator看作是你集群中一位不知疲倦的、专门负责管理某个特定应用(例如MySQL、Redis等)的AI运维专家

这位"专家"通过以下两个核心部件工作:

  1. 自定义资源(CRD):这是你为这个"专家"专门设计的工作指令单。它扩展了Kubernetes的API,让你能够定义和管理原生Kubernetes不支持的资源类型。比如,你可以创建一个 MysqlCluster 类型的资源。
  2. 自定义控制器:这是那位"AI运维专家"本身。它会持续不断地监控你发出的这些"工作指令单"(CR),并根据指令单里的描述(即你期望的应用状态),去驱动整个系统达到并维持这个状态

Operator vs. 原生控制器:你可能会问,Kubernetes本身不就有Deployment、StatefulSet等控制器吗?没错。但Operator更专业,它内嵌了特定应用领域的运维知识(比如如何安全地升级数据库,如何执行备份恢复),而原生控制器主要负责通用的、无状态的编排工作。

二、🛠️ 如何使用现成的Operator?

在考虑自己编写Operator之前,强烈建议你先熟悉如何使用社区里已有的成熟Operator。这是最快速的入门方式。

  1. 寻找Operator:你可以在 OperatorHub.io 上找到大量的、由社区维护的Operator文档

    在这里插入图片描述

  2. 安装Operator:很多Operator可以通过Operator Lifecycle Manager (OLM) 来安装,这是一个在集群内安装、管理和升级Operator的框架。操作通常很简单,在OperatorHub的网页上会有详细的安装指引。

  3. 使用Operator:安装好后,你就可以通过创建自定义资源(CR) 来使用它。例如,如果安装了一个Etcd Operator,你可能会创建一个如下所示的YAML文件,然后使用 kubectl apply -f 命令,Operator就会自动为你部署和管理一个Etcd集群。

    apiVersion: "etcd.database.coreos.com/v1beta2"
    kind: "EtcdCluster"
    metadata:
      name: "example-etcd-cluster"
    spec:
      size: 3 # 期望的集群节点数量
      version: "3.4.13"
    

三、👨‍💻 如何编写自己的Operator?

当你需要管理一个Kubernetes本身无法处理的、有状态的复杂应用时,就是考虑编写自己Operator的时候了。这里我们以Go语言和 Kubebuilder 框架为例,因为它目前是社区最主流和推荐的方式。

3.1 准备工作

我本地的k8s集群的版本是v1.23.0

  • 一个Kubernetes集群(可以是Minikube、Kind等本地开发集群)。

  • 安装好Go(1.13+版本)。

    #1官方网站下载
    https://go.dev/dl/
    #本次下载的是
    go1.24.9.linux-amd64.tar.gz
    #2 解压缩
    tar -zxvf go1.24.9.linux-amd64.tar.gz -C /usr/local/
    #3 配置环境变量
    vim /etc/profile
    export GOROOT=/usr/local/go
    export PATH=$PATH:$GOROOT/bin
    #4 生效
    source /etc/profile
    #5 验证
    go version
    go version go1.24.9 linux/amd64
    
  • 安装Docker

  • 安装kubectl

  • 安装Kubebuilder命令行工具。

    #1 创建相关文件夹
    mkdir /root/crd
    cd /root/crd
    
    #2 下载并添加到环境变量
    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/
    
    #3 部署验证
    kubebuilder version
    
    Version: cmd.version{KubeBuilderVersion:"4.9.0", KubernetesVendor:"1.34.0", GitCommit:"5e331e74c7a25c8e8fc0d9d5c33c319b7268f395", BuildDate:"2025-09-22T10:53:21Z", GoOs:"linux", GoArch:"amd64"}
    
    #4 #更改GOPROXY依赖下载代理为国内的地址
    go env -w GOPROXY=https://mirrors.aliyun.com/goproxy,direct
    

3.2 详细步骤

第一步:初始化项目
使用Kubebuilder创建一个新的Operator项目骨架。这会生成一个完整的Go模块,包含所有必要的依赖和基础配置文件。

#1 创建项目目录并进入
[root@k8s-master][~/crd]
$mkdir myopertor

[root@k8s-master][~/crd]
$cd myopertor/

#2 初始化项目
kubebuilder init --domain=mydomain.com --repo=mydomain.com/myoperator
#初始化项目的时间会稍微久一些,下面是一些日志信息
INFO Writing kustomize manifests for you to edit... 
INFO Writing scaffold for you to edit... 
INFO Get controller runtime 
go: downloading sigs.k8s.io/controller-runtime v0.22.1
go: downloading k8s.io/apimachinery v0.34.0
go: downloading github.com/gogo/protobuf v1.3.2
go: downloading github.com/go-logr/logr v1.4.2
go: downloading k8s.io/client-go v0.34.0
go: downloading k8s.io/klog/v2 v2.130.1
go: downloading k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
go: downloading k8s.io/api v0.34.0
go: downloading sigs.k8s.io/randfill v1.0.0
go: downloading sigs.k8s.io/structured-merge-diff/v6 v6.3.0
go: downloading github.com/evanphx/json-patch/v5 v5.9.11
go: downloading gomodules.xyz/jsonpatch/v2 v2.4.0
go: downloading k8s.io/apiextensions-apiserver v0.34.0
go: downloading github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
go: downloading golang.org/x/net v0.38.0
go: downloading github.com/spf13/pflag v1.0.6
go: downloading golang.org/x/term v0.30.0
go: downloading github.com/prometheus/client_golang v1.22.0
go: downloading gopkg.in/inf.v0 v0.9.1
go: downloading sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
go: downloading github.com/json-iterator/go v1.1.12
go: downloading go.yaml.in/yaml/v2 v2.4.2
go: downloading k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
go: downloading github.com/google/gnostic-models v0.7.0
go: downloading google.golang.org/protobuf v1.36.5
go: downloading golang.org/x/time v0.9.0
go: downloading github.com/google/btree v1.1.3
go: downloading golang.org/x/sync v0.12.0
go: downloading github.com/fxamacker/cbor/v2 v2.9.0
go: downloading golang.org/x/oauth2 v0.27.0
go: downloading golang.org/x/sys v0.31.0
go: downloading github.com/google/uuid v1.6.0
go: downloading github.com/fsnotify/fsnotify v1.9.0
go: downloading github.com/prometheus/client_model v0.6.1
go: downloading github.com/prometheus/common v0.62.0
go: downloading github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: downloading github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee
go: downloading sigs.k8s.io/yaml v1.6.0
go: downloading go.yaml.in/yaml/v3 v3.0.4
go: downloading github.com/go-openapi/jsonreference v0.20.2
go: downloading github.com/go-openapi/swag v0.23.0
go: downloading github.com/google/go-cmp v0.7.0
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/procfs v0.15.1
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/x448/float16 v0.8.4
go: downloading golang.org/x/text v0.23.0
go: downloading github.com/go-openapi/jsonpointer v0.21.0
go: downloading github.com/mailru/easyjson v0.7.7
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading gopkg.in/evanphx/json-patch.v4 v4.12.0
go: downloading github.com/emicklei/go-restful/v3 v3.12.2
go: downloading github.com/josharian/intern v1.0.0
go: downloading github.com/pkg/errors v0.9.1
INFO Update dependencies 
go: downloading github.com/onsi/ginkgo/v2 v2.22.0
go: downloading github.com/onsi/gomega v1.36.1
go: downloading github.com/stretchr/testify v1.10.0
go: downloading github.com/go-logr/zapr v1.3.0
go: downloading go.uber.org/zap v1.27.0
go: downloading k8s.io/apiserver v0.34.0
go: downloading go.uber.org/goleak v1.3.0
go: downloading go.uber.org/multierr v1.11.0
go: downloading k8s.io/component-base v0.34.0
go: downloading github.com/klauspost/compress v1.18.0
go: downloading github.com/evanphx/json-patch v0.5.2
go: downloading gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
go: downloading github.com/google/gofuzz v1.2.0
go: downloading github.com/google/cel-go v0.26.0
go: downloading github.com/blang/semver/v4 v4.0.0
go: downloading go.opentelemetry.io/otel/trace v1.35.0
go: downloading go.opentelemetry.io/otel v1.35.0
go: downloading github.com/stretchr/objx v0.5.2
go: downloading github.com/kylelemons/godebug v1.1.0
go: downloading github.com/kr/pretty v0.3.1
go: downloading github.com/go-task/slim-sprig/v3 v3.0.0
go: downloading golang.org/x/tools v0.26.0
go: downloading cel.dev/expr v0.24.0
go: downloading google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb
go: downloading github.com/kr/text v0.2.0
go: downloading github.com/rogpeppe/go-internal v1.13.1
go: downloading github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db
go: downloading github.com/stoewer/go-strcase v1.3.0
go: downloading github.com/antlr4-go/antlr/v4 v4.13.0
go: downloading google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb
go: downloading google.golang.org/grpc v1.72.1
go: downloading sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2
go: downloading go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0
go: downloading go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
go: downloading go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0
go: downloading go.opentelemetry.io/otel/sdk v1.34.0
go: downloading golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
go: downloading github.com/spf13/cobra v1.9.1
go: downloading github.com/felixge/httpsnoop v1.0.4
go: downloading go.opentelemetry.io/otel/metric v1.35.0
go: downloading go.opentelemetry.io/proto/otlp v1.5.0
go: downloading github.com/inconshreveable/mousetrap v1.1.0
go: downloading github.com/go-logr/stdr v1.2.2
go: downloading github.com/cenkalti/backoff/v4 v4.3.0
go: downloading github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
go: downloading go.opentelemetry.io/auto/sdk v1.1.0
go: downloading github.com/golang/protobuf v1.5.4
go: downloading go.opentelemetry.io/otel/sdk/metric v1.34.0
Next: define a resource with:
$ kubebuilder create api

第二步:创建新的API(CRD)
这一步会为你创建一个新的自定义资源(CRD)以及对应的控制器框架。

kubebuilder create api --group webapp --version v1 --kind Welcome

当提示创建资源和控制器时,都选择 y (yes)。这个命令会在 api/v1/ 目录下生成 welcome_types.go 文件,这就是你定义CRD规格的地方。

#下面是使用上述命令的一些输出信息
INFO Create Resource [y/n] 
y
INFO Create Controller [y/n] 
y
INFO Writing kustomize manifests for you to edit... 
INFO Writing scaffold for you to edit... 
INFO api/v1/welcome_types.go 
INFO api/v1/groupversion_info.go 
INFO internal/controller/suite_test.go 
INFO internal/controller/welcome_controller.go 
INFO internal/controller/welcome_controller_test.go 
INFO Update dependencies 
INFO Running make 
mkdir -p /root/crd/myopertor/bin
Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.19.0
go: downloading sigs.k8s.io/controller-tools v0.19.0
go: downloading golang.org/x/tools v0.36.0
go: downloading github.com/spf13/pflag v1.0.7
go: downloading k8s.io/code-generator v0.34.0
go: downloading k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f
go: downloading github.com/gobuffalo/flect v1.0.3
go: downloading gopkg.in/yaml.v2 v2.4.0
go: downloading github.com/fatih/color v1.18.0
go: downloading golang.org/x/sync v0.16.0
go: downloading github.com/mattn/go-colorable v0.1.13
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading google.golang.org/protobuf v1.36.7
go: downloading github.com/go-logr/logr v1.4.3
go: downloading golang.org/x/mod v0.27.0
go: downloading golang.org/x/sys v0.35.0
go: downloading golang.org/x/net v0.43.0
go: downloading golang.org/x/text v0.28.0
/root/crd/myopertor/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:
$ make manifests

第三步:定义自定义资源的规范
当前的目录结构如下:

#1 当前目录结构
$ tree /root/crd/myopertor

/root/crd/myopertor
├── api
│   └── v1
│       ├── groupversion_info.go
│       ├── welcome_types.go
│       └── zz_generated.deepcopy.go
├── bin
│   ├── controller-gen -> /root/crd/myopertor/bin/controller-gen-v0.19.0
│   └── controller-gen-v0.19.0
├── cmd
│   └── main.go
├── config
│   ├── crd
│   │   ├── kustomization.yaml
│   │   └── kustomizeconfig.yaml
│   ├── default
│   │   ├── cert_metrics_manager_patch.yaml
│   │   ├── kustomization.yaml
│   │   ├── manager_metrics_patch.yaml
│   │   └── metrics_service.yaml
│   ├── manager
│   │   ├── kustomization.yaml
│   │   └── manager.yaml
│   ├── network-policy
│   │   ├── allow-metrics-traffic.yaml
│   │   └── kustomization.yaml
│   ├── prometheus
│   │   ├── kustomization.yaml
│   │   ├── monitor_tls_patch.yaml
│   │   └── monitor.yaml
│   ├── rbac
│   │   ├── kustomization.yaml
│   │   ├── leader_election_role_binding.yaml
│   │   ├── leader_election_role.yaml
│   │   ├── metrics_auth_role_binding.yaml
│   │   ├── metrics_auth_role.yaml
│   │   ├── metrics_reader_role.yaml
│   │   ├── role_binding.yaml
│   │   ├── role.yaml
│   │   ├── service_account.yaml
│   │   ├── welcome_admin_role.yaml
│   │   ├── welcome_editor_role.yaml
│   │   └── welcome_viewer_role.yaml
│   └── samples
│       ├── kustomization.yaml
│       └── webapp_v1_welcome.yaml
├── Dockerfile
├── go.mod
├── go.sum
├── hack
│   └── boilerplate.go.txt
├── internal
│   └── controller
│       ├── suite_test.go
│       ├── welcome_controller.go
│       └── welcome_controller_test.go
├── Makefile
├── PROJECT
├── README.md
└── test
    ├── e2e
    │   ├── e2e_suite_test.go
    │   └── e2e_test.go
    └── utils
        └── utils.go

18 directories, 46 files

编辑 api/v1/welcome_types.go 文件,在 WelcomeSpec 结构体中添加你需要的字段。这些字段就是用户在YAML文件中配置的参数。

#2 编辑welcome_types.go文件
vim api/v1/welcome_types.go
// WelcomeSpec defines the desired state of Welcome
type WelcomeSpec struct {
    // 在这里添加字段。例如,让用户可以设置显示的名字和服务端口。
    Name string `json:"name"`
    Port int32  `json:"port,omitempty"`
}

同样,你也可以在 WelcomeStatus 结构体中定义状态字段,控制器可以用它来反映资源的当前状态。
定义完成后,需要运行 make install 命令,将你的CRD真正安装到Kubernetes集群中。

第四步:实现控制器的协调逻辑
这是Operator的核心大脑。你需要编辑 internal/controller/welcome_controller.go 文件中的 Reconcile 方法。
Reconcile 方法的目标是:检查系统的当前状态,并将其调整到与CR(“工作指令单”)中描述的期望状态一致。这个过程是声明式API的核心。

以下是一个非常简化的例子,当用户创建一个 Welcome 资源时,控制器会自动为我们部署一个Deployment和一个Service:

func (r *WelcomeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    log := ctrl.Log.FromContext(ctx)

    // 1. 获取当前的Welcome资源实例
    var welcome webappv1.Welcome
    if err := r.Get(ctx, req.NamespacedName, &welcome); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 2. 检查并创建Deployment(如果不存在)
    dep := &appsv1.Deployment{}
    err := r.Get(ctx, types.NamespacedName{Name: welcome.Name, Namespace: req.Namespace}, dep)
    if err != nil && errors.IsNotFound(err) {
        // 定义并创建新的Deployment
        newDep := r.newDeploymentForWelcome(&welcome)
        if err := r.Create(ctx, newDep); err != nil {
            return ctrl.Result{}, err
        }
        log.Info("创建了一个新的Deployment", "Deployment.Namespace", newDep.Namespace, "Deployment.Name", newDep.Name)
    }

    // 3. 检查并创建Service(如果不存在)
    svc := &corev1.Service{}
    err = r.Get(ctx, types.NamespacedName{Name: welcome.Name, Namespace: req.Namespace}, svc)
    if err != nil && errors.IsNotFound(err) {
        // 定义并创建新的Service
        newSvc := r.newServiceForWelcome(&welcome)
        if err := r.Create(ctx, newSvc); err != nil {
            return ctrl.Result{}, err
        }
        log.Info("创建了一个新的Service", "Service.Namespace", newSvc.Namespace, "Service.Name", newSvc.Name)
    }

    return ctrl.Result{}, nil
}

// 辅助函数,根据Welcome规格创建Deployment对象
func (r *WelcomeReconciler) newDeploymentForWelcome(w *webappv1.Welcome) *appsv1.Deployment {
    // ... 这里需要你根据Welcome对象的Spec来构建并返回一个完整的Deployment对象。
    // 例如,可以将 w.Spec.Name 作为环境变量传递给Pod。
}

// 辅助函数,根据Welcome规格创建Service对象
func (r *WelcomeReconciler) newServiceForWelcome(w *webappv1.Welcome) *corev1.Service {
    // ... 这里需要你根据Welcome对象的Spec来构建并返回一个完整的Service对象。
}

第五步:运行与测试

  1. 在集群中运行

    make docker-build docker-push IMG=<你的镜像仓库地址>/myoperator:v1
    make deploy
    
  2. 本地直接运行(用于开发调试)

    make run
    

第六步:使用你的Operator

#部署控制器,这一步需要你本地有k8s环境,它会将你的CRD部署到集群
make install

在集群中部署或本地运行你的Operator控制器后,你就可以创建一个自定义资源实例来使用它了。

# config/samples/webapp_v1_welcome.yaml
apiVersion: webapp.mydomain.com/v1
kind: Welcome
metadata:
  name: welcome-sample
spec:
  name: "Kubernetes Operator test"
  port: 8088

使用 kubectl apply -f config/samples/webapp_v1_welcome.yaml 命令创建资源,然后使用 kubectl get podskubectl get services 观察你的Operator是否成功创建了对应的Deployment和Service。

$ kubectl apply -f config/samples/webapp_v1_welcome.yaml
welcome.webapp.mydomain.com/welcome-sample created

四、💎 总结与进阶

Operator模式是Kubernetes声明式API和控制器模式的强大延伸。对于运维复杂的有状态应用来说,它是一个极具价值的工具。

  • 从使用开始:先熟练使用OperatorHub上的成熟Operator,理解其行为。
  • 选择合适框架:当需要自研时,Kubebuilder(或功能相似的 operator-sdk)是你的首选。
  • 核心是调和循环:编写Operator的关键在于理解并实现好 Reconcile 函数,不断对比"期望状态"和"实际状态",并驱动系统向期望状态收敛。

本文来源于我的微信公众号Linux运维小白,我会持续更新文章,欢迎大家关注,互相交流学习。
在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐