不能用裸Deployment跑Prometheus,因其TSDB有状态,WAL和block数据存于emptyDir或内存,Pod重启或迁移导致监控数据清零,rate()等依赖历史的查询失效,且多副本会争抢targets;必须用StatefulSet+PV或Prometheus Operator。

直接上生产环境别用单实例 Prometheus Deployment,它没持久化、不支持高可用、服务发现一崩全挂——必须用 Prometheus Operator 或至少 StatefulSet + 持久卷。
为什么不能用裸 Deployment 跑 Prometheus
Deployment 会把 Prometheus 当作无状态服务调度,但它的 TSDB 是有状态的:WAL 日志、block 数据都写在内存或 emptyDir 里。Pod 重启或节点迁移后,所有监控数据清零,rate()、histogram_quantile() 这类依赖历史窗口的查询直接失效。
常见错误现象:
- 刚部署完能查到指标,过几小时
container_cpu_usage_seconds_total突然断崖式归零 -
Alertmanager收不到告警,查日志发现Prometheus报context deadline exceeded—— 实际是 WAL replay 失败卡住 - 扩容副本数后两个实例互相抢 targets,
kubernetes-podsjob 出现重复采集
正确做法:
- 用
StatefulSet管理,绑定PersistentVolume(推荐storageClassName: "gp2"或本地 SSD) - 设置
prometheusSpec.retention: "30d"防止磁盘爆满 - 禁用
emptyDir:删掉配置里所有emptyDir: {}的 volume 定义
ServiceMonitor 不生效的三个硬坑
ServiceMonitor 是 Prometheus Operator 动态发现服务的核心,但它不工作往往不是 YAML 写错,而是权限和标签链断了。
典型故障场景:
- 明明写了
selector.matchLabels: {app: "my-app"},但Prometheus就是不拉取该 Service 的 endpoints -
kubectl get servicemonitor -n myns能看到资源,kubectl get prometheus -n monitoring却显示serviceMonitorSelector匹配数为 0
检查顺序(缺一不可):
- 确认
PrometheusCR 中设置了serviceMonitorSelector: {matchLabels: {release: "prometheus"}},且你的ServiceMonitor有对应 label:labels: {release: "prometheus"} - 确认
Service和ServiceMonitor在同一 namespace,或PrometheusCR 开启了跨 namespace 发现:serviceMonitorNamespaceSelector: {} - 确认目标
Service的ports[0].name和ServiceMonitor.endpoints[0].port完全一致(大小写敏感!比如metrics≠Metrics)
Node Exporter 必须用 DaemonSet,但别漏掉 tolerations
node-exporter 要采集节点级指标(CPU、磁盘、网络),必须每个 Node 上跑一个 Pod。用 DaemonSet 是唯一合理方式,但默认调度器会把它拒之门外。
原因:
- 控制平面节点(master/control-plane)通常打了
node-role.kubernetes.io/control-plane:NoSchedule污点 -
node-exporterPod 没配tolerations,被调度器直接忽略
实操配置片段(必须加):
tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule
漏掉这个,node-exporter 在 master 节点上永远是 0/1 Pending 状态,node_cpu_seconds_total 就少三分之一数据源。
Grafana 数据源 URL 别写 localhost
很多人在 Grafana 的 ConfigMap 里把 Prometheus 地址写成 http://localhost:9090 或 http://127.0.0.1:9090,结果 Grafana 页面一直报 Data source is not available。
根本原因:
- Grafana Pod 和 Prometheus Pod 是不同容器,
localhost指的是 Grafana 自己的 network namespace - Kubernetes 集群内服务通信必须用 Service DNS 名:格式是
http://<service-name>.<namespace>.svc.cluster.local:<port>
正确示例(假设 Prometheus Service 名为 prometheus-operated,在 monitoring 命名空间):
url: http://prometheus-operated.monitoring.svc.cluster.local:9090
如果用了 kube-prometheus-stack Helm Chart,默认 Service 名是 prometheus-k8s,那就得写 http://prometheus-k8s.monitoring.svc.cluster.local:9090。
最后提醒一句:所有 ServiceMonitor、PodMonitor、PrometheusRule 的命名空间绑定逻辑,和 RBAC 权限、CRD 版本(v1 vs v1beta1)、Operator 版本强耦合,升级前务必查官方 compatibility matrix —— 这个细节八成运维都跳过去,然后花半天 debug 为什么新规则不加载。


















