做 K8s 集群运维,特别是公司内部已经有多个业务线在上面跑之后,我最先意识到的问题不是“如何发布服务”,而是“看不见集群里正在发生什么”。某个命名空间里的 Deployment 因为一次探活失败被不断重启,节点磁盘使用率悄悄突破 90%,业务高峰期 HPA 自动把副本从 2 扩到 20,这些现象如果只能靠用户投诉和半夜响应来发现,那排查成本会高得离谱。所以,给 Kubernetes 补上可视化监控告警,我把它放在了所有平台建设的前置位。整个体系的第一环,就是先把 Prometheus 部署起来,把指标采集链路打通;只有数据进来了,后面的 Grafana 面板、Alertmanager 通知才有意义。
这篇内容面向的是已经能正常使用 K8s、但还没有成体系监控的团队。我会从部署前的思路、环境准备、具体安装步骤、核心配置拆解一直到常见问题的排查,完整记录一次 kube-prometheus-stack 的落地过程。标题里的【1】不是套路,这个系列我确实打算分几篇来写,这一篇专心解决“采得到”的问题。
1. 项目概述与部署前思考
1.1 动态环境下的监控逻辑
K8s 的调度逻辑决定了监控体系和传统服务器监控有本质区别。以前监控一台物理机,只需要固定 IP、固定端口,Agent 装好之后就是一个稳定目标;但 K8s 里 Pod 随时可能被销毁、重建、漂移到另一台节点,Service 的 Endpoint 列表也在实时变化。如果继续用“手工添加目标”的方式采集,维护量会失控,而且漏掉临时任务和单副本实例几乎是必然的。
Prometheus 模型天然适配这种动态环境。它采用主动拉模式(Pull),监控服务只需要暴露一个 HTTP 的 /metrics 端点,Prometheus 服务端会通过 Kubernetes API 的服务发现机制,持续发现新的 Pod、Service、节点等目标并采集指标。你可以把它理解成一套“自动点名系统”:不关心同学坐在哪个教室,只要名单里能找到 TA 就行。更关键的是,Prometheus 数据模型里每个指标都带有标签,比如 Pod 名、命名空间、节点名都可以作为维度,查询时能以任意维度做聚合和过滤,这比传统监控的数值列表灵活得多。
这也解释了为什么很多云原生监控方案都会围绕 Prometheus 来做:它不只是一个监控软件,更像是一套能被 K8s 原生管理的数据采集标准。我们不需要在每个 Pod 里装 Agent,业务应用只要引入对应语言客户端或自己输出 Prometheus 格式指标即可,成本低、接入也统一。
1.2 选型:用全套整合栈而不是零散组件
确定用 Prometheus 之后,紧接着是选择部署形态。我第一版只用 Helm 安装了单独的 Prometheus Server,ServiceMonitor 和告警规则全靠手工维护,结果业务隔三差五新增服务,监控配置也跟着改,很快就乱成一团。后来切到 kube-prometheus-stack,才算是把这一块理顺。
kube-prometheus-stack 本质上是一套预打包的监控解决方案,核心由 Prometheus Operator 驱动。Operator 用 CRD(CustomResourceDefinition)把监控目标、告警规则、通知配置都抽象成了 K8s 资源。比如 ServiceMonitor 定义“采集哪些 Service”、PrometheusRule 定义“什么情况触发告警”、AlertmanagerConfig 定义“告警往哪里发”。我们提交这些 YAML 后,Operator 负责把资源配置变成底层 Prometheus 实例的配置。这种声明式管理方式,让监控配置和业务部署一样走 Git 流程,可以 review、可以回滚。
除了 Prometheus Operator,这个 chart 还把 Grafana、Alertmanager、kube-state-metrics、node-exporter 都打包了。第一次部署时可能会觉得组件很多,但实际运行起来会发现它们职责非常清晰:Prometheus 管指标采集与存储,Grafana 管展示,Alertmanager 管告警分发,kube-state-metrics 负责把 K8s 对象状态转换成指标,node-exporter 负责暴露节点级指标。作为系列第一篇,我建议不要只装一个裸 Prometheus,而是直接装整套栈,把后面可视化、告警需要的组件一次到位,后面两篇文章就可以直奔主题,不用再回过头补组件。
2. 部署前环境准备
2.1 版本清点与兼容性
Prometheus Operator 依赖 Kubernetes API 的能力,版本不匹配会直接导致 CRD 安装失败或者控制器报错。所以安装前,我习惯先记录下当前集群版本和 Helm 版本:
kubectl version --short helm version如果你的 Kubernetes 是 1.26 或更高,当前主流的 kube-prometheus-stack 版本基本都能兼容;如果还在 1.20 以下,就需要去 chart 的 README 里查看对应的支持矩阵。不要觉得版本检查很多余,社区 issue 里最常见的一类问题就是把新版 chart 装到旧集群,然后 operator 的日志里充满 unknown field。
另外,还需要确认当前 kubectl 权限是否可以创建命名空间、StatefulSet、CRD。有些团队里的账号是只读的,装 chart 时一直卡在创建资源阶段,还以为网络问题。可以先执行kubectl auth can-i create namespace来判断。
2.2 持久化方案与存储类
Prometheus 把时序数据写在本地磁盘,所以存储是硬需求。对于单节点的测试环境,可以用 emptyDir,但 Pod 重启后数据就没了,生产不可接受。K8s 里正确的做法是让每个 Prometheus 副本通过 PVC(PersistentVolumeClaim)挂一块独立存储,这样即使 Pod 漂移,数据也能跟着持久卷走。
在部署前,必须确认集群里有哪些 StorageClass:
kubectl get storageclass如果是空,可以参考自己集群的存储方案,比如本地磁盘可以部署 local-path-provisioner,云环境通常自带 default 存储类。没有 StorageClass 也可以安装 chart,但后续 PVC 会一直 Pending,最终 Prometheus Pod 起不来。这个坑我踩过,所以现在每次布置前都会先看一遍存储类。
2.3 Helm仓库与命名空间
用 Helm 管理监控栈,最大的好处是升级、卸载干净。添加官方 chart 仓库和创建命名空间都可以用命令行完成:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update kubectl create namespace monitoring命名空间建议统一叫 monitoring,方便后续配置网络策略和 RBAC。如果公司里有全局的 Helm 仓库缓存,也可以直接用内部代理地址,这个取决于你的网络环境。初次部署不建议在命名空间上玩花样,越标准越好。
2.4 资源规划与配额习惯
kube-prometheus-stack 默认的资源请求其实不高,但真实跑起来之后内存增长很快。一个中型集群(约 200 个 Pod)下,Prometheus 单副本的内存很容易到 2Gi 以上,如果还开了很多聚合查询,4Gi 都不一定够。所以我不建议在资源紧张的小节点上直接安装。
大致估算的话,整套监控栈至少需要 3 个 CPU 和 4Gi 内存。其中:
- Prometheus:500m~1000m CPU,1Gi~2Gi 内存
- Grafana:200m~500m CPU,512Mi~1Gi 内存
- Alertmanager:100m~300m CPU,256Mi~512Mi 内存
- node-exporter:每个节点 100m CPU 和 150Mi 内存
- kube-state-metrics:100m CPU 和 200Mi 内存
建议给监控组件单独打一个 taint 节点,或者至少设置单独的命名空间 ResourceQuota,避免监控和业务抢资源。我见过不少集群因为监控和业务混在一起,业务大促时 Prometheus 被 OOMKilled,结果恰恰在最需要监控的时候监控挂了,非常尴尬。
3. Prometheus部署实操
3.1 编写values.yaml
这里开始进入实际部署。安装 kube-prometheus-stack 不用直接跑默认参数,最好先准备自己的 values.yaml,把存储、资源、访问方式固定下来。一个能跑起来的最小配置大概长这样:
# values-monitoring.yaml global: rbac: create: true prometheus: prometheusSpec: retention: 15d storageSpec: volumeClaimTemplate: spec: storageClassName: local-path accessModes: ["ReadWriteOnce"] resources: requests: storage: 50Gi grafana: adminPassword: admin persistence: enabled: true storageClassName: local-path size: 10Gi service: type: NodePort nodePort: 32001 alertmanager: alertmanagerSpec: storage: volumeClaimTemplate: spec: storageClassName: local-path resources: requests: storage: 10Gi prometheus-node-exporter: hostNetwork: true简单解释一下:prometheus.prometheusSpec.retention设置数据保留 15 天,storageSpec里的 PVC 指定使用 local-path 这个 StorageClass;Grafana 开了持久化,并映射到一个 NodePort 端口 32001,方便临时访问;Alertmanager 同理也有独立 PVC。prometheus-node-exporter.hostNetwork: true是为了让节点指标采集不走容器网络,直接通过宿主机端口获取。
这里要注意,每个 StorageClass 名字必须和上面第2节确认的结果一致,否则 PVC 创建不出来。还有,如果不打算让 Grafana 用 NodePort,也可以先不开 service 类型,后面通过 port-forward 访问。
3.2 安装命令与启动
配置写好后,执行安装:
helm upgrade --install prometheus prometheus-community/kube-prometheus-stack \ -n monitoring \ -f values-monitoring.yaml我习惯用 upgrade --install,因为以后修改 values 再次执行就是更新,不会破坏初次安装。如果安装成功,命令会打印一段 NOTES,提示 Grafana 的访问地址和 admin 密码。此时先不要急着关终端,另外开一个窗口持续观察 Pod:
kubectl get pods -n monitoring -w正常情况下能看到这些 Deployment/StatefulSet 逐步变成 Running:
- alertmanager-prometheus-k8s-0
- prometheus-k8s-0
- prometheus-operator-xxx
- grafana-xxx
- node-exporter-xxx(每个节点一个)
- kube-state-metrics-xxx
如果你看到 prometheus-k8s-0 一直处于 1/2 Running,不要慌,这通常是它的第二容器 config-reloader 还没就绪,等一会儿即可。真正要关注的是 CrashLoopBackOff 或者 Pending,这两个说明有配置或资源问题,后面第 5 节再展开。
3.3 访问Prometheus验证采集
Pod 起来了,链路有没有通还另说。第一步验证,可以先看 Prometheus 自身的 Web UI:
kubectl port-forward svc/prometheus-operated 9090:9090 -n monitoring打开浏览器访问 localhost:9090,进入 Status -> Targets。这里就是 Prometheus 自动发现到的所有采集目标,正常情况会有几十个 target,状态为 UP。如果大部分是 DOWN,不要继续做后面的事情了,先解决采集失败问题。在 Targets 页面里点任何一个 target,都能看到具体报错,比如 connection refused、context deadline exceeded 等,排查方向很直接。
接着在 Graph 页执行一条最简单的查询:
up返回结果应该是一系列时间序列,每个 values 是 1 或 0。1 代表该目标最近一次采集成功。如果某个指标为 0,结合 Targets 页面看它为什么失败。
3.4 接入一个业务ServiceMonitor示例
kube-prometheus-stack 自带的是系统组件监控,业务应用不会自动出现在 Targets 里。把业务接进来的标准姿势是创建 ServiceMonitor。比如业务应用 demo-app 已经通过 Service 暴露了 8080 端口,端口名是 metrics,且 /metrics 接口返回 Prometheus 格式指标,然后创建下面这个资源:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: demo-app-sm namespace: demo labels: release: prometheus spec: selector: matchLabels: app: demo-app endpoints: - port: metrics interval: 30s创建之后,Prometheus 会在下一个 reconcile 周期自动发现它,并开始采集。这个例子最关键的细节是metadata.labels.release: prometheus。因为我们安装 chart 时设置的 release 名字叫 prometheus,默认创建的 Prometheus 会要求 ServiceMonitor 一定要带这个 label,才能完成关联。很多人接了业务半天没数据,查到最后就是少了这个 label。
到这里,Prometheus 部署和基本采集链路已经通了。可视化告警里“可视化”前置条件也就有了数据来源,后面可以放心配置 Grafana 和 Alertmanager。
4. 核心配置拆解与访问方式
4.1 资源限制与数据保留
第一次安装能跑,不代表配置能适合生产。我一般会花两天时间观察 Prometheus 的内存和磁盘趋势,再回头调整 values。至少需要关注三个参数:
prometheus.prometheusSpec.resources.requests:给调度器一个基础保障值,低于这个值不调度。prometheus.prometheusSpec.resources.limits:给容器设置内存上限,防止 OOM 把节点打挂。prometheus.prometheusSpec.retention/retentionSize:控制数据保留长度。
举一个实际配置示例:
prometheus: prometheusSpec: retentionSize: 40Gi resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 3GiretentionSize配合 PVC 容量使用比较合理,比如 PVC 50Gi,数据保存 40Gi 就触发清理,留出压缩和 WAL 的余量。如果只用retention: 15d,面对突然大量指标也可能把磁盘打满,所以我更推荐按体积限制。
4.2 存储与PVC检查
安装后记得检查 PVC 状态:
kubectl get pvc -n monitoring正常情况下所有 PVC 都是 Bound。如果你发现 Prometheus 的 PVC 一直 Pending,大概率是 StorageClass 名不对或没有对应的 Provisioner。另外,PVC 一旦被 StatefulSet 使用,storageClassName就不能直接修改了,因为它属于不可变字段。要换存储类,只能先把 Prometheus StatefulSet 删除,再把 PVC 删掉,最后用新 values 重建。这个过程会导致已有的监控数据丢失,所以部署前一定要确认存储方案。
另一个和存储相关的细节是,如果你用本地存储类的 ReadWriteOnce,Prometheus 那个 Pod 会固定在某台节点上,这是设计使然。不要试图在单副本模式下让它“漂移”,除非底层存储支持 ReadWriteMany。
4.3 Ingress暴露与Grafana访问
NodePort 用于临时验证还可以,但生产环境更推荐通过 Ingress 暴露,并加一层认证。如果集群里已经有 nginx-ingress,可以在 values 里开启 Grafana Ingress:
grafana: ingress: enabled: true ingressClassName: nginx hosts: - grafana.example.local path: /注意,Ingress 需要正确设置host,然后在本地 hosts 文件或 DNS 里解析到 Ingress Controller 的地址。否则即使 Ingress 创建成功,也只是空路由。开启 Ingress 之后,建议关闭 NodePort 服务,避免安全组把端口暴露到公网。
Prometheus 和 Alertmanager 同样可以配置 Ingress,不过它们的 Web UI 通常不对普通开发开放,建议保持在集群内访问,需要时用 kubectl port-forward 或临时 NodePort。
4.4 Alertmanager告警通道占位
既然系列标题是“监控告警”,Alertmanager 也必须先就位。chart 默认创建了一个 Alertmanager 实例,但它的 receiver 是 null,也就是说告警只产生,不会发到任何地方。我不想把整篇的重点扯到告警配置上,但这里先留个伏笔——后面写告警规则和通知渠道时会专门讲。
就部署阶段而言,我们只需要确认 Alertmanager 能正常启动,并且可以在Status -> Cluster Status页面看到它自身的状态。之后我们创建 PrometheusRule 时,告警会经过 Alertmanager 按路由规则分发;这一步先让组件存在,后续留到系列第二篇再填接收器。
5. 常见问题与排查实录
5.1 Pod Pending与PVC绑定失败
症状是 prometheus-k8s-0 一直 Pending,事件里出现persistentvolumeclaim "prometheus-prometheus-k8s-db-prometheus-k8s-0" not found或者waiting for a volume to be created。排查命令:
kubectl describe pod prometheus-k8s-0 -n monitoring kubectl get pvc -n monitoring如果 PVC 处于 Pending,就去查 StorageClass 是否存在,以及 Provisioner 是否正常。有时候是 local-path-provisioner 没装,也有时候是云盘资源不够导致卷创建超时。不解决存储问题,Prometheus 是永远起不来的。
5.2 镜像Pull失败
症状是 Pod 状态 ImagePullBackOff,kubectl describe 里可以看到 Failed to pull image。最直接的原因就是当前节点无法访问镜像仓库,或者镜像 tag 不对。在受控环境里,建议把镜像提前导入节点,或者在 values 中覆盖 image repository 和 tag,指定为内网镜像仓库地址。例如:
prometheus: prometheusSpec: image: repository: registry.internal/prometheus tag: v2.54.1这里要注意不要指定latest,否则 config-reloader 对比镜像版本时会因为 tag 相同而无法感知更新,出现“配置变了但镜像不更新”的诡异问题。固定 tag 是生产常识。
5.3 时区与Grafana显示问题
监控数据的时间戳大多以 UTC 存储,展示层如果按 GMT,就会比本地时间早 8 小时。Grafana 页面右上角可以临时切换时区,但默认还是 UTC。如果希望一打开就是你本地时间,可以在 values 里设置容器环境变量:
grafana: env: TZ: Asia/ShanghaiPrometheus 本身不需要改时区,因为它的数据时间本来就是 UTC epochs。很多新手非要把 Prometheus 容器内的时区改成北京时间,完全没有必要,反而会增加和其他查询系统对齐时间的复杂度。
5.4 数据采集到了,Grafana却一片空白
这个问题的典型场景是:Prometheus Targets 里能看到业务指标,Grafana 的数据源也能连通,但某个 Dashboard 大盘全是“No data”。排查思路从数据源和变量入手:
- 检查 Grafana 数据源 URL 是否写成了
http://prometheus-operated:9090,注意端口是 9090,不是 80。 - 检查 Dashboard 的 job 名与 Prometheus 中的 target 标签是否一致。如果 dashboard 期望 job=kubernetes-pods,但你的 ServiceMonitor 没有设置 jobLabel,序列的 job 就会默认为 ServiceMonitor 名。
- 检查 Dashboard 用的指标名称是否已经改名。常见的是老版本 node-exporter 指标和 1.25+ 版本的指标命名有差异,很多第三方大盘没有及时更新。
遇到这种情况,最好的方法是在 Grafana 的 Explore 页面手动查询{job="..."}看一眼标签是否正确,再反推到 Dashboard 变量。
5.5 高可用多副本的坑
有人一上来就把 Prometheus 副本数调成 2,结果两个副本都启动失败。原因是 Prometheus Operator 创建的 StatefulSet 会用不同的 PVC 名称,如果存储类不支持多副本同名卷、或者节点亲和性不一致,就会导致其中一个副本起不来。另外,Alertmanager 多副本需要开启集群模式,涉及流量复制等配置,也不是加个 replica 就行。普通规模集群,完全没必要一开始就上多副本。先把单副本的数据链路跑稳,后续如果量大了,优先考虑远端存储方案,而不是单纯加副本。
5.6 日常排查命令速查
最后整理一组高频命令,方便出了状况快速定位:
# 查看监控命名空间内所有Pod状态 kubectl get pods -n monitoring -o wide # 查看Prometheus StatefulSet详情 kubectl describe statefulset prometheus-k8s -n monitoring # 查看Prometheus配置是否被Operator同步 kubectl get prometheus prometheus-k8s -n monitoring -o yaml # 查看ServiceMonitor关联关系 kubectl get servicemonitor -n monitoring # 看Prometheus日志 kubectl logs prometheus-k8s-0 -c prometheus -n monitoring用这些命令基本能覆盖 90% 的启动或采集问题。真正卡住时,不要凭感觉改配置,先看事件和日志,再回头检查 values。
写到这里,Prometheus 在 K8s 里的部署闭环已经完整了。我个人最大的体会是,部署本身并不难,难的是把“监控目标发现、数据采集、持久化、访问入口”这几条线一次理顺。很多人装完 Prometheus 后还是发现监控不可用,往往不是 chart 的问题,而是缺少对整个采集链路的目标感。如果你也是第一次做这件事,建议按这样的顺序验证:先看 Pod 都 Running,再看 Targets 都 UP,接着用 PromQL 查几条指标,最后再看 Grafana 有没有图形。每一步都确认过了,再进入下一步,才不会把问题留给后面的告警阶段。
下一篇我会继续整理告警规则的写法,以及 Alertmanager 如何接入真实通知渠道,到时候会给出可以直接套用的 PrometheusRule 示例。部署阶段留下的这些基础配置如果能固化成模板,后面再搭建新的监控环境,基本上就是复制粘贴和改个存储类名字的事。