四、控制平面设计:API Server设计、资源抽象(CRD)、控制器模式(Operator)

好,咱们进入控制平面的核心部分。说实话,很多PaaS平台做不好,问题就出在控制平面上——要么API设计得乱七八糟,要么资源抽象不够灵活,要么控制器写成了“面条代码”。今天我就把这三大块掰开揉碎了讲清楚。

4.1 API Server设计:平台的“神经系统”

API Server是整个控制平面的入口。你想想看,所有客户端、CLI工具、Web控制台,甚至其他组件,都要通过它来操作资源。所以它的设计质量,直接决定了平台的可用性和扩展性。

核心设计原则:

  • 声明式API:用户告诉系统“我要什么”,而不是“怎么做”。比如用户提交一个“创建MySQL实例”的请求,而不是“先装MySQL,再配参数,再启动”。
  • 幂等性:同一个请求发多次,结果是一样的。我见过一个平台因为没处理好幂等性,用户重试时创建了两个数据库实例,那叫一个尴尬。
  • 版本化:API版本要清晰,比如/api/v1/databases。别搞什么/api/databases然后偷偷改字段,前端会骂娘的。

我个人习惯的做法:

API Server采用分层架构:路由层 → 认证鉴权层 → 校验层 → 业务逻辑层 → 存储层。每一层职责单一,出了问题也好排查。

来看一个简单的API设计示例:

// 创建数据库实例的API定义
POST /api/v1/databases
{
  "apiVersion": "v1",
  "kind": "Database",
  "metadata": {
    "name": "my-mysql",
    "namespace": "production"
  },
  "spec": {
    "engine": "mysql",
    "version": "8.0",
    "storage": "100Gi",
    "replicas": 3,
    "backup": {
      "enabled": true,
      "schedule": "0 2 * * *"
    }
  }
}

嗯,这里要注意:不要把所有字段都放在spec里。metadata放标识信息,spec放期望状态,status放实际状态。这是Kubernetes教我们的好习惯。

4.2 资源抽象(CRD):把数据库变成“一等公民”

资源抽象是PaaS平台的灵魂。说白了,就是把数据库、缓存、消息队列这些中间件,抽象成平台内部的“资源对象”。

为什么需要CRD?

  • 标准化:所有数据库实例都长一个样,操作方式统一
  • 可扩展:新增一种数据库,只需要定义新的CRD,不用改核心代码
  • 自动化:配合控制器,实现“声明即所得”

我在项目中遇到过一个问题:一开始我们把所有数据库配置都写死在代码里,结果每次新增数据库类型都要改核心逻辑,测试回归一遍,累得半死。后来改成CRD方式,新增一种数据库只需要写一个CRD定义和一个控制器,爽多了。

来看一个MySQL数据库的CRD定义示例:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.paas.example.com
spec:
  group: paas.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                engine:
                  type: string
                  enum: ["mysql", "postgresql", "redis"]
                version:
                  type: string
                storage:
                  type: string
                  pattern: "^[0-9]+(Gi|Ti)$"
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                backup:
                  type: object
                  properties:
                    enabled:
                      type: boolean
                    schedule:
                      type: string
  scope: Namespaced
  names:
    plural: databases
    singular: database
    kind: Database
    shortNames:
      - db

避坑指南:我曾经把CRD的校验规则写得太松,结果用户传了个负数副本数,系统直接崩溃。后来我养成了习惯:所有字段都要做校验,尤其是数值和枚举类型。OpenAPI Schema的校验能力很强,别浪费了。

4.3 控制器模式(Operator):让资源“活”起来

有了CRD定义,资源还只是个“死”的YAML文件。控制器的作用,就是让这些资源“活”起来——监听资源变化,然后执行对应的操作。

控制器的核心逻辑:

  1. 监听:Watch API Server上的资源变化
  2. 比对:对比当前状态和期望状态
  3. 调和:执行操作,让当前状态趋近期望状态
  4. 更新:更新资源的状态字段

说白了,就是一个无限循环的“观察-决策-执行”过程。你想想看,用户提交了一个“创建3副本MySQL”的请求,控制器看到后,就去检查当前有没有3个Pod在跑,没有就创建,多了就删除,一直保持3个。

来看一个简化版的控制器代码:

// 数据库控制器的调和循环
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // 1. 获取资源对象
    db := &Database{}
    if err := r.Get(ctx, req.NamespacedName, db); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 2. 检查当前状态
    currentReplicas := r.getCurrentReplicas(db)
    desiredReplicas := db.Spec.Replicas

    // 3. 执行调和操作
    if currentReplicas < desiredReplicas {
        // 创建新的Pod
        r.createPod(db)
    } else if currentReplicas > desiredReplicas {
        // 删除多余的Pod
        r.deletePod(db)
    }

    // 4. 更新状态
    db.Status.ReadyReplicas = currentReplicas
    r.Status().Update(ctx, db)

    return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}

注意:控制器一定要处理好“重试”和“错误”。我曾经写过一个控制器,遇到网络错误就直接返回错误,结果导致无限重试,把API Server打挂了。正确的做法是:区分临时错误和永久错误,临时错误用指数退避重试,永久错误直接记录到资源状态里

4.4 三者如何协同工作?

API Server、CRD、控制器这三者,就像人体的神经系统、骨骼和肌肉:

组件 类比 职责
API Server 神经系统 接收指令、传递信息、控制访问
CRD 骨骼 定义资源结构、提供标准化框架
控制器 肌肉 执行动作、维持状态、处理异常

用户通过API Server提交一个CRD资源(比如Database),API Server校验后存入etcd。控制器监听到新资源,开始执行调和循环,创建对应的Pod、Service、PVC等底层资源。整个过程用户只需要声明“我要什么”,剩下的交给控制器。

我的建议:刚开始做PaaS平台时,别想着一步到位。先实现一个最简单的控制器——只支持一种数据库,只处理创建和删除。跑通了再慢慢加功能。我见过太多团队一上来就想做“全功能Operator”,结果半年过去了还在改Bug。

嗯,控制平面设计这块,说白了就是三件事:API Server定规矩,CRD定结构,控制器定行为。把这三件事做好了,PaaS平台的基础就稳了。下一章咱们聊聊数据平面的设计,那又是另一番天地了。