Day 07 / 共 20 天 · 第 2 周 SDK 与配置存储

配置存储:一切都落在 K8s

今天回答一个核心问题:Console 把用户配的东西存到哪、怎么存?答案是"全部落成 K8s 原生对象"。今天建立"哪类配置 → 哪种 K8s 资源"的映射表,明天再深入客户端本身。

📍 你在整门课的位置(第 2 周 SDK 与配置存储)
D01 全景 D02 启动 D03 分层 D04 REST D05 横切 D06 SDK D07 存储 D08 客户端 D09 模型 D10 流转
💡 一句话兜住今天(K8s = 唯一的档案室) Day 06 拆了发动机的骨架,今天问最实在的问题:用户配的东西到底存哪、存成什么样?答案就一句——全部落成 K8s 原生对象,塞进那间"档案室"。不同资料放进不同抽屉:普通配置进 ConfigMap 抽屉,机密(证书/密钥)进 Secret 保险柜,路由进 Ingress 档案,网关专属的东西进 CRD 定制档案。今天的核心就是背下这张"东西 → 抽屉"的对照表。
L01

存储层 = 薄封装

KubernetesClientService.java(813 行)把每类资源的 CRUD 都封成方法——没有接口、没有多实现,这个类本身就是"存储抽象"。

为什么不搞个"仓储接口 + 多实现"? 很多项目喜欢定义 Repository 接口,再写 MySQL 实现、Redis 实现……但 Higress Console 只有一种存储后端——Kubernetes API Server,永远不会换成 MySQL。既然只有一种实现,再抽个接口就是过度设计。于是它直接用一个具体类当存储层,方法就是各资源的 CRUD。简单、直接、够用。

👶 小白:书上都说"要面向接口编程",这里只用一个具体类当存储层,不算坏味道吗?

👨‍🏫 老师:接口的价值是"将来能换实现"。可这里的存储后端只可能是 K8s API Server,永远不会换成 MySQL——为一个不存在的"将来"预留抽象,就是过度设计。抽象要为真实的可变点服务;没有可变点,直接用具体类反而更清爽。这是"恰到好处"的工程判断。

L02

资源 → K8s 对象映射(本课核心表)

控制台里的东西落地为哪种 K8s 对象
路由 RouteIngress + higress.io/* 注解
AI 路由 AiRouteConfigMap(高层抽象)+ Ingress + 多个 WasmPlugin 实例 + EnvoyFilter
服务来源 ServiceSource名为 default 的单个 McpBridge CR
Wasm 插件实例WasmPlugin CR
域名 Domain / 全局配置ConfigMap
TLS 证书Secret(type kubernetes.io/tls
LLM Providerai-proxy 插件的全局 WasmPlugin 实例
消费者/鉴权key-auth 插件实例;密钥 → Secret
读法:把这张表记牢——它是理解整个 Console 的钥匙。Console 干的所有事,本质就是把用户操作翻译成这张表右列的 K8s 对象。第 3 周会逐个展开右列。
📝 举个例子:两次操作分别落进哪个抽屉 你上传一张 TLS 证书(cert + key)→ 因为含私钥这类机密,落成一个 type=kubernetes.io/tlsSecret
你新增一个域名 example.com → 普通配置,落成一个 ConfigMap(前缀 domain-)。同样是"存一条配置",敏感的进保险柜,普通的进抽屉。
档案室的四种抽屉:控制台的东西各归各位 Ingress 档案 路由 Route (+higress 注解) ConfigMap 抽屉 域名 / 全局配置 AI 路由高层抽象 Secret 保险柜 TLS 证书 认证密钥 CRD 定制档案 McpBridge(服务来源) WasmPlugin(插件) EnvoyFilter 四种 K8s 对象类型 = 四个抽屉;Console 只管"把东西放对抽屉"
图注:这张"东西 → 抽屉"图是理解整个 Console 的钥匙,右侧三列都会在第 3 周展开。
L03

Ingress CRUD(用官方 NetworkingV1Api)

KubernetesClientService.java 里 Ingress 相关方法:

listIngress()      :328-338   // 只列带 higress 标签的
readIngress        :374-384   // 404 转 null
createIngress      :386-391
replaceIngress     :393-403
deleteIngress      :405-418
读法:底层用 Kubernetes 官方 Java 客户端 io.kubernetes:client-java(不是 fabric8)。readIngress 把 K8s 的 404 转成 Java 的 null——一个贴心的适配,调用方判空即可,不用 catch 异常。
L04

ConfigMap / Secret CRUD

同一个 client,用 CoreV1Api 操作 ConfigMap 和 Secret:

// ConfigMap :420-475(域名、AI 路由、全局配置都存这)
listConfigMap / readConfigMap / createConfigMap / replaceConfigMap / deleteConfigMap
// Secret :477-530(TLS 证书、认证密钥)
listSecret / readSecret / createSecret / replaceSecret / deleteSecret
ConfigMap 和 Secret 有什么区别? 两者都是 K8s 里的键值存储。ConfigMap 存普通配置(明文),Secret 存敏感数据(base64 编码,K8s 可加密静态存储)。所以证书私钥、API 密钥这类敏感信息用 Secret,域名/路由这类普通配置用 ConfigMap。语义清晰,也符合 K8s 最佳实践。
L05

CRD 的 CRUD(用 CustomObjectsApi)

自定义资源(CRD)用 CustomObjectsApi 泛型操作:

  • McpBridge(服务来源)::532-582,group networking.higress.io、plural mcpbridges
  • WasmPlugin(插件)::586-666,group extensions.higress.io、plural wasmplugins
  • EnvoyFilter(istio CRD)::668-712
读法:CRD 没有专用的 Java API 类,所以用泛型的 CustomObjectsApi——传入 group/version/plural 三元组定位资源类型,用 io.kubernetes.client.openapi.JSONObject 和自定义 POJO 之间转换(POJO 在 crd/ 目录)。这三类 CRD 正是上一站 Higress 会 watch 的资源。
L06

只列"自己创建的"资源

所有 list 默认带标签选择器(:101-102 DEFAULT_LABEL_SELECTORS = higress.io/resource-definer=higress):

// renderDefaultMetadata :741-746
// 写入任何资源前,统一打上 resource-definer=higress 标签
// 内部资源额外打 higress.io/internal=true
为什么要打这个标签? 一个 K8s 集群里可能有很多 Ingress,有的是别的工具建的。Console 只想管"自己创建的"那些——所以写入时统一打上 resource-definer=higress 标签,列出来时也只筛这个标签的。好比在自己的东西上贴名字条,找的时候只找贴了自己名字的,绝不误删别人的。isDefinedByConsole:214-217)就靠这个标签判断"是不是我建的"。
⚠️ 常见误解:以为 Console 会管理集群里所有 Ingress。不会——它列出/修改时都带 higress.io/resource-definer=higress 标签选择器,只碰"自己贴过名字条"的那些。别的工具(比如 nginx-ingress)建的 Ingress,Console 看都不看,绝不会误删。
L07

乐观锁与冲突

更新走 replaceIngressreplaceNamespacedIngress),靠 K8s 的 resourceVersion 做乐观锁;创建撞名字时 K8s 返 409,SDK 转成 ResourceConflictException

读法:领域模型的 version 字段(VersionedDto)对应 K8s 的 resourceVersion更新时带上你读到的版本号,若期间别人改过,版本对不上,K8s 拒绝写入——避免"后写覆盖先写"。这是分布式配置管理的标准做法。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 为什么"存储层"是薄封装而不是仓储接口?
  • 路由/AI 路由/服务来源/证书分别落成哪种 K8s 对象?
  • Ingress/ConfigMap/Secret/CRD 各用哪个 K8s API 类?
  • resource-definer=higress 标签的作用?乐观锁怎么防覆盖?

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/higress-console/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes
grep -n "public.*Ingress\|public.*ConfigMap\|public.*Secret\|public.*McpBridge\|public.*WasmPlugin" KubernetesClientService.java | head -40
sed -n '741,760p' KubernetesClientService.java
明天预告 · Day 08K8s 客户端——客户端怎么初始化(in-cluster vs kubeConfig)、怎么鉴权(JWT policy + token 文件)、以及少数走 HTTP 直连 Higress 控制器的只读运维接口。
← Day 06 Day 08 · K8s 客户端 →