Kube-apiserver监控:认证鉴权与Prometheus抓取配置全解析
写这一章第二版的时候我其实犹豫了很久要不要把“抓取本身和认证鉴权”单独立成章节。因为第一版里我默认读者已经能把 Prometheus 的 /metrics 从目标上拉回来了所以从一开始就直接切入 apiserver_request_total 怎么算 QPS、apiserver_request_duration_seconds 怎么拆位点。结果这两年社群里反复问我的问题几乎都不是“这个指标是啥意思”而是“我 Prometheus 里 kubernetes-apiservers 这个 job 要么是 DOWN要么全是 401/403/x509要么抓回来了但图表全是 no data”。我这才意识到大多数人不是看不懂指标而是压根没把数据抓到手。所以这一章先不展开任何指标语义只解决一个问题你是怎样通过认证鉴权把 kube-apiserver 暴露的 metrics 稳定地抓进 Prometheus 的。这一部分捋通了你再看任何 apiserver 指标都有底气捋不通你就算把 apiserver_request_total 的文档背下来Prometheus 页面里依然是空的。1. 为什么这一章先把“抓取”单独拎出来讲1.1 先解决“能不能抓到”再解决“抓到的是什么”很多人第一次接触 Kubernetes 监控都是顺着 kube-prometheus 或者社区文档抄配置文件。抄完发现 Targets 列表里一堆 UP唯独 apiserver 相关的 job 有问题。这时候如果你直接去查“apiserver_request_total 怎么计算成功率”方向就偏了——你的问题不是不知道这个指标的含义而是根本没有数据过来什么指标都白搭。抓取这一步是整条监控链路里最底层、也最容易被忽略的环节。它包含几个子问题目标从哪发现、用什么协议连、用什么身份认证、拿到数据后 Prometheus 怎么按你的规则做标签处理。这四个子问题里认证鉴权又是最容易出事的。kube-apiserver 是 Kubernetes 集群里唯一一个默认暴露安全端口的核心服务它不会像普通应用一样给个 HTTP 端口让你随便拉数据。你身份不对、权限不够、证书不匹配它将直接拒之门外。这一章先把这些底层问题处理干净后面你要横向比较多个 apiserver 实例、做监控告警、接 Grafana 面板才有一个可靠的底座。1.2 apiserver 监控在整条链路里的位置你可以把 apiserver 理解成整个 Kubernetes 集群的“神经中枢”。所有请求——不管是 kubelet 上报状态、controller-manager 调谐资源、还是你敲 kubectl——都要先经过它。所以 apiserver 的指标直接反映了集群控制面的健康状况API 请求 QPS、延迟、错误码分布、etcd 访问延迟、goroutine 数量、WATCH 连接数量等等。但反过来讲正因为它承担了所有请求的入口它本身的 /metrics 端点在数据量上一点也不轻松。一个中等规模的集群apiserver 暴露的指标序列可以有数万条而且某些高基数指标比如按 verb、resource、code 拆分的标签组合会非常大。抓取这个 job 时如果配置草率轻则数据缺失重则 Prometheus 本身内存和存储被拖垮。所以我们必须先搞清楚 /metrics 上到底有哪些端点可抓然后再决定用什么身份去访问。2. scrape 配置的核心思路与选型2.1 端点、协议与端口/metrics 那些事kube-apiserver 默认监听 6443 端口走的是 HTTPS 协议。它暴露的 metrics 端点主要有两个/metrics全量指标。包含所有 Go runtime 指标、进程指标、apiserver 请求指标、etcd 客户端指标等。常规监控你用这个就够了。/metrics/slis这是 SLO 相关的精简指标集。里面主要是 apiserver_request_total 对应的 SLI 版本以及 apiserver_request_duration_seconds 的 SLI 数据。如果你要做 SLO 告警这个端点很有价值。注意这里说的是“非资源型端点”nonResourceURL。它的权限控制和普通 REST API 路径不完全一样——你在 RBAC 里要授权的是/metrics这个非资源路径而不是某个 APIGroup 下的资源。很多人卡在 403 上就是因为把这个区别忽略了。至于访问方式绝大多数情况下你不需要直接指定某个 apiserver 的 IP而是通过集群里内置的kubernetes这个 Service 来访问。这个 Service 在 default 命名空间下DNS 名是kubernetes.default.svc端口 443它会自动代理到后端的 kube-apiserver 实例。集群内访问时用这个 Service 是最稳的证书 SAN 正好包含这个域名能过 CA 校验。2.2 一个能用的 scrape_configs 长什么样先直接给一份完整配置。这是我在本地环境实测过、能稳定抓取正常版本 K8s 集群的配置不是从 kube-prometheus 里硬抄的而是拆开讲清楚每一行干嘛用的。scrape_configs: - job_name: kubernetes-apiservers honor_labels: true scheme: https authorization: credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt relabel_configs: - action: keep source_labels: - __meta_kubernetes_namespace - __meta_kubernetes_service_name - __meta_kubernetes_service_port_name regex: default;kubernetes;https - source_labels: - __meta_kubernetes_service_name target_label: job replacement: kubernetes-apiservers这份配置针对的是“Prometheus 跑在 Kubernetes 集群内部”的场景serviceaccount token 由 K8s 自动挂载到 Prometheus 容器里。如果你的 Prometheus 在集群外面那需要换成证书方式我下面会详细讲。先记住这几个关键字段scheme: https必须。apiserver 不会在非 TLS 端口上暴露 metrics。honor_labels: trueapiserver 返回的指标自带instance标签这个标签代表的是具体的 apiserver 实例地址。如果你不开 honor_labelsPrometheus 会用当前抓取目标的地址覆盖掉它导致你没法区分多 apiserver 实例的数据后面做聚合必然会乱。authorization.credentials_file新版 Prometheus 推荐的认证方式指向 ServiceAccount token 文件。旧配置里你可能见过bearer_token_file那个也能用但新项目不建议再用。tls_config.ca_file用于校验证书 ca 的根证书。在集群内这个文件由 K8s 自动挂载一般不动。relabel_configs核心用来从 K8s Service Endpoints 列表中把 apiserver 筛出来。很多人在这一步会犯一个接近原则性的错误直接用 kubeconfig 里的集群地址或者 LB IP 去写static_configs。一条 static_configs 只能写死一个地址而高可用集群最少也有三个 master 节点你一个 static 目标只能覆盖一个 apiserver监控数据就残了。动态发现是 K8s 监控的第一课。2.3 为什么官方配置里要有 relabel、honor_labels先说 relabel。你用的是 Kubernetes 的服务发现kubernetes_sd_configs的 endpoints 角色它会枚举集群里所有 Service 关联的 Endpoints。如果不加过滤Prometheus 会把集群内每一个 Service 的每一个 Endpoint 都当成抓取目标点开 Targets 页面就是一长串乱七八糟的 URL而且大概率全是 DOWN。所以必须用 relabel 把目标锁定到kubernetes这个 Service 上。判断条件就是三个标签组合命名空间必须是default服务名必须是kubernetes端口名必须是https。这三个标签一匹配剩下的就是 apiserver 的 Endpoints。K8s 会把每个 apiserver 实例作为一个 Endpoint 放进去Prometheus 就会自动发现所有 master 节点。honor_labels为什么重要我举一个具体场景。apiserver 的指标里有instance192.168.1.10:6443这样的标签它代表的是指标来源 apiserver 的地址。如果 honor_labels 设成 false默认值Prometheus 会拿出自己打上的instance标签——也就是它访问目标的地址——去覆盖原来的 instance。在集群内访问场景这个地址可能变成192.168.0.1:443kubernetes Service 的 ClusterIP你拿到的数据里 instance 不再是 master 节点的真实地址。之后你想按 apiserver 实例维度拆数据就会发现数据混成了一团。从经验讲凡是目标自身暴露的指标里已经带了instance这类“身份标签”的都应该把 honor_labels 设为 true。这一点在 apiserver 监控里尤其关键。3. 认证鉴权三种方案选哪种3.1 证书模式适合集群外部的 Prometheus如果你的 Prometheus 不是跑在集群内而是跑在一台独立服务器上最常见的认证方式就是直接用客户端证书。怎么做假设你有一份可以访问集群的 kubeconfig比如 kubeadm 生成的/etc/kubernetes/admin.conf。它里面有一段clusters: - cluster: certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... server: https://192.168.1.100:6443 name: kubernetes users: - name: kubernetes-admin user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0t...你需要把这三段 base64 数据解码成 PEM 文件。命令很简单echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... | base64 -d ca.crt echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS... | base64 -d client.crt echo LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0t... | base64 -d client.key然后配置- job_name: kubernetes-apiservers honor_labels: true scheme: https tls_config: ca_file: /etc/prometheus/certs/ca.crt cert_file: /etc/prometheus/certs/client.crt key_file: /etc/prometheus/certs/client.key relabel_configs: - action: keep regex: default;kubernetes;https source_labels: - __meta_kubernetes_namespace - __meta_kubernetes_service_name - __meta_kubernetes_service_port_name不过这里有个硬前提你使用的客户端证书里的 CN 或组织O必须本身具备访问 /metrics 的权限。比如 kubeadm 的 admin.conf 给的是kubernetes-admin属于system:masters用户组这个组默认拥有所有权限包括非资源端点所以能用。换成 node 节点的 kubelet 证书就不行它的权限通常只覆盖自己的 node 相关的资源访问 /metrics 会 403。这个方案的核心问题是证书会过期。默认 kubeadm 证书有效期一年到期后你要重新解码生成文件再重启 Prometheus。如果你连的是云托管的 K8s可能拿到的 kubeconfig 里是 token 而不是证书那就得走下一种方式。3.2 Token 模式与 SA 权限集群内运行 Prometheus 时标配做法是使用 ServiceAccount把 SA 的 token 作为 Bearer Token 放在请求头里。K8s 会把三个文件挂载到 Pod 内固定路径下/var/run/secrets/kubernetes.io/serviceaccount/token/var/run/secrets/kubernetes.io/serviceaccount/ca.crt/var/run/secrets/kubernetes.io/serviceaccount/namespace这正好对应 Prometheus 的authorization.credentials_file和tls_config.ca_file。配置文件在上一节已经给过这里不再重复。但只挂 token 是远远不够的——你还要给这个 SA 授权。默认创建的 SA 没有任何权限直接拿它的 token 访问/metricsapiserver 会毫不留情地返回 403。我见过太多人卡在这一步明明 token 路径、CA 路径都对Targets 面板里就是红通通的 403 Forbidden。他们不知道认证authenticate和鉴权authorization是两回事。Token 说明“你是谁”RBAC 才决定“你能干嘛”。正确做法是创建一个只给监控用的 ClusterRole绑定到 Prometheus 的 SA 上apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus:apiserver-metrics rules: - nonResourceURLs: [/metrics, /metrics/slis] verbs: [get] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus:apiserver-metrics roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus:apiserver-metrics subjects: - kind: ServiceAccount name: prometheus-k8s namespace: monitoring注意两个细节。第一nonResourceURLs里的路径必须精确匹配/metrics和/metrics下的子路径是分开的你写/metrics/*并不覆盖/metrics本身。所以上面我两个都写了。第二verbs必须是get这个是安全的。有些人图省事直接把这个 SA 绑到 cluster-admin那是给自己埋雷。监控权限就应该是监控权限不该给 Prometheus 进程留一个能删除任意资源的口子。如果你不想手动创建也可以直接用现成的system:monitoringClusterRole。我记得某些 K8s 版本确实提供了这个内置角色专门用于读取监控指标。不过为了兼容性我建议还是自己建一份字段在上面也不多。3.3 新版 authorization 配置以及 CA 校验的坑Prometheus 从 2.26 版本左右开始配置文件里推荐使用authorization配置块替代老的bearer_token/bearer_token_file。含义上两者等价但新写法更清晰Type 默认就是 Bearer只用指定 credentials 或 credentials_file。上面给的示例就是新写法。至于 CA 校验集群内访问时大概率不会出问题但我建议你理解清楚原理。kube-apiserver 证书的 SAN 里通常会包含这些域名kubernetes.default.svckubernetes.default.svc.cluster.localkubernetes.defaultkubernetes以及 master 节点 IP、ClusterIP 等你用https://kubernetes.default.svc:443/metrics这个地址访问证书里的域名是完全匹配的CA 校验通过。但你要是图省事直接把server: https://192.168.1.100:6443这种 master 节点 IP 换成访问地址而该 IP 恰好不在证书 SAN 里就会报x509: certificate is valid for ... not for ...。遇到这个报错我不建议你上来就加insecure_skip_verify: true跳过校验。先想想能不能改成用 DNS 名访问。集群内 Prometheus 直接用kubernetes.default.svc就行如果你把 Prometheus 部署在集群外那就要么在 hosts 里把 apiserver 的域名解析指到 master IP要么就把 master IP 加进证书 SAN 并重新签发证书。跳过 TLS 校验是最后的选择它会把身份伪装和中间人攻击的风险一并放进来。顺带提醒一个更隐蔽的问题很多人通过 LB 或 VIP 访问 apiserver比如云厂商的 SLB 地址而证书 SAN 只有内网 master IP。这时候即便用公网 LB 地址能连上TLS 校验也会失败。这不是 ca_file 配置问题是目标地址和证书身份不匹配的问题。排障时别一直盯着 ca 文件路径反复改要跳出来看 URL。3.4 Token 轮换与 Prometheus 容器化部署的注意点有些人对 ServiceAccount token 的“轮换”问题很焦虑。传统上K8s 挂载到/var/run/secrets/kubernetes.io/serviceaccount/token的文件不会自动轮换——它没有过期时间除非 SA 被删除重建token 永远不变。所以 Prometheus 长年运行都不会因为 token 过期而抓取失败这点不必过度设计。但是如果你用的是较新的 Kubernetes 特性例如 TokenRequestProjectedVolume 或绑定的 ServiceAccount token文件是会过期的。这种场景下Prometheus 如果持续使用同一个 credentials_file到期后就会 401。此时要么让 Prometheus 定期重加载配置要么就用传统的 secret token。说实话对绝大多数监控场景传统方式足够稳定不用追新。如果你在容器里跑 Prometheus 而且担心权限过重另一个思路是使用kubectl proxy或者--requestheader-client-ca-file相关的聚合 API 认证但那是另一套复杂机制了日常监控不推荐。4. 抓取验证与常见故障排查4.1 up 指标与手动 curl 验证两手抓配置改完第一件事不是去 Grafana 建面板而是先在 Prometheus 的 Status → Targets 页面找到kubernetes-apiservers这个 job看它的状态有没有变 UP。如果已经 UP那你再查一下up{jobkubernetes-apiservers}如果返回多个值为 1 的序列说明你动态发现了多个 apiserver 实例恭喜你抓取链路已经通了。如果只有一条且值是 1要看集群是不是单 master。如果是高可用集群但只有一个 instance那大概率是 Service Endpoints 发现不全这个往下等一等也能暴露。Targets 页面只能告诉你“抓取动作成功”不能保证内容是完整的。更可靠的验证方式是直接在 Prometheus 容器里手动模拟一次抓取kubectl exec -it prometheus-prometheus-0 -n monitoring -- sh # 集群内 Prometheus 容器中执行 TOKEN$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) curl -s \ -H Authorization: Bearer $TOKEN \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ https://kubernetes.default.svc/metrics \ | grep ^apiserver_request_total{ | head -n 5如果你能看到apiserver_request_total的输出而且不是一行空输出那就说明真实链路没问题。接下来再回到 Prometheus 页面里跑一句apiserver_request_total有数据出现整个 Kubernetes apiserver 监控的地基就打好了。此时再去研究指标的计算和告警规则才有意义。4.2 403/401/证书问题的快速定位表我整理一张速查表都是我实际遇到过的情况。碰到对应问题直接对着查。现象大概率原因验证与处理403 ForbiddenRBAC 没授权 / 客户端证书无权限确认 ClusterRole 里 nonResourceURLs 精确包含/metrics且 ClusterRoleBinding 绑到了正确的 SA证书方案则检查证书 CN 是否在 system:masters 用户组或有对应 ClusterRole401 UnauthorizedToken 无效/文件路径写错/证书过期检查 credentials_file 路径是否存在手动 curl 看一眼返回证书方案查 client certificate 的 expiryx509: certificate signed by unknown authorityca_file 没配或路径错误检查 tls_config 里 ca_file 是否指向了 K8s 的 ca.crt注意不能用错的集群 CAx509: certificate is valid for ... not for ...访问地址和证书 SAN 不匹配改用 kubernetes.default.svc 访问或把访问地址加进证书 SAN别轻易 insecure_skip_verify目标持续 DOWN报 connect refused网络不通或端口不对确认 apiserver 6443 可达集群内用 kubernetes.default.svc:443检查 Service 是否正常scrape 经常超时target 时 UP 时 DOWNapiserver 指标量大 / scrape_timeout 太短把 scrape_timeout 从默认 10s 调到 30s/60s同时考虑 scraping_interval 调宽数据能抓到但没有 instance 区分多 masterhonor_labels 没开给 job 增加honor_labels: true尽量保留原始 instance 标签其中 403 是我见过最多的一类。很多人的 ClusterRole 写的是/apis/metrics.k8s.io/*或者/metrics/*这都不对。kube-apiserver 的 /metrics 不是 metrics.k8s.io 这个 APIService 提供的是主进程自己暴露的 nonResourceURL你的规则必须精确到/metrics。4.3 抓不到、抓不全、慢的排查顺序如果前面这些都确认了数据依然不对我建议按这个顺序排查效率最高先看 Targets 状态。红色就点开看 ERROR 信息Prometheus 会把 HTTP 状态码或者 TLS 错误直接列出来。这一步能排除 90% 的问题。手动 curl 复现。在 Prometheus 容器里跑一条和 scrape 相同的请求看返回内容。如果 curl 能通但 Prometheus 不行说明是 Prometheus 配置问题多半是 relabel 或端口名写错curl 验证最直接。查 Prometheus 自身的日志。容器日志里会打出每一次失败的 scrape 错误详情比如 timeout 或者 filtered target。看抓取到的 series 数量。在 Status → TSDB 或通过 prometheus_tsdb_head_series 指标看增量。如果 series 数量几万十几万说明数据没问题只是 Grafana 面板筛选条件不对。记得大数据量的场景。apiserver 的指标特别是 apiserver_request_duration_seconds 这类直方图在高 QPS 集群里会产生非常多的 series。如果你的 Prometheus 抓取经常超时先别急着加机器看看是不是该用metric_relabel_configs做一次采样控制。比如只保留.95以上的分位数 bucket或者把apiserver_response_sizes_bucket中不关心的 bucket 删掉。这个思路可以大幅降低 scrape 压力等抓取稳定了再追求指标完整。4.4 多个 master 节点实例落地时的一个细节高可用集群里你最终会抓到多个 apiserver 实例的指标。这些指标的instance标签默认是类似192.168.1.10:6443、192.168.1.11:6443这样的地址但不同实例的 apiserver 进程里某些指标有细微差异。在聚合 QPS 时直接用sum(rate(apiserver_request_total[5m]))就行Prometheus 会自动把多实例的 series 合并。但如果你要按实例维度去看某台 master 是否异常就要保留 instance 标签。别因为 relabel 里什么情况下把__address__改了导致 instance 变成统一的 ClusterIP那就彻底没法区分了。我建议在这个 job 里加一条 relabel- action: labelmap regex: __meta_kubernetes_pod_node_name replacement: node这样每条指标都能带上 apiserver 运行所在的 node 名。后面做“哪台 master 延迟高”的告警时你会发现这个 node 标签比 instance 更直观。5. 从抓取到指标使用之间的一点提醒5.1 权限边界监控不等于万能把认证鉴权做通的下一步我建议你审视自己的权限边界。Prometheus 的 ServiceAccount 如果被绑了 cluster-admin确实能把 apiserver metrics 拉得一干二净但这也意味着你的 Prometheus 一旦被攻破攻击者可以直接操作整个集群。而一个只给了/metrics和/metrics/slis的 get 权限的 SA能干的事情极其有限被攻破也只泄露监控数据不会直接引爆集群安全。我见过不少团队为了排查方便一股脑把 cluster-admin 绑给 Prometheus。等到安全审计的时候这个问题会被拎出来重点批。所以容量允许的话给 Prometheus 建一个最精简的 RBAC 角色。监控需要多少权限就授多少不要顺手扩权。5.2 系列第三部分前的一个预告这一部分的重点是把 scrape 和认证鉴权打牢。你能稳定拿到 apiserver 的指标之后才算具备拆解指标语义的基础。接下来我会在这个系列里逐个讲apiserver_request_total怎么按返回码拆分量、apiserver_request_duration_seconds里 histogram 的分位计算方式、etcd 访问延迟怎么关联到 apiserver 的 P99 抖动以及常见的 SLO 规则写法。这些内容完全依赖前面能抓到数据所以如果你还在 403 阶段挣扎先把这一章的配置吃透再说。我实际操作下来的体会是kubernetes-apiservers 这个 job 算是最容易出问题的但只要你把认证鉴权和抓取配置一次性打对后面所有的 apiserver 监控动作都会非常顺。多花半小时在证书和 RBAC 的验证上好过之后反复排查 darkness 半天。