本篇是《Prometheus 从入门到生产实践》第 16 / 24 篇。配置以文中版本与环境前提为准,实验和故障均为教学场景。完整程序、配置及校验范围见配套实验包。

在 Kubernetes 中,Pod 地址会变化,手工维护静态 IP 很快失效。Kubernetes 服务发现的作用是从 API 对象得到候选目标,再由 relabel 选择真正要抓取的地址。它不会自动给应用添加指标,也不会代替访问权限和网络连通性。
1. 先决定要观察哪一层
| 组件或数据源 | 主要反映什么 |
|---|---|
| node-exporter | 节点操作系统、文件系统、CPU 等主机指标 |
| kubelet/cAdvisor 相关指标 | 容器运行时资源使用,具体端点和可用性受集群版本及配置影响 |
| kube-state-metrics | 从 Kubernetes 对象状态生成指标,例如期望副本、Pod 状态 |
| 应用自身 metrics | 请求量、错误率、延迟与业务自定义状态 |
kube-state-metrics 不等于实时 CPU 采样,应用的 up=1 也不保证 Deployment 已达到期望副本。先选清楚问题,再选指标来源。组件定位可参考 kube-state-metrics 项目说明。
2. 本篇实验前提
本篇切换到独立测试集群,假设 Prometheus 已在 monitoring-lab 命名空间中运行。给它使用的 Pod 配置 serviceAccountName: prometheus-discovery,并具备访问 Kubernetes API 与目标 Pod 网络的路径。
demo-lab 中需要已有应用 Pod,标签为 app=demo,容器端口命名为 metrics,实际端口 19100,指标服务监听 Pod 可达地址。前面本机 exporter 的回环监听不能直接拿来充当跨 Pod 服务,需要为集群部署调整监听与镜像。
这些前提不满足时,先完成应用部署和网络验证,不能只应用一个 scrape 配置就期待出现指标。
3. 给服务发现授权,不给整个集群管理员权限
应用前先检查同名测试命名空间没有其他用途,然后保存 b-k8s-rbac.yml:
apiVersion: v1
kind: Namespace
metadata:
name: monitoring-lab
---
apiVersion: v1
kind: Namespace
metadata:
name: demo-lab
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus-discovery
namespace: monitoring-lab
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: prometheus-discovery
namespace: demo-lab
rules:
- apiGroups: ['']
resources: ['pods', 'services']
verbs: ['get', 'list', 'watch']
- apiGroups: ['discovery.k8s.io']
resources: ['endpointslices']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prometheus-discovery
namespace: demo-lab
subjects:
- kind: ServiceAccount
name: prometheus-discovery
namespace: monitoring-lab
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prometheus-discovery
Role 只在 demo-lab 内授权读 Pod、Service、EndpointSlice,不允许读 Secret、改工作负载或访问所有节点。这里是同时支持下文两种发现方式的权限组合;如果只用 Pod 发现,可以进一步移除不需要的资源权限。
使用拥有相应管理权限的测试账户验证:
kubectl auth can-i list pods -n demo-lab \
--as=system:serviceaccount:monitoring-lab:prometheus-discovery
kubectl auth can-i list endpointslices.discovery.k8s.io -n demo-lab \
--as=system:serviceaccount:monitoring-lab:prometheus-discovery
执行者还需要 impersonation 权限,命令自身被拒绝不直接说明目标 ServiceAccount 缺少发现权限。外部 Prometheus 则需要独立的 API 地址、CA 与凭据配置,不能假定本机存在集群内 token 文件。
4. 第一种方式:按 Pod 及命名端口发现
合并这个 scrape_configs 片段:
scrape_configs:
- job_name: k8s-demo-pods
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['demo-lab']
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: demo
- source_labels: [__meta_kubernetes_pod_container_port_name]
action: keep
regex: metrics
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
Pod 发现可能为多个容器端口产生候选目标,所以显式保留 metrics 端口和 app=demo。仅按 app 筛选,却不限制端口,可能对同一个 Pod 的其他端口发出无意义抓取。
这里没有过滤 Pod Ready,便于看见尚未就绪目标的抓取状态。是否只采集 Ready 端点是监控语义选择;如果把异常对象过滤掉,就应另有对象状态监控,否则失败可能变成“没有目标”。
5. 第二种方式:从 Service 的 EndpointSlice 发现
应用已有符合条件的 Pod 后,可创建配套 Service:
# Requires existing pods in demo-lab with app=demo and a named metrics port.
apiVersion: v1
kind: Service
metadata:
name: demo-metrics
namespace: demo-lab
labels:
app: demo
spec:
selector:
app: demo
ports:
- name: metrics
port: 19100
targetPort: metrics
再选择 EndpointSlice 发现:
scrape_configs:
- job_name: k8s-demo-services
kubernetes_sd_configs:
- role: endpointslice
namespaces:
names: ['demo-lab']
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app]
action: keep
regex: demo
- source_labels: [__meta_kubernetes_endpointslice_port_name]
action: keep
regex: metrics
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: service
这里只展示两种替代方案,初次实验选择一种,避免同一应用被两个 job 重复抓取。EndpointSlice 方式不是只抓 Service ClusterIP,而是根据发现结果定位后端地址;应用只有一个 metrics 端口时,通常每个后端对应一个选中的目标。
EndpointSlice 角色还可能从关联 Pod 推导额外端口,因此继续保留端口名过滤。元标签和 API 版本要求见 Kubernetes SD 官方参考。
6. 发现成功后,还要考虑数据的生命周期
滚动更新会让旧 Pod 退出、新 Pod 加入。服务发现更新并非与所有对象变化同时完成,旧目标和新目标短暂重叠也可能是合理过程。应在一段观察窗口中核对更新结果,而不是把瞬时目标数量变化都认定为异常。
同时,Pod 名称、UID、镜像版本和任意业务注解都可能变化。把所有元标签无差别复制到时间序列,会扩大标签组合与历史序列数量。更适合长期聚合的通常是稳定的环境、集群、命名空间和服务维度,具体容器实例维度则按排障需要保留。
集群之间的同名命名空间和服务也可能产生混淆。如果多个集群的数据进入同一查询层,需要稳定的集群身份,不能仅凭 namespace 和 service 断言是同一个对象。这是标签设计问题,不应等到高可用或远程存储接入后才补救。
7. 两种权限错误不要互相代替
访问 API 被拒绝,意味着发现阶段没有拿到相应对象;访问指标端点被拒绝,则发生在抓取阶段。前者查服务账户和资源权限,后者查目标认证、监听与网络策略。给前者加采集密码或给后者加集群管理员权限,都未必接近真正原因。
在集群外运行 Prometheus 时,能连上 API 服务不等于能直达 Pod 网段。API 返回一个正确的 Pod 地址,但采集器没有对应路由,结果仍然是抓取超时。需要按网络架构选用可达路径或将采集端放到合适位置,而不是不断修改发现标签。
证书校验也有两个对象:连接 Kubernetes API 的信任,与连接 HTTPS exporter 的信任。这两侧可能由不同的 CA 签发,使用不同的服务名称,不应把一个统一的“忽略 TLS 错误”当成配置方案。
最后要记住,对象状态指标本身也有时效性。某个副本数不符可能来自部署变更、对象尚未协调或采样延迟;结合当前 Kubernetes 对象和事件,才能解释指标是在描述什么阶段。监控数据提供证据,但不会消除分布式系统的观察延迟。
8. 按“对象、发现、网络、数据”验收
先检查对象:
kubectl -n demo-lab get pods -l app=demo -o wide
kubectl -n demo-lab get service demo-metrics
kubectl -n demo-lab get endpointslices \
-l kubernetes.io/service-name=demo-metrics
再看 Prometheus Service Discovery 中的原始元标签、保留/丢弃结果和最终 URL。目标不存在优先查权限、对象和 relabel;目标存在但 down,再查监听、路径、TLS、认证和 NetworkPolicy。
NetworkPolicy 是否真正生效取决于 CNI 支持与集群配置。不要用创建了一个 YAML 就宣称网络已经隔离;需要从 Prometheus 的实际网络位置验证允许和拒绝两条路径。
最后查询选定 job 的 up 和应用业务指标,并在滚动替换一个测试 Pod 后确认地址更新。能在对象变化后继续发现正确目标,才是动态发现真正带来的收益。
还可以把发现到的目标数量与对象清单逐项对应。每个符合选择条件、暴露相应端口的应用后端,应有可解释的最终抓取地址;多出来的目标要查是否另一个容器端口也被选中,缺少的目标则追到具体选择条件。这个核对能把“图上有数据”提升为“预期范围被完整覆盖”。完成实验后保留配置、权限清单和目标样本,后续升级发现方式或控制器时就有可比较的基准。同时标记各项检查实际运行的网络位置,避免混用外部与集群内探测结果。
上一篇:安全边界:TLS、认证、凭据与管理 API · 系列目录 · 下一篇:Prometheus Operator:选择器、ServiceMonitor 与规则管理