1. 从 kubectl top 报错说起:Metrics-Server 部署时 kubelet endpoint 到底卡在哪
集群里敲下kubectl top nodes,返回error: Metrics API not available,或者 Pod 一直 CrashLoopBackOff,日志里刷unable to fully scrape metrics、x509: cannot validate certificate。这类问题在自建 K8S 集群里太常见了,尤其是 kubeadm 装出来的环境,Metrics-Server 默认根本没装,而 kubelet 的 10250 端口又是自签证书,Metrics-Server 一上去就握手失败。
Metrics-Server 是什么?一句话:它是 K8S 集群的指标聚合器,从每个节点的 kubelet 拉取 CPU、内存数据,再通过 Metrics API(metrics.k8s.io)暴露给 apiserver,让kubectl top、HPA 自动扩缩容能拿到数据。适合谁?运维、平台工程师、以及任何需要给集群做资源监控和弹性伸缩的人。
我这次遇到的场景比较特殊:集群里 kubelet 的 endpoint 不是直连节点 IP,而是统一走一条 API 通道(TaoToken 的 API 网关),Key 和 Base URL 集中管理。这样做的目的是把散落在各节点的证书、地址配置收敛到一处,避免每加一个节点就要改一遍 Metrics-Server 的 args。但代价是,endpoint 一改,证书校验、地址类型优先级、聚合层注册全得重新对一遍,否则就是各种 401 和local proxy failed。
下面把我踩过的坑和最终跑通的配置完整写出来。核心链路是:Metrics-Server 通过--kubelet-preferred-address-types找到节点地址,用--kubelet-insecure-tls跳过自签证书校验,再通过统一 Key 访问 kubelet 的 metrics 接口。整个过程涉及 components.yaml 的关键字段修改、kubelet endpoint 指向统一通道、以及kubectl top的验证。
先说结论:90% 的报错集中在三个地方——镜像拉不下来、证书校验没过、地址类型选错。把这三处按下面的配置改完,基本一次通。
2. TaoToken 前置准备:统一 Key 与 API 通道怎么配
在改 Metrics-Server 之前,得先把统一通道准备好。TaoToken 在这里扮演的角色是:给集群内所有需要访问外部模型/API 的组件提供一个统一的 Base URL 和 Key,Metrics-Server 拉指标时走的 kubelet endpoint 也收敛到这条通道上,避免每个节点单独配证书。
你需要先拿到两样东西:API Key 和 Base URL。API 地址是https://taotoken.net/api,注意这个不带任何查询参数,是纯接口地址。Key 在控制台的 API Keys 页面生成,路径是https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite。
生成 Key 的时候有几个细节要注意。第一,Key 只在创建时显示一次,复制下来存好,后面写进 Secret 里。第二,如果集群里有多个组件共用,建议按组件分 Key,方便后面排查是哪个组件在刷接口。第三,Key 的权限范围按最小化原则给,Metrics-Server 只需要读指标,不需要写权限。
拿到 Key 之后,在集群里建一个 Secret 存起来,不要硬编码在 YAML 里:
kubectl create secret generic taotoken-credentials \ --from-literal=api-key='你的Key' \ -n kube-system然后确认一下聚合层是开着的。kubeadm 装的集群默认开启 API Aggregation Layer,验证方法是看 apiserver 的启动参数里有没有--enable-aggregator-routing=true,或者直接查 APIService:
kubectl get apiservice v1beta1.metrics.k8s.io如果返回NotFound,说明 Metrics-Server 还没注册进来,这是正常的,装完就有了。如果返回False,那要看 apiserver 的聚合层配置。
这里有个容易忽略的点:统一通道的 Base URL 和 kubelet endpoint 是两个概念。Base URL 是 Metrics-Server 访问外部 API 用的,kubelet endpoint 是 Metrics-Server 访问节点 kubelet 用的。我这次把两者都收敛到 TaoToken 通道上,是为了统一管理证书和地址。如果你只是想让 Metrics-Server 正常跑,kubelet endpoint 用节点 InternalIP 就行;但如果你所在的环境节点地址经常变、证书不统一,那走统一通道会省很多事。
配置统一通道时,Base URL 填https://taotoken.net/api,Key 用上面生成的。如果你还要接 Claude Code 或做 coding 相关的 Agent,可以顺带看下 Coding Plan 的配置,路径是https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite,不过那是另一条链路,跟 Metrics-Server 不冲突。
3. 可复制配置:components.yaml 关键字段与 kubelet endpoint 指向
这一步是核心。先把官方 components.yaml 拉下来:
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml然后改三个地方。第一,镜像地址。国内环境直接拉registry.k8s.io大概率超时,换成可访问的镜像源。第二,args 里加证书和地址类型参数。第三,如果走统一通道,把 kubelet endpoint 相关配置指向 TaoToken 的 API 地址。
改完的 Deployment 关键片段如下,你可以直接对照替换:
apiVersion: apps/v1 kind: Deployment metadata: name: metrics-server namespace: kube-system spec: template: spec: containers: - name: metrics-server image: k8s.srebro.site/metrics-server/metrics-server:v0.7.1 imagePullPolicy: IfNotPresent args: - --cert-dir=/tmp - --secure-port=10250 - --kubelet-insecure-tls - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP - --kubelet-use-node-status-port - --metric-resolution=15s env: - name: TAOTOKEN_BASE_URL value: "https://taotoken.net/api" - name: TAOTOKEN_API_KEY valueFrom: secretKeyRef: name: taotoken-credentials key: api-key逐个参数解释。--kubelet-insecure-tls是跳过 kubelet 自签证书校验,不加这个,日志里就是x509: cannot validate certificate for x.x.x.x because it doesn't contain any IP SANs。--kubelet-preferred-address-types指定地址类型优先级,顺序很重要:InternalIP 排第一,因为集群内网通信最稳;Hostname 和 DNS 放后面做兜底。--kubelet-use-node-status-port让 Metrics-Server 用节点状态里上报的端口,避免硬编码 10250。--metric-resolution=15s是采集间隔,默认 60s,改成 15s 数据更实时,但别改太小,否则 kubelet 压力大。
如果你要把 kubelet endpoint 显式指向统一通道,可以在 args 里加--kubelet-preferred-address-types的同时,通过环境变量注入 Base URL,让 Metrics-Server 的请求走 TaoToken 网关。注意,kubelet 的 metrics 接口路径是/metrics/resource,走统一通道时确保网关把这个路径转发到各节点的 10250 端口。
还有一个 JSON 格式的配置片段,如果你用 ConfigMap 管理参数,可以这样写:
{ "kubeletPreferredAddressTypes": ["InternalIP", "Hostname", "InternalDNS"], "kubeletInsecureTLS": true, "metricResolution": "15s", "taotokenBaseURL": "https://taotoken.net/api", "taotokenKeyRef": "taotoken-credentials/api-key" }改完保存,然后 apply:
kubectl apply -f components.yamlapply 之后别急着验证,先看 Pod 状态。如果 Pod 起不来,大概率是镜像没拉下来或者 Secret 没建对。用kubectl describe pod -n kube-system -l k8s-app=metrics-server看 Events,镜像问题会显示ErrImagePull,Secret 问题会显示CreateContainerConfigError。
4. 验证请求:kubectl top nodes 打通指标采集链路
Pod 变成 Running 之后,先确认 APIService 注册成功:
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml看status.conditions里Available是不是True。如果是False,看message字段,通常会告诉你具体原因,比如failing or missing response from https://x.x.x.x:10250。
然后等 15 到 30 秒,让 Metrics-Server 完成第一轮采集,再执行:
kubectl top nodes正常输出类似:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% k8s-master01 230m 5% 3026Mi 41% k8s-master02 240m 6% 2399Mi 32% k8s-node01 264m 6% 6663Mi 90%如果返回error: Metrics API not available,说明 APIService 还没就绪,再等一会儿或者查 Metrics-Server 日志:
kubectl logs -n kube-system -l k8s-app=metrics-server --tail=50日志里如果出现Failed to scrape node加dial tcp x.x.x.x:10250: connect: connection refused,那是 kubelet 的 10250 端口没监听或者防火墙挡了。如果出现401 Unauthorized,那是统一通道的 Key 没配对,检查 Secret 里的 api-key 和 TaoToken 控制台生成的是否一致。
再验证 Pod 级别的指标:
kubectl top pods -A输出会列出所有命名空间下 Pod 的 CPU 和内存。到这一步,指标采集链路就通了。如果你还想验证模型对话相关的接口是否也走通了统一通道,可以到模型对话页面发一条测试请求,路径是https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite,确认 Key 和 Base URL 在两条链路上都生效。
验证通过后,建议把kubectl top加进日常巡检脚本,配合告警规则,节点内存超过 85% 就触发通知。我这次就是靠这个提前发现了一个节点内存跑到 90% 的情况。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
排障这块我按真实报错逐条对。你遇到哪个直接对号入座。
401 Unauthorized。这个最直接,Key 不对或者没传。检查三处:Secret 里的 api-key 是不是最新生成的、环境变量TAOTOKEN_API_KEY有没有正确引用 Secret、Base URL 是不是https://taotoken.net/api(注意结尾没有斜杠)。如果 Key 轮换过,记得更新 Secret 并重启 Metrics-Server Pod。
local proxy failed。这个报错通常出现在 apiserver 转发请求到 Metrics-Server 时。完整报错类似local proxy failed: error trying to reach service: dial tcp x.x.x.x:443: i/o timeout。原因是 apiserver 找不到 Metrics-Server 的 Service 或者网络不通。检查kubectl get svc -n kube-system metrics-server是否存在,Endpoints 是否有地址。如果 Service 正常但还报这个,看 apiserver 的--enable-aggregator-routing是不是 true。
reading choices。这个报错一般出现在用 OpenAI 兼容接口调模型时,返回体解析失败。如果你在 Metrics-Server 链路上看到类似error reading choices的日志,那说明请求被转发到了模型接口而不是 kubelet 接口,检查统一通道的路由规则,确保/metrics/resource路径转发到 kubelet 的 10250,而不是模型服务。
OAuth 相关报错。如果日志里出现OAuth或token expired,说明统一通道的鉴权方式配错了。TaoToken 的 API 用的是 Key 鉴权,不是 OAuth,所以检查请求头里是不是带了多余的 Authorization 字段,或者 Key 被当成了 Bearer Token。正确的做法是在请求头里带Authorization: Bearer <你的Key>,同时 Base URL 用https://taotoken.net/api。
另外补充一个 Codex auth.json 的场景。如果你在用 Codex 类工具,auth.json 里需要写全三件套:Base URL、Key、Model ID。格式如下:
{ "base_url": "https://taotoken.net/api", "api_key": "你的Key", "model": "你的模型ID" }Cline MCP 的配置同理,Base URL、Key、Model ID 三件套缺一不可。CC Switch 切换配置时也要确认这三项都带上了,否则切过去就是 401。
排障的核心思路是:先看 Pod 日志定位是证书问题还是鉴权问题,再看 APIService 状态确认聚合层注册,最后用kubectl top做端到端验证。三步走完,基本没有定位不到的错。
6. 接入文档与 API Keys:把这条链路固化下来
链路跑通之后,建议把配置固化到 Git 仓库里,components.yaml 和 Secret 的创建脚本一起管理。Secret 不要明文提交,用 Sealed Secrets 或者外部密钥管理。
后续如果要加节点,只需要确认新节点的 kubelet 10250 端口可达,Metrics-Server 会自动通过--kubelet-preferred-address-types找到它,不用改配置。如果统一通道的 Key 需要轮换,更新 Secret 后滚动重启 Metrics-Server 即可:
kubectl rollout restart deployment metrics-server -n kube-system接入文档放在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,里面有完整的接口说明和参数列表。API Keys 管理在https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite,建议按环境分 Key,生产、测试、开发各一个,方便审计和限流。
最后说个实用技巧:把kubectl top nodes和kubectl top pods -A的输出定期落盘,配合简单的 shell 脚本做趋势对比。我试过用这种方式提前发现内存缓慢增长的问题,比等告警触发要早好几个小时。指标采集链路打通只是第一步,让数据真正用起来才是目的。