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 对象 |
|---|---|
| 路由 Route | Ingress + higress.io/* 注解 |
| AI 路由 AiRoute | ConfigMap(高层抽象)+ Ingress + 多个 WasmPlugin 实例 + EnvoyFilter |
| 服务来源 ServiceSource | 名为 default 的单个 McpBridge CR |
| Wasm 插件实例 | WasmPlugin CR |
| 域名 Domain / 全局配置 | ConfigMap |
| TLS 证书 | Secret(type kubernetes.io/tls) |
| LLM Provider | ai-proxy 插件的全局 WasmPlugin 实例 |
| 消费者/鉴权 | key-auth 插件实例;密钥 → Secret |
读法:把这张表记牢——它是理解整个 Console 的钥匙。Console 干的所有事,本质就是把用户操作翻译成这张表右列的 K8s 对象。第 3 周会逐个展开右列。
📝 举个例子:两次操作分别落进哪个抽屉
你上传一张 TLS 证书(cert + key)→ 因为含私钥这类机密,落成一个
你新增一个域名
type=kubernetes.io/tls 的 Secret。你新增一个域名
example.com → 普通配置,落成一个 ConfigMap(前缀 domain-)。同样是"存一条配置",敏感的进保险柜,普通的进抽屉。图注:这张"东西 → 抽屉"图是理解整个 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,groupnetworking.higress.io、pluralmcpbridges - WasmPlugin(插件):
:586-666,groupextensions.higress.io、pluralwasmplugins - EnvoyFilter(istio CRD):
:668-712
读法:CRD 没有专用的 Java API 类,所以用泛型的
CustomObjectsApi——传入 group/version/plural 三元组定位资源类型,用 io.kubernetes.client.openapi.JSON 在 Object 和自定义 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
乐观锁与冲突
更新走 replaceIngress(replaceNamespacedIngress),靠 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 08:K8s 客户端——客户端怎么初始化(in-cluster vs kubeConfig)、怎么鉴权(JWT policy + token 文件)、以及少数走 HTTP 直连 Higress 控制器的只读运维接口。