说实话,做运维最怕的不是故障发生,而是故障来了之后,老板问“为什么”,开发人员问“是不是你配置错了”,业务方问“什么时候好”。那时候你连个能拍桌子的数据都没有,只能干瞪眼。
我见过太多同事,为了监控这套东西愁白了头。有的人用Zabbix,但在K8s动态环境下抓瞎;有的人自己写脚本,结果脚本比代码还长,维护起来想哭。直到我彻底拥抱了 Prometheus + Grafana 这套组合,才发现:原来运维可以这么优雅,背锅这件事,真的可以离你远去。
今天我不想给你甩一堆枯燥的理论,我想带你像搭积木一样,把这层“护城河”建起来。咱们不聊虚的,直接上干货,最后让你能看着Grafana大盘,自信地对老板说:“这锅,我接得住。”
一、 为什么要在这个时间点,认真聊聊监控?
首先,你得理解一个核心痛点:Kubernetes的动态性。
以前你管物理机,IP是固定的,机器是死的那几台。现在呢?Pod随时起、随时死,IP每分每秒都在变。如果你还用老办法去监控,那就是在用地图找一只正在飞且不断变换颜色的蝴蝶。
这时候,Prometheus 的优势就出来了。它不是去“轮询”固定的IP,而是通过 Service Discovery(服务发现),自动发现集群里所有的新老Pod。你想监控什么?贴一个标签(Label),它就自己找上门了。
而我们面临的“服务雪崩”,通常不是因为代码写得烂,而是因为资源瓶颈没被及时发现。CPU跑满、内存OOM、磁盘IO瓶颈、或者网络带宽打满。这些现象在爆发前,往往有征兆。Prometheus 擅长抓取这些时序数据,Grafana 擅长把这些数据变成“人话”。
这套组合拳打出来,你不再是“救火队员”,你是“消防员”,你能看见火苗,提前把它掐灭。
二、 架构设计:别急着安装,先想清楚
很多新手踩坑,是因为一上来就 helm install,结果部署了一堆垃圾数据,磁盘爆满,查询慢得怀疑人生。
一个健壮的监控架构,应该包含以下几层:
- 数据采集层:Prometheus Server(核心)、Node Exporter(主机指标)、cAdvisor(容器指标,通常被kubelet集成)、Kube State Metrics(K8s对象状态)。
- 数据存储层:Prometheus 自带的TSDB,如果数据量大,得接 VictoriaMetrics 或者 Thanos 做长期存储和全局查询。
- 告警通知层:Alertmanager,负责去重、分组、路由告警到钉钉、企业微信、Slack或邮件。
- 可视化层:Grafana,负责画图、看板展示。
重要原则:不要把所有数据都存下来!采集频率要有区分。核心业务指标 15s 采集一次,普通指标 60s 或更长。
三、 实战部署:基于Helm的优雅落地
假设你已经有一个跑起来的 K8s 集群。咱们用 Helm 来部署,这是目前最主流、最省事的方式。
1. 安装 Prometheus Operator
Operator 模式比手动部署一堆 YAML 要强大得多,它能管理 CRD(自定义资源定义),让配置变得声明式。
# 添加官方仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 创建命名空间并安装
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.retention=15d \
--set grafana.adminPassword=yourStrongPassword \
--set alertmanager.alertmanagerSpec.storage.size=10Gi
这里我设置了 15 天的数据保留。对于中小规模集群,15天足够你回溯大多数故障;如果你想要更久,建议单独配置 Thanos,否则 Prometheus 自身的磁盘压力会很大。
2. 关键组件解析
安装完成后,你会看到以下几个核心 Pod:
- prometheus-kube-prometheus-stack-0:Prometheus 主节点。
- alertmanager-prometheus-kube-prometheus-alertmanager-0:告警管理器。
- grafana-prometheus-kube-prometheus-grafana-0:可视化面板。
- prometheus-operator:负责监听 CRD 变化,自动更新 Prometheus 配置。
这时候,别忘了暴露 Grafana 服务,否则你只能在集群内部访问。可以用 Ingress 或者 NodePort,生产环境推荐 Ingress + HTTPS。
# 一个简单的 NodePort 示例,仅供测试方便
apiVersion: v1
kind: Service
metadata:
name: grafana-nodeport
namespace: monitoring
spec:
type: NodePort
selector:
app.kubernetes.io/name: grafana
ports:
- port: 3000
targetPort: 3000
nodePort: 30000
访问 http://<YourNodeIP>:30000,用户名 admin,密码是你刚才设置的 yourStrongPassword。
四、 配置抓取:让数据流起来
光装好没用,你得告诉 Prometheus “我要监控谁”。在 K8s 里,最优雅的方式是使用 ServiceMonitor 或 PodMonitor 资源对象。
示例:监控一个普通的 Nginx 应用
假设你有一个叫 nginx-demo 的应用,你想监控它的 QPS 和延迟。通常你需要在应用里埋点(比如使用 nginx-lua-prometheus 模块),然后暴露 /metrics 接口。
第一步:在应用 Deployment 中暴露端口
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
team: frontend
annotations:
prometheus.io/scrape: "true" # 关键:告诉 Prometheus 来抓我
prometheus.io/port: "9113" # 关键:监控端口
prometheus.io/path: "/metrics"
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: http
- containerPort: 9113
name: metrics
第二步:创建 Service
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: default
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: http
- port: 9113
targetPort: metrics
第三步:创建 ServiceMonitor(这才是精髓)
不用改 Prometheus 的主配置文件,只需要定义一个 CRD,Operator 会自动帮你生成抓取规则。
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: nginx-demo-monitor
namespace: default
labels:
team: frontend
spec:
selector:
matchLabels:
app: nginx-demo
endpoints:
- port: metrics
interval: 15s # 每15秒采集一次
path: /metrics
namespaceSelector:
matchNames:
- default
应用这个 YAML 后,你去 Prometheus UI 的 Status -> Targets 里看一眼,如果看到 nginx-demo 的状态是 UP,恭喜你,数据源打通了!
五、 Grafana 看板:把数据变成直觉
现在数据有了,但 Prometheus 自带的 UI 很难受,全是冷冰冰的数字和曲线。Grafana 才是神器。
1. 导入现成模板
千万不要自己从零画图表!那是浪费生命。
- K8s 集群总览:搜索 Dashboard ID
315或1860,这是社区最经典的 K8s 集群监控大盘,包含 CPU、内存、网络、磁盘的总和。 - Node 节点监控:搜索
13506,基于 Node Exporter,能看到每台物理机/虚拟机的详细资源使用情况。 - Pod 级别监控:搜索
15187,可以看到每个 Pod 的资源消耗和重启次数。
操作小技巧:在 Grafana 右上角点击“Dashboard” -> “Import”,输入 ID,点击 Load,瞬间拥有专业级大屏。
2. 关键指标解读(避坑指南)
很多新人看监控,只看 CPU 使用率。这是不够的。在 K8s 里,你要关注这几个“保命”指标:
- Container CPU Usage (cores):不要只看百分比,要看绝对值。如果一个容器分配了 1 核 CPU,但只用了 0.1 核,那是资源浪费;如果长期跑满 1 核,就会触发限流(Throttling),导致服务响应变慢。
- Container Memory Working Set:注意,是 Working Set,不是 RSS。RSS 包含缓存,容易被误导。Working Set 才是程序真正占用的、不可回收的物理内存。如果这个值接近 Limit,随时可能 OOM Kill。
- Network I/O:尤其是
container_network_transmit_errors_total。如果有网络错误增长,说明你的服务之间通信有问题,可能是网络插件(CNI)或交换机的问题。 - Pod Restarts:这是最直接的“生病”信号。如果一个 Pod 每小时重启多次,99% 是代码 Bug 或配置错误,而不是基础设施问题。
3. 自定义一个“业务健康度”面板
除了资源,你肯定关心业务。比如,“我的订单服务 QPS 跌了”或者“接口延迟变高了”。
假设你的应用暴露了 /metrics,其中有一个自定义指标 order_service_total_latency_seconds_bucket。
在 Grafana 中,你可以用 PromQL 写出这样的查询:
# 计算 P99 延迟
histogram_quantile(0.99, sum(rate(order_service_total_latency_seconds_bucket[5m])) by (le))
把这条语句放进 Panel,你会看到一条黄色的曲线。当曲线突然抬高,说明你的后端处理变慢了。这时候告警触发,你就能在用户投诉之前介入。
六、 告警配置:拒绝噪音,只报真凶
这是运维最能体现价值的地方,也是最能背锅的地方。如果告警太多,你会麻木(狼来了的故事);如果告警太少,你会睡不好。
我们需要借助 Alertmanager 和 PrometheusRule CRD 来构建智能告警。
1. 定义告警规则
创建一个 AlertingRules 文件:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: critical-alerts
namespace: monitoring
spec:
groups:
- name: k8s-critical
rules:
- alert: HighCPUUsage
expr: sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (pod) > 0.9
for: 5m
labels:
severity: critical
team: frontend
annotations:
summary: "Pod {{ $labels.pod }} CPU usage is too high (>90%)"
description: "Pod {{ $labels.pod }} in namespace {{ $labels.namespace }} has been using more than 90% CPU for 5 minutes."
- alert: OOMKilled
expr: increase(container_memory_working_set_bytes{container!=""}[1h]) > 0 and container_oom_events_total > 0
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.pod }} was OOM Killed"
description: "Container {{ $labels.container }} in pod {{ $labels.pod }} has been killed due to OOM."
解读:
HighCPUUsage:Pod 持续 5 分钟 CPU 超过 90% 才告警。这避免了瞬时波动带来的误报。OOMKilled:这是最严重的。一旦触发,说明内存配置不足,必须立刻扩容或修复内存泄漏。
2. 配置 Alertmanager 路由
Alertmanager 的配置文件 alertmanager.yaml 决定了告警往哪里发。
route:
group_by: ['alertname', 'severity', 'team'] # 按这些标签分组,避免刷屏
group_wait: 10s # 等待10秒,收集同一组告警
group_interval: 5m # 组内新告警间隔5分钟再发
repeat_interval: 4h # 相同告警重复发送间隔4小时
receiver: 'default-webhook'
routes:
- match:
severity: 'critical'
receiver: 'emergency-pager'
- match:
severity: 'warning'
receiver: 'dingtalk-bot'
receivers:
- name: 'default-webhook'
webhook_configs:
- url: 'http://alertmanager-webhook.default.svc.cluster.local:5001/'
- name: 'emergency-pager'
opsgenie_configs:
- api_key: 'your-opsgenie-key'
- name: 'dingtalk-bot'
dingtalk_configs:
- webhook: 'https://oapi.dingtalk.com/robot/send?access_token=xxx'
message: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
关键点:
- 分组:一定要分组。当磁盘故障时,可能瞬间触发 50 个告警。如果不分组,你的手机会震到发麻。分组后,你会收到一条消息:“集群有 50 个磁盘告警”。
- 静默与暂停:如果你知道今晚要发布新版本,可以在 Alertmanager UI 里设置静默,避免被无关告警打扰。
- 分级:Warning 级别发钉钉/企业微信,Critical 级别直接打电话或发 PagerDuty。这样你晚上能睡个好觉。
七、 场景演练:当雪崩发生时,你该怎么做?
假设周五晚上 10 点,你的手机响了。Grafana 大屏全红。
错误做法:慌了,到处 grep 日志,问开发“是不是你改代码了”,问基础设施“是不是网络断了”。
正确做法(SRE 思维):
看大盘,定范围:
- 打开 Grafana 的“全局概览”面板。
- 是所有服务都慢?还是某一个服务慢?
- 如果是全局慢,可能是 K8s 集群底层的网络或 API Server 问题,或者是 DNS 问题。
- 如果是单个服务慢,比如
order-service,那问题大概率局限在这个服务或其依赖上。
查资源瓶颈:
- 看
order-service的 CPU 和 Memory。 - 如果 CPU 跑满:检查是否有死循环,或者流量突增。如果是流量突增,考虑自动扩缩容(HPA)是否生效。
- 如果 Memory 涨到 Limit:检查是否有内存泄漏,或者缓存没设置过期时间。此时 Pod 可能正在频繁重启。
- 看
查下游依赖:
- 看
order-service调用的 MySQL 或 Redis。 - 检查数据库连接池是否打满(
db_connections_active)。 - 检查 Redis 是否有慢查询(
redis_slowlog)。
- 看
查链路日志:
- 如果资源都正常,那就可能是代码逻辑问题。
- 此时打开 Jaeger 或 SkyWalking(如果接了),追踪一个慢请求的链路,看卡在哪个 Span。
执行应急预案:
- 如果是流量洪峰,立即扩容。
- 如果是 Bug,立即回滚版本。
- 如果是资源不足,紧急调大 Limit。
在这个过程中,你的 Grafana 看板就是你最强大的武器。你能指着屏幕说:“你看,这个时间点,QPS 突然涨了 5 倍,导致 CPU 打满,进而导致响应超时。” 这句话,比任何解释都有力。
八、 进阶技巧:让监控真正“懂”业务
很多公司的监控只管机器死活,不管业务死活。机器好好的,但用户下单失败。这种监控是失败的。
你需要引入 RED 方法 和 USE 方法 的结合。
- USE 方法(针对资源):Utilization(利用率)、Saturation(饱和度)、Errors(错误)。主要用于监控 Node Exporter 和 Kubelet 指标。
- RED 方法(针对服务):Rate(速率)、Errors(错误率)、Duration(持续时间)。主要用于监控应用层的指标。
实战建议:
在 Grafana 里建立一个“业务监控”分组。对于每一个核心微服务,都要有以下几个 Panel:
- Request Rate:每秒请求数。
- Error Rate:5xx 错误的比例。
- P99 Latency:99% 的请求响应时间。
- Server Saturation:如果有的话,比如数据库连接数、线程池使用率。
例子:
# 错误率
sum(rate(http_requests_total{status=~"5..", job="order-service"}[5m])) / sum(rate(http_requests_total{job="order-service"}[5m]))
当这个比率超过 1% 时,触发 Warning 告警。这比监控 CPU 更能直接反映用户感知的故障。
