Metrics Server 与 Resource Metrics API
集群升级后,所有 Node 都是 Ready,应用请求也正常,kubectl top node 却只返回 Metrics API not available。值班人员看到 metrics-server Pod 为 Running,便把故障归咎于 kubectl 缓存;HPA 同时开始报告 FailedGetResourceMetric,副本数停在原位。真正的问题藏在 API 聚合层:v1beta1.metrics.k8s.io 对应的 APIService 不可用,前端命令和控制器根本没有进入 Metrics Server。
另一次故障里,APIService 显示 Available=True,部分节点却持续缺样本。组件日志反复出现 kubelet 证书校验错误和 no metrics to serve,只有安装时追加 --kubelet-insecure-tls 才能“恢复”。这个参数绕过的是 Metrics Server 到 kubelet 的服务端身份验证,并没有修复节点证书、地址选择或控制面网络。把临时绕过固化进生产,会让一个可定位的 PKI 问题变成长期的中间人风险。
先理解 kubectl top 背后的四跳链路
Metrics Server 不是常规监控时序库。它周期性从每个 kubelet 获取 CPU 与内存资源样本,在内存中整理为 Node 和 Pod 指标,再通过 Kubernetes API 聚合层暴露 metrics.k8s.io/v1beta1。kubectl top、HPA 等消费者向 kube-apiserver 请求聚合 API,并不直接连接 Metrics Server Pod。
kubelet /metrics/resource
-> Metrics Server scrape 与短时内存状态
-> APIService v1beta1.metrics.k8s.io
-> kube-apiserver aggregation layer
-> kubectl top / HPA / API client这条链有两个不同的 TLS 与鉴权方向。Metrics Server 作为客户端访问 kubelet,需要能路由到节点地址与 kubelet 端口、验证 kubelet serving certificate,并通过 kubelet 的 webhook authentication/authorization。另一方面,kube-apiserver 作为聚合代理访问 Metrics Server,需要正确的 APIService、代理身份和委托鉴权配置。官方 v0.9.0 清单的 APIService 使用 insecureSkipTLSVerify: true,因此默认安装不会用 caBundle 校验 Metrics Server 的 serving certificate;若团队把这一跳改为 CA 校验,才必须让 APIService 的 caBundle、服务端证书和 Service DNS 名同时匹配。任一方向失败,最终都可能表现为 top 无数据,但证据位置完全不同。
Resource Metrics API 只提供 Node/Pod 的 CPU、内存资源指标,不保存历史曲线,不承载业务 QPS、队列长度或自定义指标,也不适合计费与审计。资源指标流水线说明了组件关系;API 对象字段可在 metrics.k8s.io/v1beta1 参考中核对。
安装前先检查集群能否承载聚合 API
Metrics Server v0.9.0 面向 Kubernetes 1.34+。较旧控制面应选择项目兼容矩阵支持的发行线,不要强装新镜像再用参数掩盖不兼容。先记录服务端版本与节点状态,并确认 API 聚合能力没有被发行版裁剪:
kubectl version
kubectl get nodes -o wide
kubectl api-versions | grep '^apiregistration.k8s.io/'
kubectl get apiservice网络至少有两条方向:控制面的 kube-apiserver 能访问 Metrics Server 的 HTTPS 服务端口;Metrics Server Pod 能访问每个 kubelet 的节点地址与端口,通常是 Node status 中报告的 daemon endpoint,常见安全端口为 10250。网络策略、安全组、主机防火墙或托管控制面出口规则阻断任一方向,Pod 自身仍可能保持 Running。
kubelet 还需要启用 webhook token authentication 与 webhook authorization,使 Metrics Server 的 ServiceAccount token 能被验证和授权。节点 serving certificate 的 SAN 必须覆盖实际选择的地址,且签发链能被 Metrics Server 信任。自建集群若使用自签名、地址缺失或长期证书,应该修复 kubelet 证书签发与轮换,而不是把 --kubelet-insecure-tls 当成安装步骤。
安装动作会创建 cluster-scoped APIService、ClusterRole 和 ClusterRoleBinding。操作者不仅要有目标 namespace 写权限,还要有这些集群资源的受控变更权限。先在隔离或候选集群审阅 manifest,固定 release 与镜像 digest,并通过组织镜像仓库的供应链检查。
用固定发行版安装并验证注册对象
官方项目同时提供 release manifest 与 Helm Chart。最直接的可审计入口是固定 release manifest;下面先下载到变更工作区供评审,再应用,避免把远程内容直接管道给集群:
curl -fL -o metrics-server-components.yaml \
https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml
kubectl apply --server-side --dry-run=server -f metrics-server-components.yaml
kubectl apply -f metrics-server-components.yaml
kubectl -n kube-system rollout status deployment/metrics-server --timeout=300s应在 manifest 中核对 namespace、ServiceAccount、ClusterRole、ClusterRoleBinding、Service、Deployment 和 v1beta1.metrics.k8s.io APIService。镜像最好从 tag 进一步固定到组织审阅过的 digest;如果发行版由 GitOps 管理,下载后的原始 manifest 与本地 patch 都应进入版本控制,由唯一 owner 调谐。
Helm 更适合需要长期维护副本、亲和性、资源和参数覆盖的团队,但 Chart 版本与 Metrics Server 应用版本不是同一序列。使用前查询仓库索引,把候选 Chart 的 appVersion、values 和渲染结果一并评审:
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm repo update
helm search repo metrics-server/metrics-server --versions
export METRICS_SERVER_CHART_VERSION='<reviewed-chart-version>'
helm show chart metrics-server/metrics-server \
--version "${METRICS_SERVER_CHART_VERSION}"
helm show values metrics-server/metrics-server \
--version "${METRICS_SERVER_CHART_VERSION}" > metrics-server-values.reference.yaml选定 Helm 后应由 values 和 release 持续管理,不再与原始 manifest 并行写同一 Deployment。项目的版本、兼容矩阵、安装入口和运行要求以 Metrics Server 官方仓库及 v0.9.0 发行说明为准。
配置参数决定采谁、从哪里采、多久采一次
先读取实际 Deployment 参数,不要从旧教程猜默认值:
kubectl -n kube-system get deployment metrics-server \
-o jsonpath='{.spec.template.spec.containers[0].args}{"\n"}'
kubectl -n kube-system get deployment metrics-server -o yaml--kubelet-preferred-address-types 定义从 Node status 的哪些地址类型中按顺序选择连接目标,例如 InternalIP、ExternalIP、Hostname。地址必须同时满足可路由、DNS 或 IP 可解析、证书 SAN 匹配。把 Hostname 放在最前面可能在本地集群工作,却在控制面 DNS 无法解析节点名的环境失败;把 InternalIP 放在最前面又要求 kubelet 证书包含该 IP。
--kubelet-use-node-status-port 让组件使用 Node status 报告的 kubelet endpoint 端口,而不是假设固定端口。这对自定义 kubelet 端口和发行版封装更稳健,但仍需核对 status 是否真实、网络是否放行。
--metric-resolution 控制采集分辨率。更短间隔会降低样本年龄,却按节点数放大 kubelet 请求、Metrics Server CPU/内存、API 消费和故障时重试压力;更长间隔会让 HPA 看到更陈旧的信号。它不是监控保留期,因为 Metrics Server 不提供长期历史。
--kubelet-certificate-authority 指向验证 kubelet serving certificate 的 CA bundle。CA 文件只包含公开信任材料,可通过受控 ConfigMap 或发行版提供的只读主机文件挂载,并随证书轮换;不能把私钥装进 Metrics Server。--kubelet-insecure-tls 会跳过服务端证书验证,只适合隔离诊断,用它恢复数据恰恰证明信任链或地址匹配存在问题。
Deployment 的 resources.requests/limits、副本数、滚动策略、拓扑分散和优先级同样是有效配置。组件采集全体节点,过低 CPU limit 可能制造自身节流,过低内存 limit 会让缓存与并发在大集群中 OOM;过高 request 则增加系统命名空间的固定成本。
正向实验:从 APIService 走到原始样本
验证不能止于 Pod Ready。先确认 APIService 注册与发现:
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
kubectl describe apiservice v1beta1.metrics.k8s.io
kubectl get --raw /apis/metrics.k8s.io/v1beta1 | head预期 APIService 的 Available condition 为 True,原始发现端点返回 APIResourceList,其中包含 nodes 和 pods。若这里失败,先处理聚合层和服务路由;此时反复检查某个业务 Pod 的 request 没有意义。
再读取 Node 与指定 namespace 的指标:
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods
kubectl top nodes
kubectl top pods -n kube-system --containersNodeMetrics 和 PodMetrics 项目应包含 timestamp、window 与 usage。CPU 常以 core/nanocore 表示采样窗口内的平均使用率,内存是工作集语义的资源量;它们不是容器 request/limit,也不是从容器启动以来的累计账单。kubectl top 为自动伸缩信号优化,数值可能与操作系统 top、cAdvisor 时序或监控平台因采样窗口和定义不同而不完全一致。
为了检查新鲜度,可保存两次原始响应,间隔至少一个采集周期,比较同一对象 timestamp 是否推进、window 是否合理、Node/Pod UID 是否对应当前对象。一个稳定判据是:连续多轮中绝大多数 Ready 节点都产生时间推进的样本,缺失集合不会持续扩大。不能用一次非空 JSON 证明采集链长期健康。
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes > metrics-nodes-a.json
sleep 30
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes > metrics-nodes-b.json这里没有宣称固定 30 秒适用于所有配置;实际等待时间应大于 Deployment 中生效的 --metric-resolution,并考虑采集与 API 延迟。
反向实验:让地址选择稳定暴露采集失败
在隔离集群先列出 Node address 类型,选择一个所有节点都没有的类型。下面以 ExternalDNS 为例;如果任何节点已经存在该类型,就换用另一个确实缺失的受支持类型,不能盲目执行。官方清单本来就有同名参数,所以反例必须替换原值,不能再追加第二个 flag;命令还需要本机有 jq。
kubectl get nodes \
-o jsonpath='{range .items[*]}{.metadata.name}{" => "}{range .status.addresses[*]}{.type}{"="}{.address}{" "}{end}{"\n"}{end}'
kubectl -n kube-system get deployment metrics-server \
-o yaml > metrics-server.before-negative.yaml
DEPLOYMENT_JSON=$(kubectl -n kube-system get deployment metrics-server -o json)
CONTAINER_INDEX=$(printf '%s' "${DEPLOYMENT_JSON}" | jq -er '
.spec.template.spec.containers | to_entries |
map(select(.value.name == "metrics-server")) |
if length == 1 then .[0].key else error("metrics-server container is not unique") end')
ARG_INDEX=$(printf '%s' "${DEPLOYMENT_JSON}" | jq -er --argjson ci "${CONTAINER_INDEX}" '
.spec.template.spec.containers[$ci].args | to_entries |
map(select(.value | startswith("--kubelet-preferred-address-types="))) |
if length == 1 then .[0].key else error("preferred address flag is not unique") end')
kubectl -n kube-system patch deployment metrics-server --type='json' -p="[
{\"op\":\"replace\",\"path\":\"/spec/template/spec/containers/${CONTAINER_INDEX}/args/${ARG_INDEX}\",\"value\":\"--kubelet-preferred-address-types=ExternalDNS\"}
]"
kubectl -n kube-system rollout status deployment/metrics-server --timeout=180s
kubectl -n kube-system logs deployment/metrics-server --since=10m两个 jq -e 查询要求容器名与目标 flag 都恰好命中一次;空值或重复值会直接停止实验,此时应检查实际容器名与参数来源。预期日志出现无法为节点选择地址或抓取失败,随后原始 Metrics API 中 Node/Pod 样本缺失或陈旧;实际错误文本随发行版变化,应以日志中的节点名、地址选择和 scrape error 为证据。
恢复时重新应用已审阅的安装 manifest 或回滚 GitOps/Helm 变更,等待 rollout,再验证 APIService、日志和 timestamp 推进:
kubectl apply -f metrics-server-components.yaml
kubectl -n kube-system rollout status deployment/metrics-server --timeout=300s
kubectl -n kube-system logs deployment/metrics-server --since=5m
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes只有当 metrics-server-components.yaml 正是这个 Deployment 的权威来源时才这样恢复;Helm 或 GitOps 管理的环境应回滚对应 release/commit。metrics-server.before-negative.yaml 用来审计原参数,不应直接长期接管原有字段所有权。恢复后还要确认生效参数中同名 flag 只有一个。
这组反例证明“Pod Ready”与“能选择并采集节点”是两件事。若要验证 TLS 失败,应在可销毁集群为 kubelet 配置不匹配 CA 或证书 SAN,再观察 x509 错误;不要在共享集群随意替换节点证书。
指标缺失时按故障层取第一证据
当 kubectl top 返回 Metrics API not available,先查看 API discovery 和 APIService condition。MissingEndpoints、服务不可达、TLS handshake 或 discovery failure 指向聚合层到 Metrics Server 的路径。此时检查 Service、EndpointSlice、Deployment readiness 和 kube-apiserver 日志:
kubectl describe apiservice v1beta1.metrics.k8s.io
kubectl -n kube-system get service,endpointslice -l k8s-app=metrics-server
kubectl -n kube-system get pod -l k8s-app=metrics-server -o wide
kubectl -n kube-system logs deployment/metrics-server --since=15mAPIService 可用但只有部分节点缺指标时,重点看 Metrics Server 日志中的节点名、所选地址、连接超时、x509、401 或 403。连接超时通常指向 Pod 到节点网络或端口;x509 指向 CA、SAN、有效期或地址选择;401 指向 kubelet 认证;403 指向 kubelet webhook 授权或委托身份。
所有节点都有 NodeMetrics,但业务 Pod 缺容器样本时,检查 Pod 是否刚启动、容器是否还未产生完整窗口、节点是否刚重启、sandbox/运行时是否正常,以及 Metrics Server 日志是否报告对应 Pod。HPA 报 missing request for cpu 则不是 Metrics Server 采集故障,而是资源利用率缺少分母;应修复工作负载 request。
kubectl describe hpa <hpa-name> -n <namespace>
kubectl get pod <pod-name> -n <namespace> -o yaml
kubectl get --raw \
/apis/metrics.k8s.io/v1beta1/namespaces/<namespace>/pods/<pod-name>只有 kubectl top 被拒绝而管理员可读取 raw API 时,问题更可能是调用者 RBAC。只有 raw API 返回空或旧数据时,才回到服务端采集链。按这个顺序可以避免用扩大集群权限解决节点 TLS,也避免用关闭 TLS 解决用户没有读权限。
聚合层与 kubelet 的 TLS 不能混成一张证书
API 聚合层通常由 kube-apiserver 使用 front-proxy client certificate 向扩展 API 标识代理身份,并通过 request-header CA 建立信任。Metrics Server 读取 extension-apiserver-authentication ConfigMap 获得相关配置,使用 system:auth-delegator 把授权判断委托回 API Server。官方 v0.9.0 APIService 默认跳过这一跳的 serving certificate 校验,但代理客户端证书、request-header CA、Service/EndpointSlice 和委托鉴权仍必须成立;若平台把 insecureSkipTLSVerify 改为 false,错误的 caBundle 或服务端证书才会额外造成聚合请求失败。跳过这层校验不等于 --kubelet-insecure-tls:后者作用在 Metrics Server 到 kubelet 的另一跳。
Metrics Server 到 kubelet 是另一条连接。它使用自己的 ServiceAccount bearer token 调用 kubelet,验证 kubelet serving certificate。kubelet 再通过 TokenReview 与 SubjectAccessReview 交给 API Server 认证授权。这要求 API Server、kubelet 与 Metrics Server 的时钟、CA、网络和 webhook 配置都成立。Kubernetes 聚合层说明适合核对代理证书与 request header 信任模型。
抓取错误中出现的证书正文、节点地址和用户名可能暴露内部拓扑。证据包应记录 issuer、subject、SAN、有效期和错误类型,不上传私钥、完整 bearer token 或 kubeconfig。Secret 挂载只提供 CA 公钥材料;任何私钥都由对应签发端或 serving endpoint 持有,不应为了“方便”复制进 Metrics Server Pod。
读权限、云权限与租户边界
读取 Resource Metrics API 需要对 metrics.k8s.io API group 中的 nodes、pods 具有相应 get/list/watch 权限。官方 manifest 通常使用聚合 ClusterRole 让既有 view/edit/admin 角色获得合适的 metrics 读取能力,并为 Metrics Server 本身绑定读取 Node/Pod、委托认证与相关配置的权限。部署后应检查实际聚合结果,而不是假定角色名存在就已授权。
kubectl auth can-i list nodes.metrics.k8s.io --as=system:serviceaccount:<namespace>:<service-account>
kubectl auth can-i list pods.metrics.k8s.io -n <namespace> \
--as=system:serviceaccount:<namespace>:<service-account>
kubectl get clusterrole,clusterrolebinding | grep metrics-servernamespace 租户通常只需读取自己 namespace 的 PodMetrics,不应默认读取全局 NodeMetrics;节点容量与其他租户负载属于敏感运营事实。HPA controller 的权限由控制面系统角色提供,应用 ServiceAccount 不需要因为创建了 HPA 就获得全局 metrics 读取权。
自建 Metrics Server 通常不调用云厂商 API,因此不需要云 AK/SK。云相关动作主要是控制面网络、安全组、防火墙、托管插件和节点证书责任。托管集群若以 add-on 形式提供组件,应使用厂商文档定义的身份与升级通道,不额外给 Pod 绑定宽泛云 IAM;无法解释用途的云权限就是应删除的权限。
高可用首先要消除单副本和单路径假象
增加副本可以避免单 Pod 故障,但还要验证 kube-apiserver 的聚合请求确实能分布到多个后端。官方项目把 --enable-aggregator-routing=true 列为高可用配置的推荐项,用它让请求在 Metrics Server 实例间负载均衡,而不是把该参数写成“创建第二个副本才生效”的硬前提。托管控制面无法由租户修改时,应核对厂商的聚合路由实现,并用逐个删除实例的实验验证,而不是根据参数可见性猜测 HA 是否成立。
多个副本会分别抓取所有节点,增加 kubelet 连接和系统资源消耗。Deployment 应设置跨节点或跨故障域分散、合理的滚动更新、资源 requests/limits 与 PodDisruptionBudget;但 PDB 只约束自愿驱逐,不能修复控制面到 Pod 网络断路。高可用验收要删除一个实例、排空一个承载节点并制造一次滚动升级,观察 APIService 可用性、样本 timestamp、错误率和 HPA condition,而不是只看副本数为 2。
Metrics Server 容量主要随节点数和 Pod 数增长。v0.9.0 的官方基线为 100m CPU、200MiB 内存,默认伸缩包络同时假设不超过 100 个节点、每节点 70 个 Pod、100 个带 HPA 的 Deployment;超过 100 个节点后,项目给出的起始增量是每节点 1m CPU 与 2MiB 内存。这些是估算入口,不是生产保证:CPU 消耗还来自 kubelet 抓取、解析和 API 请求,内存消耗来自当前样本与对象规模,网络消耗会随副本和采集频率放大。生产 request 仍应根据目标规模、metric-resolution、消费者 QPS 与故障重试测量;组件被 CPU 节流会提高样本年龄,被 OOM 会造成全局指标空窗。
消费者设计要承认样本会短暂缺失
HPA 使用 Resource Metrics 时,requests 是利用率分母,Metrics Server 提供当前使用量。新 Pod 启动、节点重启、采集超时和滚动升级都会形成短暂缺样本;控制器会按自身算法处理缺失与未就绪 Pod。业务不能把 Metrics Server 当成唯一保护机制,还需要设置合理的最小副本、扩缩行为、业务限流和容量余量。
监控系统也不应通过高频轮询 Resource Metrics API 冒充时序采集。需要长期趋势、告警、审计或成本分摊时,应由专门观测流水线采集并存储;需要业务或外部指标时,应使用对应 Custom/External Metrics 适配链。Resource Metrics API 的优势是低成本、标准化地服务自动伸缩和 kubectl top,不是覆盖所有可观测需求。
团队告警可围绕链路不变量设计:APIService Available 持续为真;Ready 节点的样本覆盖率稳定;timestamp 年龄不持续增长;scrape error 按 TLS、网络、认证和地址类型分组;Metrics Server 重启、CPU 节流和 OOM 不增长;关键 HPA 的 ScalingActive 与指标获取 condition 可解释。阈值来自采集周期、HPA 周期和业务恢复目标,不使用脱离集群规模的万能数字。
升级、回滚与退出要保护 HPA 依赖
升级前先盘点 metrics.k8s.io 的消费者,尤其是资源型 HPA、运维脚本和容量检查。保存当前 Deployment、APIService、RBAC、镜像 digest、参数、Service、NetworkPolicy 与 kube-apiserver 聚合配置;在候选集群验证 API discovery、全节点覆盖、timestamp 推进、RBAC 正反例和一个 HPA 控制环。跨不兼容发行线时按项目兼容矩阵逐级迁移,不假定 API 仍为同一实现细节。
滚动升级期间同时观察旧、新 Pod 日志与样本年龄。若新版本出现聚合 TLS、kubelet 地址或资源回归,回滚到已经保存的 manifest/Helm release,并再次验证原始 API 与 HPA condition;Deployment Available 不是回滚完成的充分证据。CA 或 RBAC 变更也要随版本一起回滚,不能只替换镜像。
安全退出前先把所有 HPA 从 Resource Metrics 迁移到已验证的替代信号,或在变更窗口把副本固定到容量评审值。随后删除安装清单,并确认 APIService、ClusterRoleBinding、Service、Deployment 和 namespace 内残留都按预期消失:
kubectl delete -f metrics-server-components.yaml
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl -n kube-system get deployment,service | grep metrics-server删除 Metrics Server 不会删除历史监控数据,因为它本就不是历史存储;但它会让 kubectl top 和依赖 Resource Metrics 的控制器失去信号。退出还要撤销临时 NetworkPolicy 放行、测试 RBAC、CA ConfigMap/Secret、镜像镜像库例外和工单中的敏感日志。真正可治理的交付不是“top 能出数字”,而是任意一次空白都能被定位到消费者、聚合层、服务、采集、节点身份或权限中的一个明确责任点。
