说实话,我也经历过那种深夜被报警电话叫醒的噩梦。凌晨三点,监控大屏一片红,K8s集群的节点负载直接飙到100%,Pod一个接一个地被OOM Kill(内存溢出被强制终止),用户反馈系统卡顿甚至完全不可用。那种时候,你根本不知道是哪个服务出了问题,是内存泄漏?还是流量突然激增?又或者是某个疯狂的查询查询把数据库打爆了?
如果你正在经历或者担心未来会经历这种崩溃,那么这篇文章就是为你准备的。我们不只讲理论,我要带你从零搭建一套真正的生产级监控体系,并用它来解决那些让你头疼的OOM和节点压力问题。
为什么你的集群会“猝死”?
在动手之前,我们先聊聊为什么K8s集群容易出这些问题。Kubernetes本身就像一个高效的调度器,但它并不“聪明”到能预知未来。当你的应用出现内存泄漏时,Prometheus如果不报警,你直到Pod崩溃的那一刻才知道。而当大量Pod同时重启时,API Server和etcd就会不堪重负,导致整个集群“卡死”。
常见的三大杀手:
- 内存泄漏(Memory Leak):应用代码问题,内存只增不减,最终触发OOM。
- 突增流量(Traffic Spike):促销活动或爬虫攻击,导致CPU瞬间打满,请求堆积。
- 资源限制不当(Misconfiguration):Request设置太低,Limit设置太高,或者反过来,导致调度失败或资源争抢。
第一步:搭建监控基石——Prometheus
Prometheus是云原生监控的事实标准。它不依赖分布式存储,而是通过本地存储和拉取模型来工作。对于K8s集群,我们通常使用kube-prometheus-stack这个Helm Chart,因为它把Prometheus、Grafana、Alertmanager都打包好了,开箱即用。
假设你已经在K8s集群中安装了Helm,让我们开始部署。
# 添加Prometheus社区的Helm仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 安装kube-prometheus-stack
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.adminPassword='<your-secure-password>' \
--set prometheus.prometheusSpec.retention=15d \
--set alertmanager.alertmanagerSpec.storage.enabled=true
这段命令做了三件事:
- 创建
monitoring命名空间。 - 安装全套监控组件。
- 设置Grafana密码,并将Prometheus的数据保留时间设为15天(默认只有15天,生产环境建议更久)。
安装完成后,你可以通过端口转发来访问Grafana:
kubectl port-forward service/monitoring-grafana 3000:80 -n monitoring
然后打开浏览器访问 http://localhost:3000,用户名是admin,密码是你刚才设置的。
第二步:让Grafana“看懂”集群状态
安装好只是第一步,Grafana默认只有一些基础面板。为了实时监控CPU和内存,我们需要导入更专业的Dashboard。
在Grafana中,点击右上角的“+”号,选择“Import”,然后输入Dashboard ID。这里有几个我必用的ID:
- Node Exporter:
1860(监控节点本身的CPU、内存、磁盘、网络) - Kubernetes Compute Resources:
15757(监控Pod和容器的CPU/内存使用率,这个最常用) - Kubernetes Storage:
11458(监控PV和PVC的使用情况)
当你导入15757这个面板后,你会看到每个Namespace、每个Pod的CPU和内存曲线。这时候,如果有一个Pod的内存曲线持续上升,直到触碰Limit线然后骤降(因为被Kill了),你就能一眼看出问题。
第三步:深入排查——CPU飙升和内存OOM
光有监控不够,你得会看。让我给你讲两个真实的案例。
案例一:内存泄漏导致的OOM
假设你在Grafana中看到一个Pod payment-service-5d8f7x9k2 的内存使用率在过去两小时内从100MB平稳上升到512MB(Limit),然后突然归零,Pod重启。
如何排查?
确认OOM:在Grafana中搜索
container_memory_working_set_bytes,找到该Pod的峰值。同时检查Events:kubectl describe pod payment-service-5d8f7x9k2 -n production你会在Events中看到:
Container payment-service was OOMKilled。分析代码:这时候不要猜,要抓现场。如果应用支持pprof(Go语言常见)或JMX(Java常见),可以进入Pod进行堆栈分析。
# 以Go应用为例,获取堆栈 kubectl exec -it payment-service-5d8f7x9k2 -n production -- go tool pprof http://localhost:6060/debug/pprof/heap如果你发现某个缓存结构在无限增长,那就是泄漏点。
临时缓解:如果代码不能马上改,可以临时调大Limit,但这只是饮鸩止渴。更好的办法是设置
resources.limits.memory略高于当前峰值,并启用vertical-pod-autoscaler(VPA)自动调整。
案例二:CPU突发导致的卡死
另一个常见场景是某个定时任务在整点执行,导致CPU瞬间打满,影响其他Pod。
如何排查?
查看CPU使用率:在Grafana中查看
container_cpu_usage_seconds_total。如果看到某个Pod在特定时间点出现尖峰,而其他Pod的CPU也被拉高,那可能是节点层面的资源争抢。检查QoS等级:K8s中,如果两个Pod在同一个节点,且一个CPU飙满,另一个可能会被“饿死”。检查你的Pod资源请求:
kubectl get pods -n production -o jsonpath='{range .items[*]}{.metadata.name}{.spec.containers[0].resources.requests.cpu}{end} \n'确保每个Pod都设置了合理的
requests,这样调度器才能避免将高负载Pod调度到同一节点。使用cAdvisor数据:Prometheus默认采集的是cAdvisor的数据,它能看到容器的真实CPU使用率。如果Pod的CPU使用率远高于Limit,说明你的Limit设置太低,或者应用确实需要更多资源。
第四步:设置智能告警——告别睡眠剥夺
监控是为了发现问题,告警是为了让你及时知道。我们使用Alertmanager来配置告警规则。
在kube-prometheus-stack中,告警规则已经预置了一些。我们可以自定义规则,比如当内存使用率超过90%时告警。
创建一个custom-alerts.yaml文件:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: custom-alerts
namespace: monitoring
labels:
release: monitoring
spec:
groups:
- name: custom.rules
rules:
- alert: HighMemoryUsage
expr: container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "高内存使用率"
description: "Pod {{ $labels.pod }} 在 namespace {{ $labels.namespace }} 中的内存使用率超过90%"
- alert: OOMKilled
expr: kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1
for: 0m
labels:
severity: critical
annotations:
summary: "容器OOM Kill"
description: "Pod {{ $labels.pod }} 在 namespace {{ $labels.namespace }} 中被OOM Kill,请立即检查"
应用这个规则:
kubectl apply -f custom-alerts.yaml
然后,你需要配置Alertmanager的接收器,比如发送通知到Slack、钉钉或企业微信。在Helm值文件中配置:
alertmanager:
alertmanagerSpec:
config:
receivers:
- name: 'slack'
slack_configs:
- channel: '#k8s-alerts'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
这样,当OOM发生时,你会立刻收到Slack消息,而不是等到用户投诉。
第五步:高级技巧——节点压力与调度优化
有时候,问题不在单个Pod,而在整个节点。如果节点内存压力过大,K8s可能会驱逐Pod。
如何监控节点压力?
节点内存压力告警: “`yaml
- alert: NodeMemoryPressure expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.1 for: 10m labels: severity: warning annotations: summary: “节点内存压力” description: “节点 {{ $labels.node }} 的可用内存低于10%”
”`
使用PodDisruptionBudget (PDB):为了防止节点维护时Pod被过多驱逐,你可以设置PDB:
apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: payment-pdb namespace: production spec: minAvailable: 2 selector: matchLabels: app: payment-service这样,即使节点压力很大,K8s也会尽量保留至少2个Pod可用。
垂直Pod自动伸缩(VPA):如果你发现Pod经常被OOM Kill,或者CPU经常打满,可以使用VPA自动调整资源请求和限制。
helm install vpa prometheus-community/kube-vpa \ --namespace vpa-system \ --create-namespaceVPA会分析历史指标,给出推荐值。虽然它不会自动应用(为了安全),但你可以参考它的建议来调整你的Deployment。
第六步:实战演练——从报警到恢复的完整流程
让我给你一个完整的故障排查剧本。
场景:你收到Slack告警:“Pod payment-service-5d8f7x9k2 被OOM Kill”。
- 确认影响:查看Grafana中
payment-service的总QPS是否下降,用户是否有报错。 - 定位根因:
- 进入Grafana,查看该Pod的内存曲线。发现内存在使用高峰时缓慢上升,最终达到Limit。
- 执行
kubectl top pod payment-service-5d8f7x9k2 -n production,确认当前内存使用量。 - 查看应用日志:
kubectl logs payment-service-5d8f7x9k2 -n production --previous(--previous查看上一个容器的日志,因为当前容器可能还在重启中)。
- 临时恢复:
- 如果是缓存泄漏,尝试重启Pod。
- 如果是流量激增,考虑临时扩容Pod副本数。
- 长期解决:
- 联系开发团队,分析内存泄漏代码。
- 调整资源Limit,给予更多缓冲。
- 启用VPA,让系统自动适应资源需求。
结语:监控是持续的过程
搭建Prometheus和Grafana只是开始,真正的挑战在于持续优化告警规则、调整资源限制、以及培养团队的监控意识。不要等到集群崩溃才想起监控,要把监控当作基础设施的一部分,像管理代码一样管理你的监控配置。
记住,好的监控能让你在问题发生前就发现端倪,而不是在用户投诉后才慌忙排查。希望这套方案能帮你摆脱深夜报警的噩梦,让你能安心入睡。如果有任何具体问题,欢迎在评论区讨论,我们一起解决。
