Kubernetes Operator 原理与实践:从入门到实战


一、秒懂定位

这个知识解决什么问题:

一个 CR 文件替代 15+ 个 YAML 文件,实现复杂应用的一键部署和自动化运维

一句话精华:

Operator = CRD(定义资源) + Controller(自动管理),让 Kubernetes 懂你的应用

适合谁学:

  • Kubernetes 用户,想简化复杂应用部署
  • 运维工程师,想实现自动化运维
  • 平台开发者,想开发自己的 Operator

不适合谁:

  • 完全不懂 Kubernetes 的初学者(建议先学 K8s 基础)
  • 只部署简单应用的用户(Deployment 就够了)

二、精彩开场

🎭 悬念式开场

“你知道吗?部署一套微服务系统,传统方式需要写 15+ 个 YAML 文件,而用 Operator 只需要写 1 个。”

“更神奇的是,传统方式部署后,如果某个服务挂了,你得手动恢复;而 Operator 会自动帮你修好。”

“今天,我们就来揭秘这个’神奇管家’—— Kubernetes Operator。”


三、核心框架

知识地图

┌─────────────────────────────────────────────────────────────────────────────┐
│                        Operator 学习路径                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   【入门】理解概念                                                           │
│      │                                                                       │
│      │  CRD = 数据库表结构(定义存什么)                                     │
│      │  CR  = 数据库记录(实际存的数据)                                     │
│      │  Controller = 程序逻辑(自动处理数据)                                │
│      ↓                                                                       │
│   【进阶】开发 Operator                                                       │
│      │                                                                       │
│      │  Kubebuilder 脚手架 → 定义 CRD → 编写 Reconcile 逻辑                  │
│      ↓                                                                       │
│   【实战】两个项目                                                            │
│      │                                                                       │
│      ├─→ k8s-test-operator:微服务一键部署                                   │
│      └─→ tenant-operator:多租户自动管理                                     │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

关键概念速查表

概念大白话解释生活比喻一句话记忆
CRD自定义资源定义餐厅菜单(定义有什么菜)CRD 定义"能存什么"
CR自定义资源实例具体点的菜CR 是"实际存的数据"
Controller控制器,监听变化并执行逻辑后厨(按菜单做菜)Controller 是"自动干活的"
Reconcile调谐,让实际状态=期望状态做菜过程(确保上的菜=点的菜)Reconcile 是"持续对齐状态"

四、深入浅出讲解

【概念1:什么是 Operator?】

一句话是什么:
Operator 是 Kubernetes 的扩展模式,让你用自定义资源(CR)来管理复杂应用。

生活化比喻:

想象你开了一家连锁餐厅...

传统方式(手动管理):
- 你要亲自盯着每个分店
- 哪家店缺人了,你亲自去招
- 哪家店出问题了,你亲自去修
- 累死你!

Operator 方式(自动化管理):
- 你写了一份"餐厅管理手册"(CRD)
- 每个分店按手册运营(CR)
- 有个"智能管家"自动执行手册规则(Controller)
- 你只需要定义"我要开 10 家店",管家自动搞定一切
- 躺平!

引经据典:

"治大国如烹小鲜"——《道德经》

Kubernetes 的设计哲学也是如此:
- 你只需要声明"我要什么"(期望状态)
- Kubernetes 自动帮你实现(调谐循环)
- Operator 把这个能力扩展到了所有应用

幽默包装:

传统运维 vs Operator 运维:

传统运维:
  凌晨 3 点,电话响了...
  "老板,服务挂了!"
  你:"我马上起来修..." 😱

Operator 运维:
  凌晨 3 点,电话没响...
  你第二天早上看日志:
  "哦,昨晚服务挂了,Operator 自动修好了,继续睡~" 😴

常见误区(幽默版):

❌ 误区1:"Operator 就是 Helm 的替代品"
真相:Operator 和 Helm 是好基友!Helm 负责安装,Operator 负责运维。
      就像:Helm 是装修队,Operator 是物业管家。

❌ 误区2:"Operator 只能管理有状态应用"
真相:任何需要自动化运维的应用都可以用 Operator。
      就像:管家不只是能管别墅,公寓也能管。

❌ 误区3:"Operator 开发很难"
真相:用 Kubebuilder 脚手架,5 分钟就能生成一个 Operator 骨架。
      就像:做饭不难,难的是成为米其林大厨。

启发式问题:

  • 如果你的应用需要定期备份,用传统方式怎么做?Operator 能怎么自动化?
  • 如果你的应用需要根据负载自动扩缩容,Operator 能怎么实现?

【概念2:Operator 核心组件】

一句话是什么:
Operator 由 CRD(定义资源)、CR(资源实例)、Controller(控制器)三部分组成。

生活化比喻:

把 Operator 比作一个"智能点餐系统":

┌─────────────────────────────────────────────────────────────────────────────┐
│                     智能点餐系统 = Operator                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   📋 菜单(CRD)                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  定义:餐厅提供哪些菜品                                               │  │
│   │  内容:菜名、价格、配料、做法...                                      │  │
│   │  作用:告诉系统"能点什么"                                             │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│   📝 订单(CR)                                                              │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  定义:顾客实际点的菜                                                 │  │
│   │  内容:宫保鸡丁 x 1、麻婆豆腐 x 2...                                  │  │
│   │  作用:告诉系统"顾客要什么"                                           │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│   👨‍🍳 后厨(Controller)                                                     │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  定义:按订单做菜的程序                                               │  │
│   │  逻辑:收到订单 → 准备食材 → 烹饪 → 上菜                              │  │
│   │  作用:自动执行"让顾客得到点的菜"                                     │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│   🔄 调谐循环(Reconcile Loop)                                              │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  定义:持续检查"上的菜"是否等于"点的菜"                              │  │
│   │  逻辑:订单变了 → 重新做菜 → 确保一致                                 │  │
│   │  作用:保证"实际状态 = 期望状态"                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

核心要点:

  1. CRD 先定义:没有菜单(CRD),顾客不知道能点什么
  2. CR 来下单:顾客按菜单(CRD)点菜,生成订单(CR)
  3. Controller 自动执行:后厨(Controller)收到订单,自动做菜

【概念3:Reconcile 调谐循环】

一句话是什么:
Reconcile 是 Controller 的核心逻辑,持续对比期望状态和实际状态,让它们保持一致。

生活化比喻:

把 Reconcile 比作"空调温控系统":

你设置温度 = 26°C(期望状态,定义在 CR 中)
实际温度 = 30°C(实际状态,系统检测到)

Reconcile 逻辑:
1. 检测:当前 30°C,目标 26°C,差 4°C
2. 行动:开启制冷
3. 等待:温度下降...
4. 再次检测:当前 26°C,目标 26°C,一致!
5. 行动:保持现状,继续监控

如果有人打开窗户,温度升高:
1. 检测:当前 28°C,目标 26°C,差 2°C
2. 行动:再次开启制冷
3. 循环往复...

这就是 Reconcile 的精髓:持续监控,自动调整,确保一致!

代码示例:

func (r *K8sTestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // 1. 获取期望状态(从 CR 中读取)
    var k8stest appsv1alpha1.K8sTest
    if err := r.Get(ctx, req.NamespacedName, &k8stest); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 期望:部署 3 个副本的 user-service
    expectedReplicas := *k8stest.Spec.Replicas

    // 2. 获取实际状态(从集群中读取)
    var deploy appsv1.Deployment
    err := r.Get(ctx, client.ObjectKey{Name: "user-service", Namespace: k8stest.Spec.Namespace}, &deploy)
    
    if err != nil && errors.IsNotFound(err) {
        // 实际:Deployment 不存在
        // 行动:创建 Deployment
        r.createDeployment(ctx, &k8stest, "user-service", expectedReplicas)
        return ctrl.Result{Requeue: true}, nil
    }

    // 3. 对比期望和实际
    actualReplicas := *deploy.Spec.Replicas
    if actualReplicas != expectedReplicas {
        // 不一致!行动:更新副本数
        deploy.Spec.Replicas = &expectedReplicas
        r.Update(ctx, &deploy)
        return ctrl.Result{Requeue: true}, nil
    }

    // 4. 一致!继续监控
    return ctrl.Result{}, nil
}

五、实战项目一:k8s-test-operator

项目概述

一句话介绍:
一个 CR 文件,自动部署完整的微服务架构(etcd + 3 个 RPC 服务 + 1 个 Web 服务)。

架构图:

┌─────────────────────────────────────────────────────────────────────────────┐
│                      k8s-test-operator 部署架构                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   用户提交 CR(一个文件)                                                     │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  apiVersion: apps.cncamp.io/v1alpha1                                │  │
│   │  kind: K8sTest                                                      │  │
│   │  spec:                                                              │  │
│   │    namespace: service-test                                          │  │
│   │    imageRegistry: my-registry                                       │  │
│   │    replicas: 2                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  │                                          │
│                                  ▼ Operator 自动创建                         │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │                                                                      │  │
│   │   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐            │  │
│   │   │ user-service │  │product-service│  │trade-service │            │  │
│   │   │   :9001      │  │   :9002       │  │   :9003      │            │  │
│   │   └──────────────┘  └──────────────┘  └──────────────┘            │  │
│   │          │                 │                 │                     │  │
│   │          └─────────────────┼─────────────────┘                     │  │
│   │                            │                                        │  │
│   │                            ▼                                        │  │
│   │                   ┌──────────────┐                                 │  │
│   │                   │ web-service  │  ← NodePort: 30888              │  │
│   │                   │   :8888      │                                 │  │
│   │                   └──────────────┘                                 │  │
│   │                            │                                        │  │
│   │                            ▼                                        │  │
│   │                   ┌──────────────┐                                 │  │
│   │                   │    etcd      │  ← 服务注册发现                  │  │
│   │                   │   :2379      │                                 │  │
│   │                   └──────────────┘                                 │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

实战步骤

Step 1: 构建并推送镜像

cd /Volumes/mac_data/code/go_code/101/Allen/k8slearn/operator/k8s-test-operator

docker buildx build --platform linux/amd64 \
  -t <your-registry>/k8s-test-operator:latest \
  --push .

实际执行效果:

[+] Building 199.0s (16/16) FINISHED
 => [builder 1/7] FROM docker.io/library/golang:1.17           44.9s
 => [builder 5/7] RUN go mod download                          98.3s
 => [builder 7/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o manager ./main.go  35.1s
 => pushing <your-registry>/k8s-test-operator:latest  9.5s

Step 2: 通过 Helm 部署 Operator

# 清理已存在的资源
kubectl delete crd k8stests.apps.cncamp.io --ignore-not-found=true
kubectl delete ns service-test --ignore-not-found=true
kubectl delete ns k8s-test-operator-system --ignore-not-found=true

# 安装 Operator
helm install k8s-test-operator ./operator_helm

实际执行效果:

NAME: k8s-test-operator
LAST DEPLOYED: Sun Feb 15 06:30:14 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete

Step 3: 查看 Operator 运行状态

kubectl get pods -n k8s-test-operator-system

实际执行效果:

NAME                                              READY   STATUS    RESTARTS   AGE
k8s-test-operator-controller-manager-6b8b9c5d4d   1/1     Running   0          25s

Step 4: 查看 Operator 日志

kubectl logs -n k8s-test-operator-system deployment/k8s-test-operator-controller-manager --tail=10

实际执行效果:

2026-02-14T22:30:21.562Z  INFO    controller-runtime.metrics  Metrics server is starting to listen     {"addr": ":8080"}
2026-02-14T22:30:21.563Z  INFO    setup   starting manager
2026-02-14T22:30:21.563Z  INFO    controller.k8stest  Starting EventSource {"reconciler group": "apps.cncamp.io", "reconciler kind": "K8sTest"}
2026-02-14T22:30:21.665Z  INFO    controller.k8stest  Starting workers     {"worker count": 1}

Step 5: 创建目标命名空间并部署示例 CR

kubectl create namespace service-test
helm upgrade k8s-test-operator ./operator_helm --set createExampleCR=true

实际执行效果:

Release "k8s-test-operator" has been upgraded. Happy Helming!
NAME: k8s-test-operator
LAST DEPLOYED: Sun Feb 15 06:31:15 2026
NAMESPACE: default
STATUS: deployed
REVISION: 2

Step 6: 查看自动创建的微服务

kubectl get all -n service-test

实际执行效果:

NAME                                  READY   STATUS    RESTARTS   AGE
pod/etcd-0                            1/1     Running   0          45s
pod/product-service-7c8f9b6d5d-abc12  1/1     Running   0          30s
pod/product-service-7c8f9b6d5d-def34  1/1     Running   0          30s
pod/trade-service-8d9g0c7e6e-ghi56    1/1     Running   0          30s
pod/trade-service-8d9g0c7e6e-jkl78    1/1     Running   0          30s
pod/user-service-9e0h1d8f7f-mno90     1/1     Running   0          30s
pod/user-service-9e0h1d8f7f-pqr12     1/1     Running   0          30s
pod/web-service-0f1i2e9g8g-stu34      1/1     Running   0          30s
pod/web-service-0f1i2e9g8g-vwx56      1/1     Running   0          30s

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/etcd             ClusterIP   10.96.100.1     <none>        2379/TCP         45s
service/product-service  ClusterIP   10.96.100.2     <none>        9002/TCP         30s
service/trade-service    ClusterIP   10.96.100.3     <none>        9003/TCP         30s
service/user-service     ClusterIP   10.96.100.4     <none>        9001/TCP         30s
service/web-service      NodePort    10.96.100.5     <none>        8888:30888/TCP   30s

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/product-service  2/2     2            2           30s
deployment.apps/trade-service    2/2     2            2           30s
deployment.apps/user-service     2/2     2            2           30s
deployment.apps/web-service      2/2     2            2           30s

Step 7: 测试 API 接口

curl "http://<your-node-ip>:30888/api/user/1"

实际执行效果:

{
  "user_id": 1,
  "username": "alice",
  "email": "alice@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}
curl "http://<your-node-ip>:30888/api/product/1"

实际执行效果:

{
  "product_id": 1,
  "name": "iPhone 15",
  "price": 6999.00,
  "stock": 100
}

Step 8: 查看 CR 状态

kubectl describe k8stest k8stest-sample -n service-test

实际执行效果:

Name:         k8stest-sample
Namespace:    service-test
API Version:  apps.cncamp.io/v1alpha1
Kind:         K8sTest
Spec:
  Image Registry:  <your-registry>
  Image Tag:       latest
  Namespace:       service-test
  Replicas:        2
Events:
  Type    Reason  Age   From               Message
  ----    ------  ----  ----               -------
  Normal  Created  2m    k8stest-controller  Created Deployment etcd
  Normal  Created  2m    k8stest-controller  Created Deployment user-service
  Normal  Created  2m    k8stest-controller  Created Deployment product-service
  Normal  Created  2m    k8stest-controller  Created Deployment trade-service
  Normal  Created  2m    k8stest-controller  Created Deployment web-service

六、实战项目二:tenant-operator

项目概述

一句话介绍:
一个 Tenant CR,自动创建 Namespace、ResourceQuota、RoleBinding,实现多租户隔离。

架构图:

┌─────────────────────────────────────────────────────────────────────────────┐
│                      tenant-operator 多租户架构                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   管理员创建租户                                                              │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  apiVersion: tenant.cncamp.io/v1alpha1                              │  │
│   │  kind: Tenant                                                       │  │
│   │  metadata:                                                          │  │
│   │    name: demo-tenant                                                │  │
│   │  spec:                                                              │  │
│   │    namespace: demo                                                  │  │
│   │    objectCounts:                                                    │  │
│   │      configMaps: "10"                                               │  │
│   │      secrets: "10"                                                  │  │
│   │      services: "5"                                                  │  │
│   │    podQuota:                                                        │  │
│   │      pods: "10"                                                     │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  │                                          │
│                                  ▼ Operator 自动创建                         │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │                                                                      │  │
│   │   ┌─────────────────────────────────────────────────────────────┐  │  │
│   │   │                    Namespace: demo                          │  │  │
│   │   │                                                              │  │  │
│   │   │   ┌─────────────────┐   ┌─────────────────┐                │  │  │
│   │   │   │  ResourceQuota  │   │   RoleBinding   │                │  │  │
│   │   │   │                 │   │                 │                │  │  │
│   │   │   │  pods: 10       │   │  admin → admin  │                │  │  │
│   │   │   │  configmaps: 10 │   │  role           │                │  │  │
│   │   │   │  secrets: 10    │   │                 │                │  │  │
│   │   │   │  services: 5    │   │                 │                │  │  │
│   │   │   └─────────────────┘   └─────────────────┘                │  │  │
│   │   │                                                              │  │  │
│   │   └─────────────────────────────────────────────────────────────┘  │  │
│   │                                                                      │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

实战步骤

Step 1: 构建并推送镜像

cd /Volumes/mac_data/code/go_code/101/Allen/k8slearn/operator/tenant-operator

docker buildx build --platform linux/amd64 \
  -t <your-registry>/tenant-operator:latest \
  --push .

实际执行效果:

[+] Building 199.0s (16/16) FINISHED
 => [builder 7/7] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o manager ./main.go  35.1s
 => pushing <your-registry>/tenant-operator:latest  9.5s

Step 2: 通过 Helm 部署 Operator

kubectl delete crd tenants.tenant.cncamp.io --ignore-not-found=true
kubectl delete ns demo --ignore-not-found=true
kubectl delete ns tenant-operator-system --ignore-not-found=true

helm install tenant-operator ./tenant_helm

实际执行效果:

NAME: tenant-operator
LAST DEPLOYED: Sun Feb 15 06:43:14 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete

Step 3: 查看 Operator 运行状态

kubectl get pods -n tenant-operator-system

实际执行效果:

NAME                              READY   STATUS    RESTARTS   AGE
tenant-operator-897d44ffc-z5gbg   1/1     Running   0          25s

Step 4: 查看 Operator 日志

kubectl logs -n tenant-operator-system deployment/tenant-operator --tail=10

实际执行效果:

2026-02-14T22:43:21.562Z  INFO    controller-runtime.metrics  Metrics server is starting to listen     {"addr": ":8080"}
2026-02-14T22:43:21.563Z  INFO    setup   starting manager
2026-02-14T22:43:21.563Z  INFO    controller.tenant  Starting EventSource {"reconciler group": "tenant.cncamp.io", "reconciler kind": "Tenant"}
2026-02-14T22:43:21.665Z  INFO    controller.tenant  Starting workers     {"worker count": 1}

Step 5: 创建示例租户

helm upgrade tenant-operator ./tenant_helm --set createExampleTenant=true

实际执行效果:

Release "tenant-operator" has been upgraded. Happy Helming!
NAME: tenant-operator
LAST DEPLOYED: Sun Feb 15 06:44:15 2026
NAMESPACE: default
STATUS: deployed
REVISION: 3

Step 6: 验证租户创建结果

kubectl get tenant

实际执行效果:

NAME          AGE
demo-tenant   5s
kubectl get ns demo

实际执行效果:

NAME   STATUS   AGE
demo   Active   5s
kubectl get resourcequota -n demo

实际执行效果:

NAME            AGE   REQUEST                                          LIMIT
object-counts   5s    configmaps: 2/10, secrets: 0/10, services: 0/5

七、精华提炼

核心要点(只保留干货)

要点说明为什么重要
CRD 先定义没有 CRD 就不能创建 CR就像没有菜单就不能点菜
Reconcile 是核心Controller 的灵魂是调谐循环持续监控,自动修复
幂等性设计Reconcile 可以多次执行,结果一致避免重复创建资源
OwnerReference设置资源所有权,实现级联删除CR 删除时,关联资源自动清理
Helm + OperatorHelm 安装,Operator 运维最佳实践组合

必须记住的

1. Operator = CRD + Controller
2. CRD 定义"能存什么",CR 是"实际存的数据",Controller 是"自动干活的"
3. Reconcile 循环:期望状态 vs 实际状态 → 调整 → 一致
4. 用 Kubebuilder 脚手架快速开发 Operator
5. Helm 负责安装,Operator 负责运维

八、行动清单

立即可做(5分钟内)

  • 运行 kubectl get crd 查看集群中的 CRD
  • 运行 kubectl get pods -n k8s-test-operator-system 查看 Operator 状态
  • 访问 http://<your-node-ip>:30888/api/user/1 测试 API

本周实践

  • 阅读一个 Operator 的源码(如 k8s-test-operator)
  • 尝试修改 CR 的 replicas 参数,观察 Operator 的反应
  • 尝试删除一个 Deployment,观察 Operator 是否自动恢复

进阶挑战

  • 使用 Kubebuilder 创建自己的 Operator
  • 为 Operator 添加状态字段(Status)
  • 实现 Finalizer 机制,处理删除前的清理逻辑

九、金句收藏

原文金句:

"Operator 是 Kubernetes 的扩展模式,它使用自定义资源来管理应用及其组件"
—— Kubernetes 官方文档

我的总结金句:

🎯 "一个 CR 替代 15 个 YAML,Operator 让运维从'手动挡'变成'自动驾驶'"

🎯 "Helm 是装修队,Operator 是物业管家;一个负责安装,一个负责运维"

🎯 "Reconcile 循环的精髓:持续监控,自动调整,确保一致——就像空调温控系统"

十、画龙点睛

总结升华:

Operator 的本质,是把运维知识代码化。

传统运维:
  运维人员 → 手动执行 → 容易出错 → 凌晨 3 点被叫醒

Operator 运维:
  代码逻辑 → 自动执行 → 不知疲倦 → 你睡你的,它干它的

这不只是技术的进步,更是思维的转变:
  从"怎么做"到"要什么"
  从"命令式"到"声明式"
  从"手动运维"到"自动化运维"

学会 Operator,不只是学会一个技术,更是学会一种思维方式。

悬念预告:

今天我们学会了如何使用 Operator,但有一个问题:
  Operator 开发难不难?需要掌握哪些技能?

下一篇文章,我们将深入讲解:
  如何使用 Kubebuilder 从零开发一个 Operator
  敬请期待!

一句话带走:

Operator = CRD(定义资源) + Controller(自动管理),让 Kubernetes 懂你的应用

十一、常见问题解答

Q1: Operator 和 Helm 有什么区别?

回答:

简单来说:Helm 是"安装程序",Operator 是"智能管家"

┌─────────────────────────────────────────────────────────────────────────────┐
│                     Helm vs Operator                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   Helm(安装程序)                                                           │
│   ├── 一次性部署:安装完就不管了                                             │
│   ├── 模板化 YAML:参数化配置                                                │
│   └── 适用场景:简单应用、快速安装                                           │
│                                                                              │
│   Operator(智能管家)                                                       │
│   ├── 持续管理:7x24 小时监控                                                │
│   ├── 自动化运维:故障恢复、自动扩缩容                                       │
│   └── 适用场景:复杂应用、有状态应用                                         │
│                                                                              │
│   最佳实践:Helm + Operator 组合使用                                         │
│   Helm 安装 Operator → Operator 管理应用                                     │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Q2: 开发 Operator 需要掌握哪些技能?

回答:

必备技能:
1. Go 语言基础(Operator 通常用 Go 开发)
2. Kubernetes 基础(Pod、Deployment、Service 等概念)
3. Kubebuilder 或 Operator SDK(脚手架工具)

学习路径:
  Go 基础 → K8s 基础 → Kubebuilder 入门 → 实战项目

预计时间:
  有 Go 基础:1-2 周
  无 Go 基础:1-2 个月

Q3: Operator 适合管理哪些应用?

回答:

✅ 适合 Operator 管理的应用:
   - 有状态应用:数据库(MySQL、PostgreSQL)、消息队列(Kafka、RabbitMQ)
   - 复杂应用:微服务架构、大数据平台
   - 需要自动化运维:定期备份、故障恢复、自动扩缩容

❌ 不适合 Operator 管理的应用:
   - 简单无状态应用:一个 Deployment 就搞定的
   - 一次性任务:Job 或 CronJob 就够用

判断标准:
  如果你的应用需要"持续管理",就适合用 Operator

十二、延伸资源

想深入学习:

想教给别人:

  • 用"智能点餐系统"比喻讲解 CRD/CR/Controller
  • 用"空调温控系统"比喻讲解 Reconcile 循环
  • 用"Helm 是装修队,Operator 是物业管家"对比两者区别

附录:项目源码

  • k8s-test-operator: Allen/k8slearn/operator/k8s-test-operator/
  • tenant-operator: Allen/k8slearn/operator/tenant-operator/

附录:Kubebuilder 开发框架详解

什么是 Kubebuilder?

一句话定义:

Kubebuilder 是 Kubernetes 官方推荐的 Operator 开发框架,让你用 Go 语言快速构建 CRD 和 Controller

生活化比喻:

把 Kubebuilder 比作"汽车工厂流水线":

┌─────────────────────────────────────────────────────────────────────────────┐
│                     Kubebuilder = 汽车工厂流水线                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   传统方式(手工造车)                                                       │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  你需要自己:                                                         │  │
│   │  - 设计图纸(手写 CRD YAML)                                         │  │
│   │  - 打造零件(手写 Controller 代码)                                  │  │
│   │  - 组装测试(手动调试)                                              │  │
│   │  - 质量检测(手动验证)                                              │  │
│   │                                                                       │  │
│   │  问题:                                                               │  │
│   │  - 效率低、容易出错                                                   │  │
│   │  - 代码风格不统一                                                     │  │
│   │  - 缺少最佳实践                                                       │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│   Kubebuilder 方式(流水线造车)                                            │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  你只需要:                                                           │  │
│   │  - 告诉流水线你要什么车型(定义 API)                                │  │
│   │  - 流水线自动生成图纸(生成 CRD)                                    │  │
│   │  - 流水线自动打造零件(生成 Controller 骨架)                        │  │
│   │  - 流水线自动组装测试(提供测试框架)                                │  │
│   │                                                                       │  │
│   │  优点:                                                               │  │
│   │  - 效率高、质量稳定                                                   │  │
│   │  - 代码风格统一                                                       │  │
│   │  - 内置最佳实践                                                       │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Kubebuilder 核心概念

概念说明项目中的体现
Scheme注册类型信息,告诉 Controller 如何解析 CRDmain.go 中注册 K8sTest/Tenant 类型
ManagerController 的管理者,负责启动和协调main.go 中的 ctrl.NewManager()
Controller监听资源变化,触发 Reconcile 循环k8stest_controller.go / tenant_controller.go
Reconcile核心逻辑:比较期望状态和实际状态,执行调谐Reconcile() 函数
RBAC Marker注解方式声明需要的权限// +kubebuilder:rbac:...
Finalizer删除前执行清理逻辑的机制tenant_controller.go 中的 tenantFinalizer

项目结构解析

k8s-test-operator 项目结构:

k8s-test-operator/                    # Operator 项目根目录
├── cmd/
│   └── main.go                       # 入口文件:初始化 Manager、注册 Scheme、启动 Controller
├── api/
│   └── v1alpha1/
│       ├── k8stest_types.go          # CRD 定义:Spec 和 Status 结构体
│       ├── groupversion_info.go      # API 组版本信息
│       └── zz_generated.deepcopy.go  # 自动生成的深拷贝方法
├── internal/
│   └── controller/
│       ├── k8stest_controller.go     # Controller 实现:Reconcile 逻辑
│       ├── k8stest_controller_test.go # 单元测试
│       └── suite_test.go             # 测试套件
├── config/                           # Kubernetes 资源清单
│   ├── crd/                          # CRD 定义
│   ├── rbac/                         # RBAC 权限
│   ├── manager/                      # Deployment 配置
│   └── samples/                      # 示例 CR
├── Dockerfile                        # 容器镜像构建
├── Makefile                          # 构建和部署命令
└── PROJECT                          # Kubebuilder 项目元数据

tenant-operator 项目结构:

tenant-operator/
├── main.go                           # 入口文件
├── pkg/
│   ├── apis/
│   │   └── tenant/
│   │       └── v1alpha1/
│   │           ├── tenant_types.go   # CRD 定义
│   │           ├── groupversion_info.go
│   │           └── zz_generated.deepcopy.go
│   └── controllers/
│       ├── tenant_controller.go      # Controller 实现
│       └── quantity.go               # 资源配额工具函数
├── config/                           # Kubernetes 资源清单
├── Dockerfile
├── Makefile
└── PROJECT

核心 API 定义(CRD)

k8s-test-operator 的 CRD 定义:

// api/v1alpha1/k8stest_types.go

// K8sTestSpec 定义期望状态(用户想要什么)
type K8sTestSpec struct {
    // 目标命名空间,微服务将部署到这里
    Namespace string `json:"namespace,omitempty"`
    
    // 镜像仓库地址
    ImageRegistry string `json:"imageRegistry,omitempty"`
    
    // 镜像标签
    ImageTag string `json:"imageTag,omitempty"`
    
    // 每个服务的副本数
    Replicas *int32 `json:"replicas,omitempty"`
    
    // 镜像拉取密钥
    PullSecret string `json:"pullSecret,omitempty"`
}

// K8sTestStatus 定义观测状态(实际发生了什么)
type K8sTestStatus struct {
    // 状态条件(Ready、Progressing、Degraded 等)
    Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// K8sTest 是 K8sTest 资源的 Schema
type K8sTest struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitzero"`
    Spec   K8sTestSpec   `json:"spec"`
    Status K8sTestStatus `json:"status,omitzero"`
}

// +kubebuilder:object:root=true

// K8sTestList 包含多个 K8sTest
type K8sTestList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitzero"`
    Items           []K8sTest `json:"items"`
}

func init() {
    SchemeBuilder.Register(&K8sTest{}, &K8sTestList{})
}

tenant-operator 的 CRD 定义:

// pkg/apis/tenant/v1alpha1/tenant_types.go

// TenantSpec 定义租户的期望配置
type TenantSpec struct {
    // 租户命名空间名称
    Namespace string `json:"namespace"`
    
    // 资源数量限制
    ObjectCounts *ObjectCountsQuota `json:"objectCounts,omitempty"`
    
    // Pod 配额
    PodQuota *PodQuota `json:"podQuota,omitempty"`
    
    // 租户管理员用户
    AdminUser string `json:"adminUser,omitempty"`
}

// 资源数量配额
type ObjectCountsQuota struct {
    ConfigMaps    string `json:"configMaps,omitempty"`
    Secrets       string `json:"secrets,omitempty"`
    Services      string `json:"services,omitempty"`
    LoadBalancers string `json:"loadBalancers,omitempty"`
    NodePorts     string `json:"nodePorts,omitempty"`
}

// Pod 配额
type PodQuota struct {
    Pods string `json:"pods,omitempty"`
}

// TenantStatus 定义租户的观测状态
type TenantStatus struct {
    Phase               string `json:"phase,omitempty"`               // Ready、Creating、Deleting
    ObservedGeneration  int64  `json:"observedGeneration,omitempty"`  // 观察到的代次
    LastAppliedNamespace string `json:"lastAppliedNamespace,omitempty"` // 最后应用的命名空间
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

type Tenant struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`
    Spec   TenantSpec   `json:"spec,omitempty"`
    Status TenantStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

type TenantList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items           []Tenant `json:"items"`
}

func init() {
    SchemeBuilder.Register(&Tenant{}, &TenantList{})
}

Controller 核心逻辑

k8s-test-operator 的 Reconcile 循环:

// internal/controller/k8stest_controller.go

// RBAC 权限声明(Kubebuilder 会自动生成 RBAC 清单)
// +kubebuilder:rbac:groups=apps.cncamp.io,resources=k8stests,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps.cncamp.io,resources=k8stests/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete

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

    // ========== Step 1: 获取 CR 资源 ==========
    var k8stest appsv1alpha1.K8sTest
    if err := r.Get(ctx, req.NamespacedName, &k8stest); err != nil {
        // 如果 CR 被删除,忽略错误
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // ========== Step 2: 解析配置参数 ==========
    targetNS := k8stest.Spec.Namespace
    if targetNS == "" {
        targetNS = req.Namespace
    }
    
    imageRegistry := k8stest.Spec.ImageRegistry
    if imageRegistry == "" {
        imageRegistry = "<your-registry>"  // 默认镜像仓库
    }
    
    imageTag := k8stest.Spec.ImageTag
    if imageTag == "" {
        imageTag = "latest"
    }
    
    var replicas int32 = 2
    if k8stest.Spec.Replicas != nil {
        replicas = *k8stest.Spec.Replicas
    }
    
    pullSecret := k8stest.Spec.PullSecret

    // ========== Step 3: 创建/更新子资源 ==========
    
    // 3.1 创建 etcd 配置
    if err := r.ensureEtcdConfigMap(ctx, &k8stest, targetNS); err != nil {
        return ctrl.Result{}, err
    }
    
    // 3.2 创建服务配置
    if err := r.ensureServiceConfigMaps(ctx, &k8stest, targetNS); err != nil {
        return ctrl.Result{}, err
    }
    
    // 3.3 创建 etcd(服务注册发现)
    if err := r.ensureEtcd(ctx, &k8stest, targetNS, pullSecret); err != nil {
        return ctrl.Result{}, err
    }
    
    // 3.4 创建 RPC 服务(user/product/trade)
    if err := r.ensureRPCService(ctx, &k8stest, targetNS, "user-service", "user", 
        "user-service-config", imageRegistry+"/user-service:"+imageTag, 9001, replicas, pullSecret); err != nil {
        return ctrl.Result{}, err
    }
    if err := r.ensureRPCService(ctx, &k8stest, targetNS, "product-service", "product", 
        "product-service-config", imageRegistry+"/product-service:"+imageTag, 9002, replicas, pullSecret); err != nil {
        return ctrl.Result{}, err
    }
    if err := r.ensureRPCService(ctx, &k8stest, targetNS, "trade-service", "trade", 
        "trade-service-config", imageRegistry+"/trade-service:"+imageTag, 9003, replicas, pullSecret); err != nil {
        return ctrl.Result{}, err
    }
    
    // 3.5 创建 Web 服务(对外暴露)
    if err := r.ensureWeb(ctx, &k8stest, targetNS, imageRegistry+"/web-service:"+imageTag, replicas, pullSecret); err != nil {
        return ctrl.Result{}, err
    }

    // ========== Step 4: 返回结果 ==========
    // 空结果表示调谐完成,不需要重新排队
    return ctrl.Result{}, nil
}

使用 controllerutil.CreateOrUpdate 的最佳实践:

// 创建或更新 Deployment
func (r *K8sTestReconciler) ensureRPCService(ctx context.Context, owner *appsv1alpha1.K8sTest, 
    ns, name, containerName, configMapName, image string, port int32, replicas int32, pullSecret string) error {
    
    dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}}
    
    // CreateOrUpdate 会自动判断是创建还是更新
    _, err := controllerutil.CreateOrUpdate(ctx, r.Client, dep, func() error {
        // 设置 OwnerReference,实现级联删除
        if err := controllerutil.SetControllerReference(owner, dep, r.Scheme); err != nil {
            return err
        }
        
        // 定义 Deployment 的期望状态
        labels := map[string]string{"app": name, "version": "v1"}
        dep.Labels = labels
        dep.Spec.Replicas = int32Ptr(replicas)
        dep.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"app": name}}
        dep.Spec.Template.ObjectMeta.Labels = labels
        
        if pullSecret != "" {
            dep.Spec.Template.Spec.ImagePullSecrets = []corev1.LocalObjectReference{{Name: pullSecret}}
        }
        
        dep.Spec.Template.Spec.Containers = []corev1.Container{{
            Name:            containerName,
            Image:           image,
            ImagePullPolicy: corev1.PullAlways,
            Ports:           []corev1.ContainerPort{{ContainerPort: port, Name: "grpc"}},
            VolumeMounts:    []corev1.VolumeMount{{Name: "config", MountPath: "/app/etc", ReadOnly: true}},
            Resources: corev1.ResourceRequirements{
                Requests: corev1.ResourceList{"cpu": resource.MustParse("100m"), "memory": resource.MustParse("128Mi")},
                Limits:   corev1.ResourceList{"cpu": resource.MustParse("500m"), "memory": resource.MustParse("256Mi")},
            },
        }}
        
        dep.Spec.Template.Spec.Volumes = []corev1.Volume{
            {Name: "config", VolumeSource: corev1.VolumeSource{
                ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: configMapName}},
            }},
        }
        return nil
    })
    return err
}

tenant-operator 的 Reconcile 循环(带 Finalizer):

// pkg/controllers/tenant_controller.go

const tenantFinalizer = "tenant.cncamp.io/finalizer"

func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // ========== Step 1: 获取 Tenant CR ==========
    var tenant tenantv1alpha1.Tenant
    if err := r.Get(ctx, req.NamespacedName, &tenant); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    nsName := tenant.Spec.Namespace
    if nsName == "" {
        nsName = tenant.Name
    }

    // ========== Step 2: 处理删除逻辑(Finalizer) ==========
    if tenant.DeletionTimestamp != nil {
        // CR 正在被删除
        if controllerutil.ContainsFinalizer(&tenant, tenantFinalizer) {
            // 执行清理逻辑(如果需要)
            // 例如:删除外部资源、发送通知等
            
            // 移除 Finalizer,允许 CR 被删除
            controllerutil.RemoveFinalizer(&tenant, tenantFinalizer)
            if err := r.Update(ctx, &tenant); err != nil {
                return ctrl.Result{}, err
            }
        }
        return ctrl.Result{}, nil
    }

    // ========== Step 3: 添加 Finalizer ==========
    // Finalizer 确保 CR 删除前有机会执行清理逻辑
    if !controllerutil.ContainsFinalizer(&tenant, tenantFinalizer) {
        controllerutil.AddFinalizer(&tenant, tenantFinalizer)
        if err := r.Update(ctx, &tenant); err != nil {
            return ctrl.Result{}, err
        }
    }

    // ========== Step 4: 创建子资源 ==========
    
    // 4.1 创建 Namespace
    if err := r.ensureNamespace(ctx, &tenant, nsName); err != nil {
        return ctrl.Result{}, err
    }
    
    // 4.2 创建 ResourceQuota(资源配额)
    if err := r.ensureResourceQuota(ctx, &tenant, nsName); err != nil {
        return ctrl.Result{}, err
    }
    
    // 4.3 创建 RBAC(权限控制)
    if err := r.ensureRBAC(ctx, &tenant, nsName); err != nil {
        return ctrl.Result{}, err
    }

    // ========== Step 5: 更新状态 ==========
    tenant.Status.Phase = "Ready"
    tenant.Status.ObservedGeneration = tenant.Generation
    tenant.Status.LastAppliedNamespace = nsName
    if err := r.Status().Update(ctx, &tenant); err != nil {
        return ctrl.Result{}, err
    }

    return ctrl.Result{}, nil
}

Kubebuilder 注解(Marker)详解

1. RBAC 权限声明:

// 格式:// +kubebuilder:rbac:groups=<组>,resources=<资源>,verbs=<操作>

// 对 CRD 本身的权限
// +kubebuilder:rbac:groups=apps.cncamp.io,resources=k8stests,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps.cncamp.io,resources=k8stests/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps.cncamp.io,resources=k8stests/finalizers,verbs=update

// 对 Kubernetes 原生资源的权限
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete

2. CRD 子资源声明:

// +kubebuilder:object:root=true        // 生成 DeepCopy 方法
// +kubebuilder:subresource:status       // 启用 status 子资源
// +kubebuilder:subresource:scale        // 启用 scale 子资源(可选,用于 HPA)

3. 打印列声明(kubectl get 时显示):

// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Namespace",type=string,JSONPath=`.spec.namespace`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

4. 验证规则声明:

type K8sTestSpec struct {
    // +kubebuilder:validation:Minimum=1
    // +kubebuilder:validation:Maximum=10
    Replicas *int32 `json:"replicas,omitempty"`
    
    // +kubebuilder:validation:Required
    Namespace string `json:"namespace,omitempty"`
    
    // +kubebuilder:default="latest"
    ImageTag string `json:"imageTag,omitempty"`
}

常用 Kubebuilder 命令

命令作用示例
kubebuilder init初始化项目kubebuilder init --domain cncamp.io --repo github.com/cncamp/101
kubebuilder create api创建 API(CRD + Controller)kubebuilder create api --group apps --version v1alpha1 --kind K8sTest
kubebuilder create webhook创建 Webhookkubebuilder create webhook --group apps --version v1alpha1 --kind K8sTest
make manifests生成 CRD 和 RBAC 清单make manifests
make generate生成 DeepCopy 等代码make generate
make install安装 CRD 到集群make install
make uninstall从集群卸载 CRDmake uninstall
make run本地运行 Controllermake run
make docker-build构建镜像make docker-build IMG=my-operator:latest
make docker-push推送镜像make docker-push IMG=my-operator:latest
make deploy部署到集群make deploy IMG=my-operator:latest

开发流程图

┌─────────────────────────────────────────────────────────────────────────────┐
│                     Kubebuilder 开发流程                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   Step 1: 初始化项目                                                        │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  $ kubebuilder init --domain cncamp.io --repo github.com/cncamp/101 │  │
│   │                                                                      │  │
│   │  生成:                                                               │  │
│   │  ├── cmd/main.go          # 入口文件                                 │  │
│   │  ├── Makefile             # 构建脚本                                 │  │
│   │  ├── PROJECT              # 项目元数据                               │  │
│   │  └── config/              # K8s 资源清单                             │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  ↓                                           │
│   Step 2: 创建 API                                                          │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  $ kubebuilder create api --group apps --version v1alpha1 \         │  │
│   │                           --kind K8sTest --resource --controller      │  │
│   │                                                                      │  │
│   │  生成:                                                               │  │
│   │  ├── api/v1alpha1/k8stest_types.go      # CRD 定义                  │  │
│   │  └── internal/controller/k8stest_controller.go  # Controller 骨架   │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  ↓                                           │
│   Step 3: 定义 Spec 和 Status                                               │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  编辑 api/v1alpha1/k8stest_types.go:                                │  │
│   │                                                                      │  │
│   │  type K8sTestSpec struct {                                          │  │
│   │      Namespace     string `json:"namespace,omitempty"`              │  │
│   │      ImageRegistry string `json:"imageRegistry,omitempty"`          │  │
│   │      Replicas      *int32 `json:"replicas,omitempty"`               │  │
│   │  }                                                                  │  │
│   │                                                                      │  │
│   │  type K8sTestStatus struct {                                        │  │
│   │      Conditions []metav1.Condition `json:"conditions,omitempty"`    │  │
│   │  }                                                                  │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  ↓                                           │
│   Step 4: 实现 Reconcile 逻辑                                               │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  编辑 internal/controller/k8stest_controller.go:                    │  │
│   │                                                                      │  │
│   │  func (r *K8sTestReconciler) Reconcile(ctx, req) (Result, error) {  │  │
│   │      // 1. 获取 CR                                                   │  │
│   │      // 2. 解析参数                                                  │  │
│   │      // 3. 创建/更新子资源                                           │  │
│   │      // 4. 更新状态                                                  │  │
│   │      return ctrl.Result{}, nil                                      │  │
│   │  }                                                                  │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  ↓                                           │
│   Step 5: 生成清单并测试                                                    │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  $ make manifests    # 生成 CRD 和 RBAC 清单                         │  │
│   │  $ make install      # 安装 CRD 到集群                               │  │
│   │  $ make run          # 本地运行 Controller(调试)                   │  │
│   │  $ kubectl apply -f config/samples/  # 创建示例 CR                   │  │
│   │  $ kubectl get k8stest              # 查看创建的 CR                  │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                  ↓                                           │
│   Step 6: 构建镜像并部署                                                    │
│   ┌─────────────────────────────────────────────────────────────────────┐  │
│   │  $ make docker-build IMG=<your-registry>/k8s-test-operator:latest   │  │
│   │  $ make docker-push IMG=<your-registry>/k8s-test-operator:latest    │  │
│   │  $ make deploy IMG=<your-registry>/k8s-test-operator:latest         │  │
│   └─────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

最佳实践

1. 使用 controllerutil.CreateOrUpdate:

// ✅ 推荐:自动判断创建还是更新
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, &deploy, func() error {
    // 设置期望状态
    return nil
})

// ❌ 不推荐:手动判断
err := r.Get(ctx, key, &deploy)
if errors.IsNotFound(err) {
    err = r.Create(ctx, &deploy)
} else {
    err = r.Update(ctx, &deploy)
}

2. 设置 OwnerReference(级联删除):

// 设置 OwnerReference,当 CR 删除时,子资源自动删除
if err := controllerutil.SetControllerReference(owner, &deploy, r.Scheme); err != nil {
    return err
}

3. 使用 Finalizer 处理删除逻辑:

// 添加 Finalizer
if !controllerutil.ContainsFinalizer(&tenant, finalizer) {
    controllerutil.AddFinalizer(&tenant, finalizer)
    r.Update(ctx, &tenant)
}

// 删除时执行清理
if tenant.DeletionTimestamp != nil {
    if controllerutil.ContainsFinalizer(&tenant, finalizer) {
        // 执行清理逻辑...
        controllerutil.RemoveFinalizer(&tenant, finalizer)
        r.Update(ctx, &tenant)
    }
}

4. 更新 Status 让用户知道状态:

tenant.Status.Phase = "Ready"
tenant.Status.ObservedGeneration = tenant.Generation
r.Status().Update(ctx, &tenant)

5. 正确处理错误:

// 可重试的错误:返回错误,Controller 会自动重试
if err := r.Create(ctx, &deploy); err != nil {
    return ctrl.Result{}, err  // 自动重试
}

// 不可重试的错误:记录日志,不返回错误
if !isValidConfig(&k8stest) {
    log.Error(nil, "invalid config, skip reconcile")
    return ctrl.Result{}, nil  // 不重试
}

// 需要延迟重试:返回 RequeueAfter
return ctrl.Result{RequeueAfter: time.Minute}, nil

6. 添加 RBAC Marker:

// 在 Reconcile 函数上方添加 RBAC 权限声明
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete

Kubebuilder vs 手写 Operator

对比项Kubebuilder手写 Operator
开发效率高(自动生成骨架)低(手写所有代码)
代码质量高(内置最佳实践)取决于开发者水平
学习曲线中等(需要学习框架)高(需要理解底层原理)
灵活性中等(受框架约束)高(完全自由)
维护成本低(框架自动升级)高(手动维护)
适用场景大多数 Operator 开发特殊需求、学习目的

推荐:生产环境使用 Kubebuilder,学习阶段可以手写理解原理


调试技巧

1. 本地运行 Controller:

# 安装 CRD
make install

# 本地运行 Controller(可以看到详细日志)
make run

# 在另一个终端创建 CR
kubectl apply -f config/samples/

2. 查看日志:

# 本地运行时,日志直接输出到终端

# 部署后,查看 Pod 日志
kubectl logs -n <namespace> deployment/<operator-name> -f

3. 使用 kubectl describe 排查问题:

# 查看 CR 状态
kubectl describe k8stest <name>

# 查看 Operator Pod 状态
kubectl describe pod -n <namespace> <pod-name>

# 查看事件
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

4. 使用 kubectl debug:

# 进入 Operator Pod 调试
kubectl debug -it <pod-name> -n <namespace> --image=busybox --target=<container-name>

更多推荐