SubRelay Operator 项目全景与学习路线

先把项目当成一座自动化公寓

第一次打开这个项目,很容易被 CRD、StatefulSet、NetworkPolicy、Gateway API 和一堆 Go 代码吓住。其实它做的事情可以先用一句话概括:

用户提交一张名为 SubPool 的“入住申请”,Operator 自动建出一套隔离的 CPA 运行环境,并持续保证它与申请内容一致。

可以把 Kubernetes 集群想成一座公寓楼:

项目概念公寓类比实际职责
SubPool入住申请单描述一个号池想要的镜像、资源、存储和访问方式
Operator物业管家读取申请,创建、修正、暂停或删除资源
Namespace独立房间隔离不同号池的资源
StatefulSet固定工位运行 CPA 与 cpa-manager 两个容器
PVC带锁储物柜保存账号配置、日志和管理数据
Service稳定房间号码为会变化的 Pod 提供稳定访问地址
NetworkPolicy门禁规则限制谁能进入、Pod 能去哪里
Shared Mihomo共用出站大厅为所有号池提供代理出口
Envoy Gateway共用前台在 Gateway 模式下接收外部 HTTP 流量

这个类比不是 Kubernetes 的正式定义,但它能帮我们抓住项目最重要的设计:一个号池不是一个孤零零的 Pod,而是一组彼此配合、由 Operator 统一管理的对象。

一张图看懂完整链路

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
                        Kubernetes API
                 用户提交 SubPool Custom Resource
                    SubRelay Operator
                    持续执行 reconcile
              ┌───────────────┴────────────────┐
              │                                │
              ▼                                ▼
    subpool-<pool-id> Namespace        读取共享基础设施状态
              │                                │
     ┌────────┼────────┐              ┌────────┴────────┐
     │        │        │              │                 │
 StatefulSet Service  PVC       Shared Mihomo    Shared Envoy
     │                              Gateway        Gateway
 ┌───┴──────────┐                       ▲              │
 │ CPA          │──── HTTP Proxy ──────┘              │
 │ cpa-manager  │                                      │
 └──────────────┘◀──── HTTPRoute(仅 Gateway 模式)────┘

这里有两条不同方向的数据流:

  • 入站流量:用户访问 CPA。开发时通常通过 kubectl port-forward;生产的 Gateway 模式通过 Envoy Gateway 和 HTTPRoute
  • 出站流量:CPA 访问外部服务时,只允许走 Shared Mihomo Gateway,再由 Mihomo 选择健康的上游代理。

不要把二者混为一谈。Envoy 解决“外面怎么进来”,Mihomo 解决“里面怎么出去”。

三层结构:声明、控制和数据

声明层:SubPool CR

用户不直接创建十几种底层资源,而是提交一个 SubPool

 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
apiVersion: subrelay.example.com/v1alpha1
kind: SubPool
metadata:
  name: demo-pool
spec:
  exposure:
    mode: Internal
  storage:
    size: 20Gi
  cpa:
    image: registry.example.invalid/subrelay/cpa:vX.Y.Z
    resources:
      requests:
        cpu: 2200m
        memory: 3Gi
      limits:
        cpu: 3200m
        memory: 4Gi
  cpaManager:
    image: registry.example.invalid/subrelay/cpa-manager:vX.Y.Z
    resources:
      requests:
        cpu: 200m
        memory: 256Mi
      limits:
        cpu: 300m
        memory: 512Mi

这是项目对使用者提供的业务 API。使用者只表达“我要什么”,不必自己拼装 Namespace、PVC、Service 和 NetworkPolicy。

控制层:Operator

Operator 是一个运行在 subpool-system Namespace 中的 Deployment。它监听 SubPool 的变化,然后不断执行控制循环:

1
读取当前状态 → 计算期望状态 → 创建或修正资源 → 更新 status → 等待下一次事件

这套循环叫 reconcile。它不是只执行一次的安装脚本;只要资源被误删或配置发生漂移,Operator 还会尝试把它恢复到期望状态。

数据层:CPA、cpa-manager 与网关

每个号池的 CPA 和 cpa-manager 运行在同一个 Pod 中,共享网络和 RWO 数据卷。Shared Mihomo 与 Envoy Gateway 是集群级共享基础设施,不归任何单个 SubPool 所有。

这个边界很关键:删除一个 SubPool 可以删除它自己的 Namespace,但不能顺手删除共享网关,否则一个号池的生命周期操作会影响所有号池。

创建一个 SubPool 后会发生什么

demo-pool 为例,Operator 会派生 Namespace:

1
subpool-demo-pool

随后创建或维护这些资源:

资源用途
Namespace形成号池隔离边界
ServiceAccount给运行时 Pod 提供身份,同时禁止自动挂载 token
Secret保存随机生成的 CPA 和管理端凭据
ConfigMap保存幂等初始化脚本
StatefulSet在一个 Pod 中运行 CPA 与 cpa-manager
PVC保存两套程序的持久数据
两个 Service分别提供 CPA 和 cpa-manager 的稳定入口
ResourceQuota限制整个号池 Namespace 的资源上限
PodDisruptionBudget限制自愿中断期间的可用副本数
NetworkPolicy默认拒绝,并只开放必要流量
HTTPRoute仅 Gateway 模式创建,将域名流量转给 CPA

Operator 还会把检查结果写入 SubPool.status.conditions,让使用者知道究竟卡在工作负载、代理网关还是外部入口。

两种访问模式

Internal:适合开发和管理

Internal 只创建 ClusterIP Service,不创建公网入口。访问方式是:

1
2
kubectl -n subpool-demo-pool port-forward service/cpa 8317:8317
kubectl -n subpool-demo-pool port-forward service/cpa-manager 18317:18317

它的优点是简单、安全、几乎不依赖集群外部设施。缺点是终端一断开,转发就停止,因此不适合作为生产入口。

Gateway:适合统一的外部入口

Gateway 模式会额外创建 HTTPRoute,使用 <pool-id>.<baseDomain> 这样的域名连接 Shared Envoy Gateway。

Operator 不会为每个号池创建独立 LoadBalancer、DNS 记录或 TLS 证书。共享入口只维护一套,单个号池只维护自己的路由。这既节约成本,也让证书、域名和入口安全策略集中管理。

Ready 到底表示什么

本项目的 Ready=True 表示“空号池平台已经准备好”,具体包括:

  • StatefulSet 至少有一个 Ready replica;
  • CPA 与 cpa-manager Service 存在;
  • Shared Mihomo Service 有 Ready Endpoint;
  • 当前暴露模式的前提已满足;
  • Operator 已观察到当前这一版 spec。

它不表示以下业务事实:

  • 已导入账号;
  • OAuth 一定有效;
  • Mihomo 的每个上游节点都可用;
  • 真实模型请求已经成功;
  • 每个请求都会切换出口 IP。

基础设施 Ready 和业务 Ready 是两个层次。把边界写清楚,比做一个“看起来什么都检查了、实际上没人知道含义”的健康状态更可靠。

生命周期不是只有创建

一个 SubPool 主要有四种操作:

操作发生什么数据是否保留
创建建 Namespace 和全部运行资源新建数据
更新镜像和计算资源在策略范围内收敛保留
暂停StatefulSet 缩容为 0,删除 HTTPRoute保留 Namespace、PVC 和 Secret
删除删除专属 Namespace,等待清理完成永久删除

暂停和删除的区别非常大。只是临时停用时应设置 spec.suspend: true,不要删除 CR。

推荐阅读顺序

这套笔记按依赖关系排列:

  1. 先读 Kubernetes 对象、Namespace、Label 和 Selector,建立“对象如何互相找到”的直觉。
  2. 再读 Pod、多容器、StatefulSet、PVC、Service,理解号池的运行实体。
  3. 接着读 ConfigMap、Secret、健康探针、资源管理和 NetworkPolicy,理解稳定性与安全基线。
  4. 然后读 Gateway API、RBAC 和 leader election,理解共享入口及控制面高可用。
  5. 最后读 CRD、reconcile、finalizer 与 Kustomize,把所有积木拼回完整 Operator。

读代码时从哪里下手

项目中的关键入口如下:

路径建议关注点
examples/subpool-internal.yaml用户最小输入是什么
api/v1alpha1/subpool_types.goCRD 的 Go 类型和校验规则
controllers/subpool_controller.goreconcile 入口、watch 和 finalizer
controllers/subpool_reconcile.go创建资源的顺序与阻塞条件
internal/subpool/resources.go一个 SubPool 会生成哪些对象
internal/subpool/workloads.goStatefulSet、容器、探针和存储细节
internal/subpool/network.goNetworkPolicy 与 HTTPRoute
config/default/operator-config.yaml全局平台配置和资源包络
deploy/gateway/Shared Mihomo 与 Envoy Gateway

学习 Kubernetes 最舒服的方式不是背完所有 Kind,而是每次追踪一个具体问题:这个对象是谁创建的、被谁选择、依赖谁、坏了看哪里、删除时谁负责收尾。这个项目正好把这些问题串成了一条完整链路。

使用 Hugo 构建
主题 StackJimmy 设计