深色模式
Prometheus 部署与架构:在 K8s 中落地指标采集
摘要:本文面向生产 SRE / 平台工程师,讲解 Prometheus 在 Kubernetes 中的两种主流落地方式、pull 抓取模型的原理、ServiceMonitor/PodMonitor 如何实现"按 label 自动发现目标",以及单实例的容量边界与高可用方案(Thanos/Agent)。覆盖
monitoring.coreos.com/v1CRD、抓取配置、target 排障与回滚。适用 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+
- 组件:
prometheus-operator(CRDmonitoring.coreos.com/v1)、kube-prometheus-stack(Helm)、Prometheus/ServiceMonitor/PodMonitor/PrometheusRule - 前提:具备
monitoring命名空间与创建 CRD 的权限;被监控 Pod 已暴露/metrics
背景与问题
Prometheus 是 CNCF 毕业项目,Kubernetes 生态的事实标准 TSDB。它用 pull 模型:服务端主动周期抓取目标 /metrics,而非目标上报。这带来几个关键收益:被监控方无状态、抓取节奏由服务端统一控制、目标故障可由"抓取失败"直接暴露。
但在 K8s 里直接写 scrape_configs 静态 IP 不现实——Pod IP 每天都变。核心问题是:如何让 Prometheus 自动发现"此刻哪些 Pod 该被抓、抓哪个端口"。答案就是 Prometheus Operator 引入的 ServiceMonitor / PodMonitor。
核心概念
Prometheus Operator 管理的 CRD 分两组:
- Instance-Based(管生命周期):
Prometheus、Alertmanager、ThanosRuler、PrometheusAgent。 - Config-Based(管抓取/规则配置):
ServiceMonitor、PodMonitor、Probe、ScrapeConfig、PrometheusRule、AlertmanagerConfig。
Prometheus CRD 通过 serviceMonitorSelector 等 selector 决定纳入哪些 ServiceMonitor,Operator 据此动态生成 Prometheus 配置 Secret并热加载,无需重启。
ServiceMonitor 的发现链
ServiceMonitor(按 label 选 Service)→ Service 选 Pod 生成 Endpoints → Operator 把 Endpoints 写进 Prometheus scrape 配置。注意 endpoints(小写,ServiceMonitor 字段)与 Endpoints(大写,K8s 对象)是不同东西。跨命名空间抓取需 namespaceSelector: { any: true }。
架构与原理
原理要点:
- 抓取周期:
scrape_interval(默认 1m)到达时,Prometheus 从 Service 的 Endpoints 拿到 Pod IP:port,发起 HTTP GET/metrics,解析文本格式样本。 - 样本模型:每条样本是
metric_name{label="v"} value timestamp。label 基数 = 不同 label 组合的数量,是容量第一杀手。 - 存储:默认本地 TSDB(PVC),
retention控制留存;大规模用remote_write到 Thanos/Cortex/Mimir 或对象存储。
生产实践:用 kube-prometheus-stack 部署
最稳的起步是 kube-prometheus-stack Helm chart,它部署 Operator + Prometheus + Alertmanager + Grafana + node-exporter + kube-state-metrics + 一套默认规则与看板。
bash
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kps prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespace \
--set prometheus.prometheusSpec.retention=15d \
--set prometheus.prometheusSpec.resources.requests.memory=1Gi1
2
3
4
5
6
2
3
4
5
6
容量边界(单 Prometheus 实例)
单个 Prometheus 实例的水平扩展能力有限:官方社区经验,单实例在百万级活跃序列、数百万样本/秒就会遇到内存与查询瓶颈([未实测],实际取决于节点规格、采集间隔、label 基数)。超过此规模应:
- 用
ServiceMonitor的label把不同团队/业务分片到多个Prometheus实例(水平分片); - 或引入 Thanos Sidecar + 对象存储做长期存储与全局查询;
- 或采用
PrometheusAgent(只抓取+remote_write,不本地存算,适合边缘/大规模)。
操作步骤
步骤 1:暴露应用指标
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
namespace: demo
spec:
replicas: 3
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
- name: example-app
image: quay.io/brancz/prometheus-example-app:v0.5.0
ports:
- name: web # 命名端口,ServiceMonitor 引用此名
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: example-app
namespace: demo
labels:
app: example-app
spec:
selector:
app: example-app
ports:
- name: web
port: 8080
targetPort: web1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
步骤 2:用 ServiceMonitor 让 Prometheus 自动发现
yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: example-app
namespace: demo
labels:
team: frontend # 被 Prometheus.spec.serviceMonitorSelector 选中
spec:
selector:
matchLabels:
app: example-app
namespaceSelector:
matchNames:
- demo
endpoints:
- port: web # 必须是 Service 里定义的端口名
interval: 30s
path: /metrics1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
步骤 3:让 Prometheus 实例纳入该 ServiceMonitor
yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: prometheus
namespace: monitoring
spec:
serviceAccountName: prometheus
serviceMonitorSelector:
matchLabels:
team: frontend # 与上面 ServiceMonitor 的 label 对应
serviceMonitorNamespaceSelector: {} # 空=只选同命名空间;跨 ns 用 matchExpressions
resources:
requests:
memory: 800Mi
enableAdminAPI: false
retention: 15d1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PodMonitor 的无 Service 场景
当目标没有 Service(如 DaemonSet、第三方 Pod)时,用 PodMonitor 直接按 Pod label 抓取,字段 podMetricsEndpoints 替代 endpoints,其余语义一致。
步骤 4:验证
bash
# 1. 确认 ServiceMonitor 被 Prometheus 选中(status 中可见)
kubectl get servicemonitor -n demo
kubectl get prometheus -n monitoring -o yaml | grep -A3 serviceMonitorSelector
# 2. 在 Prometheus UI 查看 target 状态(应为 up)
kubectl port-forward svc/prometheus-operated 9090 -n monitoring
# 浏览器打开 http://localhost:9090/targets
# 3. 查询样本
kubectl exec -n monitoring deploy/prometheus-k8s -- \
promtool query instant http://localhost:9090 'up{job="example-app"}'1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
回滚与清理
生产危险
删除 Prometheus CR 会触发 Operator 删除对应 StatefulSet 与 PVC,数据不可恢复。清理前先备份 PVC 或确认已 remote_write 到外部存储。
bash
# 仅删除示例资源,勿对生产 monitoring 命名空间执行
kubectl delete servicemonitor example-app -n demo
kubectl delete -n demo deploy/example-app svc/example-app1
2
3
2
3
故障排查
| 现象 | 根因 | 处理 |
|---|---|---|
target down | Service 无 Endpoints | kubectl get endpoints example-app -n demo,确认 Pod label 匹配 Service selector |
| 抓取 404 | path 错或端口名不匹配 | 核对 ServiceMonitor port 名 == Service ports[].name;path 默认 /metrics |
scrape timeout | 指标体量过大/间隔过短 | 增大 scrape_timeout 或降低 scrape_interval |
| series 暴涨 OOM | label 含高基数(pod、le、user_id) | 用 metric_relabel_configs 丢弃/聚合标签;见下 |
| 配置未生效 | selector 不匹配 | 检查 serviceMonitorSelector 与 ServiceMonitor labels 是否一致 |
用 metric_relabel_configs 在抓取端降基数:
yaml
# ServiceMonitor.endpoints 内
metricRelabelings:
- sourceLabels: [__name__]
regex: 'go_gc_.*'
action: drop1
2
3
4
5
2
3
4
5
性能、容量与成本
- 采集间隔:基础设施 15s 合理,业务 KPI 无需 15s,烧写容量。
- 内存:与活跃序列数近似线性;
retention越大磁盘越多,WAL 也占空间。 - 远程写:高基数下
remote_write队列可能堆积,调queue_config的max_shards/capacity。 - 成本权衡:本地盘便宜但无全局查询;Thanos 对象存储按量付费但引入查询组件复杂度。中小集群单机 + PVC 足够。
常见坑
- ServiceMonitor
namespaceSelector默认只选同命名空间,跨 ns 必须显式配any: true或matchNames。 - 把
port写成数字(如8080)而 Service 用的是命名端口 → 配置生成失败。 enableAdminAPI: true在生产开启有安全风险(可删除数据),保持 false。- 用
kubectl edit prometheus改配置不会持久,应通过 CR 或 Helm values 管理(Git 化)。
替代方案与权衡
- VictoriaMetrics:兼容 PromQL、更高压缩比与写入吞吐,单二进制易运维;代价是生态/规则语法细节差异。
- Prometheus Agent 模式:只采集+remote_write,无本地告警/存储,适合超大规模分片与边缘。
- Managed Prometheus(云厂商):免运维,代价是出口费用与跨云锁定。
FAQ
Q:ServiceMonitor 和直接在 prometheus.yml 写 scrape_configs 有何区别? A:前者是声明式、按 label 自动发现、热加载、无需手碰 Secret;后者是静态配置,适合集群外目标用 ScrapeConfig CR 补充。
Q:一个 Service 多个端口都抓? A:ServiceMonitor 的 endpoints 是数组,可列多个 port,各自独立 interval/path。
参考资料
- Prometheus Operator 官方文档 - Design,访问日期:2026-10-08。
- Prometheus Operator 官方文档 - Getting Started(ServiceMonitor/PodMonitor),访问日期:2026-10-08。
- Prometheus 官方文档 - Configuration(scrape_config),访问日期:2026-10-08。
- kube-prometheus-stack Helm Chart,访问日期:2026-10-08。
- Kubernetes 官方文档 - Resource metrics pipeline(cAdvisor/kubelet/metrics-server),访问日期:2026-10-08。