Prometheus 监控体系¶
Prometheus 不只是一个抓指标的进程,而是一套从服务发现、抓取、存储、告警到自定义指标的完整体系。本章从拉取模型讲起,用 kube-prometheus-stack 把 Operator 的 CRD 跑通,再让 HPA 真正读到业务指标。
你将学到¶
- 用指标名、标签和时间戳描述一条时间序列,理解拉取模型与 ServiceMonitor 的关系。
- 区分
prometheus-community/prometheus与kube-prometheus-stack两条部署路线。 - 用 Prometheus、ServiceMonitor、PodMonitor、Probe、PrometheusRule 这些 CRD 声明监控对象。
- 部署 prometheus-adapter,让 HPA 通过
custom.metrics.k8s.io读取业务指标。 - 用 blackbox exporter 做 HTTP 黑盒探测,并写出可用的告警规则。
为什么需要它¶
Kubernetes 的动态性让传统监控很难受:Pod 的 IP 和名字一直在变,手工维护抓取目标不现实;业务希望“请求量超过阈值就告警”,运维希望“节点磁盘四小时后写满就告警”,这些都需要一套能自动发现目标、长期保存样本、按规则求值的系统。
Prometheus 用拉取模型解决了服务发现问题,用标签解决了多维查询问题,用 PromQL 把“指标”变成了可计算的信号。但它本身不负责日志,也不负责展示,所以生产上通常和 Grafana、Alertmanager、日志系统一起出现。本章只讲指标链路,日志见 日志与 ELK / Loki,弹性伸缩如何消费这些指标见 调度、资源与弹性。
核心概念¶
数据模型与拉取模型¶
Prometheus 的一条样本由“指标名 + 一组标签”标识时间序列,再加上时间戳和浮点值,例如 http_requests_total{job="demoapp",status="200"} 1024 1700000000。标签是查询和聚合的核心,也是基数的来源:给每个请求 ID 打标签会迅速撑爆内存,这是 Prometheus 最常见的误用。
Prometheus 主动按 scrape_interval 拉取目标,而不是等应用推送。目标可以来自静态配置、Kubernetes SD、Consul 等;抓取前通过 relabel_configs 过滤和改写标签,抓取后通过 metric_relabel_configs 丢弃不需要的指标。Pushgateway 只用于短生命周期批任务,不能当成通用推送网关。
部署路线¶
仓库里的 Prometheus/prom-values.yaml 面向 prometheus-community/prometheus Chart,直接写 scrape_configs;helm-examples/kube-prometheus/ 是更早的 Bitnami Chart,rbac.pspEnabled 对应的 PSP 已在 v1.25 移除,不要照抄。现行推荐 kube-prometheus-stack:把 Operator、Prometheus、Alertmanager、Grafana 和 exporters 打包,用 CRD 声明监控对象。
Operator 的 CRD¶
Prometheus Operator 监听以下资源,把声明翻译成实际的 StatefulSet、ConfigMap 和 Secret:
Prometheus、Alertmanager、ServiceMonitor、PodMonitor、Probe、PrometheusRule 和 AlertmanagerConfig 覆盖了实例、抓取、探测与告警四类对象。
Prometheus 资源通过 serviceMonitorSelector、podMonitorSelector、probeSelector 和 ruleSelector 决定吸收哪些对象。Helm Chart 默认只选择带 release: <release-name> 标签的对象;实验里把 *NilUsesHelmValues 设为 false 可以放宽到所有命名空间,生产环境则应保留标签约束。
ServiceMonitor 与 Probe¶
ServiceMonitor 的 endpoints[].port 写的是 Service 的端口名,也可以用 targetPort 匹配端口号;path 默认 /metrics,interval 覆盖全局抓取间隔。jobLabel 指定用 Service 的哪个标签作为 job 标签,没写时通常退化为 Service 名。跨命名空间选择需要 namespaceSelector,否则只在自身命名空间生效。
Probe 把黑盒探测也声明化了:Operator 生成类似 /probe?target=<url>&module=http_2xx 的抓取任务,目标 URL 通过 relabel 放进 __param_target,真正的抓取地址指向 blackbox exporter。这样新增一个探测目标只需要加一行 YAML,不用改 Prometheus 配置。
prometheus-adapter:把 Prometheus 指标接进 API¶
HPA 只认识 Kubernetes 的指标 API,不认识 PromQL。prometheus-adapter 通过 APIService 注册 custom.metrics.k8s.io/v1beta1 和 external.metrics.k8s.io/v1beta1,把 PromQL 查询结果翻译成 MetricValueList;这里的 v1beta1 是聚合 API 的版本,不是已移除的 workload beta API。它的规则分四步:seriesQuery 发现指标,resources 关联 Kubernetes 对象,name 决定对外暴露的名字,metricsQuery 生成实际查询。
自定义指标(custom)关联到 Pod、Namespace 等对象,适合“每个 Pod 的 QPS”;外部指标(external)不关联对象,适合“队列积压数”。HPA 的 autoscaling/v2 分别用 type: Pods、Object、External 消费它们。仓库 prom-adapter-values.yaml 里的 http_requests_total → http_requests_per_second 就是典型例子。
告警与 PromQL¶
PrometheusRule 里的 alert 规则在 expr 为真并持续 for 指定时间后进入 firing,带上 labels 和 annotations 发给 Alertmanager;record 规则则预计算一个指标,供看板和告警复用。Alertmanager 负责分组、抑制、静默和路由,默认配置只有一个 null 接收器,所以“告警触发了但没人收到”通常是路由没配。
| 场景 | PromQL |
|---|---|
| 请求速率 | sum(rate(http_requests_total{job="demoapp"}[5m])) |
| 错误率 | sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) |
| P99 延迟 | histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))) |
| 容器 CPU | sum by (pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m])) |
| 实例存活 | up{job="demoapp"} == 0 |
flowchart LR
APP[demoapp /metrics] --> SM[ServiceMonitor]
KSM[kube-state-metrics] --> P[Prometheus]
NE[node-exporter] --> P
SM --> P
P --> AM[Alertmanager]
P --> G[Grafana]
P --> AD[prometheus-adapter]
AD --> HPA[HPA]
图:Prometheus 从各类目标拉取指标,向上供 Grafana 展示、向 Alertmanager 告警、向 adapter 供给 HPA。
动手实验¶
实验:安装 kube-prometheus-stack 并接入 demoapp
目标:用 Helm 安装 Operator 体系,创建 ServiceMonitor 抓取 demoapp,验证 custom metrics API,安装 adapter 并创建一个 Probe。
步骤 1 · 安装 kube-prometheus-stack
grafana:
adminPassword: admin
prometheus:
prometheusSpec:
serviceMonitorSelectorNilUsesHelmValues: false
probeSelectorNilUsesHelmValues: false
ruleSelectorNilUsesHelmValues: false
# 实验环境用 emptyDir;生产用 storageSpec 挂 PVC
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespace -f kube-prometheus-values.yaml
kubectl -n monitoring get pod -l app.kubernetes.io/name=prometheus
步骤 2 · 创建 ServiceMonitor 抓取 demoapp
先用命令拉起 demoapp,再单独声明 ServiceMonitor:
kubectl create deployment demoapp --image=ikubernetes/metrics-app --replicas=2
kubectl expose deployment demoapp --port=80 --target-port=80
kubectl label service demoapp app=demoapp
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: demoapp
labels: {release: prometheus}
spec:
jobLabel: app
selector:
matchLabels: {app: demoapp}
endpoints:
- targetPort: 80
path: /metrics
kubectl apply -f demoapp-servicemonitor.yaml
kubectl -n monitoring get servicemonitor demoapp
kubectl -n monitoring port-forward svc/prometheus-kube-prometheus-prometheus 9090:9090 &
curl -s 'http://127.0.0.1:9090/api/v1/query?query=up%7Bjob%3D%22demoapp%22%7D' \
| jq -r '.data.result[].value[1]'
预期输出 1,表示两个 demoapp Pod 都在被成功抓取。打开 Prometheus UI 的 Status → Targets,能看到 demoapp 这个 job。
步骤 3 · 安装并验证 prometheus-adapter
prometheus:
url: http://prometheus-kube-prometheus-prometheus.monitoring.svc
port: 9090
rules:
default: true
custom:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: namespace}
pod: {resource: pod}
name: {matches: "^(.*)_total", as: "${1}_per_second"}
metricsQuery: rate(<<.Series>>{<<.LabelMatchers>>}[1m])
helm install prometheus-adapter prometheus-community/prometheus-adapter \
-n monitoring -f prometheus-adapter-values.yaml
adapter 启动后需要几十秒发现指标,确认 API 已注册:
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 \
| jq -r '.resources[].name' | grep http_requests
步骤 4 · 创建 Probe 黑盒探测
helm install blackbox prometheus-community/prometheus-blackbox-exporter \
-n monitoring --set fullnameOverride=blackbox-exporter
apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
name: demoapp-http
namespace: monitoring
spec:
jobName: demoapp-http
module: http_2xx
prober:
url: blackbox-exporter.monitoring.svc:9115
scheme: http
path: /probe
targets:
staticConfig:
static:
- http://demoapp.default.svc.cluster.local
kubectl apply -f demoapp-probe.yaml
kubectl -n monitoring get probe demoapp-http
curl -s 'http://127.0.0.1:9090/api/v1/query?query=probe_success%7Bjob%3D%22demoapp-http%22%7D' \
| jq -r '.data.result[].value[1]'
预期输出 1。把 targets 改成一个不存在的域名,再查询会得到 0,这就是黑盒探测的价值:从集群外部视角验证“服务是否真的可访问”。Grafana 可用 kubectl -n monitoring port-forward svc/prometheus-grafana 3000:80 打开,admin/admin;告警规则用 PrometheusRule 声明,默认 Alertmanager 是 null 接收器,需要另配路由。
验证清单¶
-
kubectl -n monitoring get pod中 Prometheus、Alertmanager、Grafana、adapter 均 Running。 - ServiceMonitor 的
kubectl get endpoints demoapp有地址,Prometheus Targets 中demoapp为 UP。 -
/apis/custom.metrics.k8s.io/v1beta1能列出http_requests_per_second。 -
probe_success{job="demoapp-http"}的值为 1。
常见问题与排错¶
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| ServiceMonitor 创建了但 Targets 里没有 | 标签选择器不匹配,或 *NilUsesHelmValues 仍为 true |
给对象加 release 标签,或关闭 NilUsesHelmValues |
custom.metrics.k8s.io 返回 404 |
adapter 未启用或未就绪 | 查 adapter Pod 日志,确认 APIService 的 AVAILABLE 为 True |
| 有指标但 HPA 读不到 | 指标没有关联到 Pod/Namespace,或查询为空 | 检查 adapter 规则的 resources 与 metricsQuery |
Probe 一直 probe_success 0 |
目标不可达、模块名错误、blackbox Service 名不对 | 直接 curl blackbox 的 /probe?target=...&module=http_2xx |
| 告警触发但没收到 | Alertmanager 仍是 null receiver,或路由不匹配 | 配置 receiver 与 route,检查 Alertmanager 的 silences |
进阶挑战¶
- 用 PodMonitor 抓取一个没有 Service 的工作负载,比较它和 ServiceMonitor 的 relabel 差异。
- 给 adapter 增加一条规则,把
http_request_duration_seconds暴露成 P99 指标,并让 HPA 基于它扩容。
小结¶
- Prometheus 用“指标名 + 标签”标识时间序列,主动拉取目标;标签基数决定内存成本。
prometheus-community/prometheus适合快速验证,生产优先kube-prometheus-stack的 Operator 模式。- ServiceMonitor/PodMonitor/Probe 把抓取目标声明化,PrometheusRule 把告警和记录规则声明化。