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

K8s 客户端:怎么连、怎么认证

昨天看了"存了哪些资源",今天看"客户端本身怎么建、怎么鉴权"。重点是两种连接模式(集群内/外)和 token 的来源——这决定了 Console 部署在哪、以什么身份读写 K8s。

📍 你在整门课的位置(第 2 周 SDK 与配置存储)
D01 全景 D02 启动 D03 分层 D04 REST D05 横切 D06 SDK D07 存储 D08 客户端 D09 模型 D10 流转
💡 一句话兜住今天(连 K8s = 拿门禁卡进档案室) Day 07 说"东西存进档案室",今天问:Console 凭什么进得去、以谁的身份进?进档案室要刷卡,卡有两种:跑在集群里(in-cluster)时用员工工牌——K8s 自动把 ServiceAccount token 挂到 Pod 固定路径,天生带卡;在本机开发时没工牌,就用访客证 kubeConfig(你 ~/.kube/config 那个)。"那个 token 文件在不在"就是判断"我是员工还是访客"的唯一依据。今天就讲这张卡怎么拿、三个 API 门各通往哪。
L01

三大 API 入口

KubernetesClientService.java 用官方客户端 io.kubernetes.client.openapi.*,三个 API 对象各管一类资源:

  • CoreV1Api:ConfigMap / Secret / Service / Endpoints(:298, :425, :478
  • NetworkingV1Api:Ingress(:279, :329, :375
  • CustomObjectsApi:所有 CRD(McpBridge/WasmPlugin/EnvoyFilter,:533, :612, :669
读法:记住这个对应关系,读到任何 K8s 操作就知道它用哪个 API 类。CustomObjectsApi 是"万能"的,因为 CRD 千变万化,K8s 用一套泛型接口统一处理。
三扇门,各通往一类资源(同一个已认证的 client) 已认证的 K8s client CoreV1Api ConfigMap/Secret/Service NetworkingV1Api Ingress CustomObjectsApi 所有 CRD(万能)
图注:读到任何 K8s 操作,先看它用哪个 API 类,就知道在动哪类资源。
L02

客户端初始化

构造函数 KubernetesClientService(HigressServiceConfig):140-179):

validateConfig(config);                     // :791-812 校验
// 赋值 controller 相关字段(:143-158)
inClusterMode = isInCluster();              // :153-154 判定
// :160-176 三种建客户端方式:
//   ClientBuilder.cluster()                        (集群内)
//   ClientBuilder.kubeconfig(loadKubeConfig(file)) (从 kubeconfig 文件)
//   ClientBuilder.kubeconfig(loadKubeConfig(str))  (从 kubeconfig 内容字符串)
initializeK8sCapabilities();                // :178 能力探测
"连 K8s"到底是连什么? K8s 集群有一个 API Server(就是那个管一切的中枢)。"连 K8s"= 建一个能向 API Server 发 HTTP 请求的客户端,并带上身份凭证。凭证要么是集群内的 ServiceAccount token(Pod 里自带),要么是 kubeconfig 文件(你本机 ~/.kube/config 那个)。三种建法对应三种拿凭证的场景。
L03

in-cluster 判定

isInCluster():273-275):检查 /var/run/secrets/kubernetes.io/serviceaccount/token 文件是否存在。

这个文件是怎么来的? 当 Console 作为 Pod 跑在 K8s 集群里时,K8s 会自动把该 Pod 的 ServiceAccount token 挂载到这个固定路径。所以"这个文件存在"就等于"我正跑在集群里",可以直接用它做身份。如果文件不存在(比如你在本机开发跑 jar),就说明在集群外,得改用 kubeconfig。一个文件的有无就区分了两种部署形态,很巧妙。
你在哪跑token 文件存在?凭证来源建 client 的方式
Pod 里(集群内)✅ 存在ServiceAccount 工牌ClientBuilder.cluster()
本机 --local 跑 jar❌ 不存在~/.kube/config 访客证kubeconfig(loadKubeConfig(file))
传入 kubeConfig 内容串❌ 不存在配置里的内容字符串kubeconfig(loadKubeConfig(str))
📝 举个例子:同一份代码,两处跑 本机调试:isInCluster() 检查 /var/run/secrets/.../token → 不存在 → 走 kubeConfig,用你电脑上的 kube 配置连测试集群。
部署上线:同样一行代码,Pod 里那个 token 文件被 K8s 自动挂上了isInCluster() 为 true → 走 cluster(),用 Pod 自己的工牌。代码没改一个字,环境自己"认卡"。
L04

能力探测

initializeK8sCapabilities():181-204):探测集群是否支持 Ingress v1 API,重试 5 次(CAPABILITY_CHECK_ATTEMPTS),探测不到就假定支持(K8s ≥ 1.19 都有)。

读法:不同版本 K8s 的 Ingress API 版本不同(老版本是 v1beta1)。启动时先探一探集群支持哪个版本,后续就用对的 API,避免"新代码打老集群"报错。重试 5 次是防启动瞬间 API Server 还没就绪。
L05

鉴权 token(JWT policy)

访问 Higress 控制器 debug 接口时的 token 来源,见 readTokenFromFile():733-739):

// 按 controllerJwtPolicy 选 token 文件:
// FIRST_PARTY_JWT  → /var/run/secrets/kubernetes.io/serviceaccount/token
// THIRD_PARTY_JWT  → /var/run/secrets/access-token/token
为什么有两种 JWT policy? token(一段代表身份的加密字符串,JWT)有两种签发方式:first-party 用 K8s 默认给 Pod 的 ServiceAccount token;third-party 用一个专门挂载的、有受众/过期控制的 token(更安全,符合较新的 K8s 安全实践)。controllerJwtPolicy 让部署方按集群策略选,代码读对应路径的文件即可。

👶 小白:既然都是身份 token,first-party 和 third-party 有啥实质区别?

👨‍🏫 老师:类比门禁卡。first-party 是"万能工牌"——K8s 默认发给 Pod 的 ServiceAccount token,哪儿都能刷、不太过期。third-party 是"限定访客证"——专门签发、绑定受众(只准进指定的门)、还带过期时间,更安全,符合较新的 K8s 安全实践。controllerJwtPolicy 只是让部署方按集群策略选用哪种卡,代码去对应路径把卡取出来用而已。

L06

直连控制器 debug 接口

绝大多数操作走 K8s API,但少数只读运维数据直接 HTTP 调 Higress 控制器(用 okHttp):

gatewayServiceList()     :235-252  // GET /debug/registryz    已发现的服务列表
gatewayServiceEndpoint() :254-272  // GET /debug/endpointShardz endpoint 分片
buildControllerRequest(path) :719-731
//   host:集群内用 controllerServiceName.controllerNamespace,否则 controllerServiceHost
//   带 Bearer token(controllerAccessToken 或 readTokenFromFile)
读法:"服务发现列表""endpoint 分片"这类实时运维数据不存在 K8s 里,而是 Higress 控制器内存中算出来的,所以只能直接问控制器。写配置走 K8s(解耦),读运行时状态走 HTTP(直连)——各取所需。
L07

命名空间 / ingressClass 过滤

写入前 fillDefaultIngressClass:755-760)补上 ingressClassName;列出时 retainWatchedIngress:749-753)+ buildIsIngressWatchedPredicate:762-774)按 ingressClass 过滤,兼容 nginx class 为空的场景。

ingressClassName 是干嘛的? 一个集群里可能同时装了多个 Ingress 控制器(nginx、Higress……)。ingressClassName 就是 Ingress 的"归属标签"——写着 higress 的才归 Higress 管。Console 写入时自动打上这个 class,列出时也只看归 Higress 管的那些,避免和 nginx 的 Ingress 混在一起。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 三大 K8s API 入口各管什么?
  • in-cluster 怎么判定?两种连接模式分别拿哪种凭证?
  • 两种 JWT policy 的 token 来自哪个文件?
  • 哪些数据走 K8s、哪些走 HTTP 直连控制器?为什么?

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/higress-console/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/kubernetes
sed -n '140,204p' KubernetesClientService.java
sed -n '235,275p' KubernetesClientService.java
sed -n '719,760p' KubernetesClientService.java
明天预告 · Day 09领域模型——Route/ServiceSource/WasmPluginInstance/Consumer 等实体的字段与校验,以及 VersionedDto 的乐观锁、WasmPluginInstanceScope 的四作用域。
← Day 07 Day 09 · 领域模型 →